From 3c60e2e2d5aa953856a267f8ae350d9599d77572 Mon Sep 17 00:00:00 2001 From: TT-ReBORN Date: Sun, 18 Feb 2024 15:51:09 +0100 Subject: [PATCH] - Rewrote Georgia-ReBORN into a full-fledged OOP framework: - Introduced a clean, new class architecture with improved encapsulation and initialization. - Enhanced code organization with collapsible sections in IDE for better navigation. - Reorganized script files for clearer structure. - Eliminated almost all global variables, retaining only common enums. - Refactored numerous functions for efficiency and readability. - Optimized overall theme performance, resulting in ~30-40% improvement. - Overhauled the playlist script for better modularity. - Significantly boosted playlist performance with faster thumbnail fetching and smoother scrolling. - Expanded and improved JSDocs for better developer guidance. - Added a new "Add tracks" button (+) that adds selected albums/tracks from Library or Playlist to user defined playlist. - Can be displayed via top menu Options > Player controls > Lower bar > Show add tracks button or right click on lower bar for context menu > Display > Show add tracks button. - The default playlist for using the add tracks button is "Favorites", the add tracks button playlist can be changed via top menu Options > Player controls > Lower bar > Add tracks button > Add tracks playlist or right click on lower bar for context menu > Controls > Add tracks playlist. - When adding albums/tracks from the Library or Playlist (to another playlist), you can activate additionally top menu Options > Player controls > Panel > Switch to playlist when adding songs or right click on lower bar for context menu > Controls > Switch to playlist when adding songs. - Added auto-download biography and auto-download lyrics modes. - Developer tools need to be enabled first via top menu Options > Settings > Developer tools. - Both modes can be activated via top menu Options > Developer tools > Enable auto-download biography/lyrics. - The auto-download biography mode has a 5 sec timer interval to play a new track for an artist and then shuffles to another artist. - The auto-download lyrics mode has a 15 sec timer interval that does the same to fetch the lyrics for the tracks. - It works the best if you leave your PC unattended for a longer period of time and when you come back, all the stuff has been downloaded. - Added new codec logos, thx @ZavierQuixote =) - Removed and merged Options > Lyrics > Controls > Remember active lyrics state into Remember lyrics panel state - This was confusing and unneeded, functionality remains the same as both features are merged into one now. - Removed Options > Library > Controls > Switch to playlist when adding songs. - The new feature can be now found in top menu Options > Player controls > Panel > Switch to playlist when adding songs. - Due to the new "Add tracks" button, this Library only feature has been enhanced for additional Playlist usage. - Fixed a rare disc art rotation crash. - Fixed a playlist custom track pattern issue that displayed duplicated track number indexes. - Fixed very old sticky scrollbars, when scrollbars are being dragged and mouse button was clicked outside the window. - Fixed some cosmetic bugs. --- profile/configuration/foo_ui_columns.dll.cfg | Bin 89713 -> 90475 bytes .../scripts/Base/gr-async-loader.js | 641 +- .../georgia-reborn/scripts/Base/gr-buttons.js | 1550 ++- .../scripts/Base/gr-callbacks.js | 1664 +-- .../georgia-reborn/scripts/Base/gr-color.js | 335 +- .../georgia-reborn/scripts/Base/gr-common.js | 794 +- .../scripts/Base/gr-config-defaults.js | 2537 +++++ .../georgia-reborn/scripts/Base/gr-config.js | 962 ++ .../scripts/Base/gr-configuration.js | 384 - .../scripts/Base/gr-context-menu.js | 1789 ---- .../scripts/Base/gr-defaults.js | 2570 ----- .../georgia-reborn/scripts/Base/gr-display.js | 455 +- .../georgia-reborn/scripts/Base/gr-helpers.js | 1250 ++- .../scripts/Base/gr-initialize.js | 73 + .../georgia-reborn/scripts/Base/gr-lyrics.js | 315 +- .../scripts/Base/gr-main-components.js | 3734 ++++--- .../scripts/Base/gr-main-functions.js | 3289 ------ .../georgia-reborn/scripts/Base/gr-main.js | 6117 ++++++++--- .../scripts/Base/gr-menu-context.js | 1823 ++++ .../scripts/Base/gr-menu-custom.js | 3505 +++--- .../georgia-reborn/scripts/Base/gr-menu.js | 7358 +++++++------ .../scripts/Base/gr-settings.js | 2834 ++--- .../georgia-reborn/scripts/Base/gr-setup.js | 1294 +-- .../scripts/Base/gr-theme-colors.js | 6550 ++++++++++++ .../scripts/Base/gr-theme-presets.js | 3774 ++++--- .../georgia-reborn/scripts/Base/gr-themes.js | 6360 ----------- .../scripts/Biography/assets/html/config.html | 182 +- .../scripts/Biography/bio-main.js | 139 +- .../scripts/Biography/scripts/bio-allmusic.js | 182 +- .../scripts/Biography/scripts/bio-buttons.js | 586 +- .../Biography/scripts/bio-callbacks.js | 750 +- .../Biography/scripts/bio-filmstrip.js | 384 +- .../scripts/Biography/scripts/bio-helpers.js | 87 +- .../scripts/Biography/scripts/bio-images.js | 969 +- .../Biography/scripts/bio-initialise.js | 124 +- .../Biography/scripts/bio-interface.js | 545 +- .../scripts/Biography/scripts/bio-language.js | 12 +- .../scripts/Biography/scripts/bio-lastfm.js | 224 +- .../scripts/Biography/scripts/bio-library.js | 10 +- .../scripts/Biography/scripts/bio-lyrics.js | 60 +- .../scripts/Biography/scripts/bio-menu.js | 1161 +- .../scripts/Biography/scripts/bio-names.js | 34 +- .../scripts/Biography/scripts/bio-panel.js | 886 +- .../scripts/Biography/scripts/bio-popupbox.js | 44 +- .../Biography/scripts/bio-properties.js | 50 +- .../scripts/Biography/scripts/bio-resize.js | 362 +- .../Biography/scripts/bio-scrollbar.js | 210 +- .../scripts/Biography/scripts/bio-server.js | 250 +- .../scripts/Biography/scripts/bio-settings.js | 211 +- .../scripts/Biography/scripts/bio-tagger.js | 100 +- .../scripts/Biography/scripts/bio-text.js | 1106 +- .../scripts/Biography/scripts/bio-timers.js | 10 +- .../scripts/Biography/scripts/bio-utils.js | 17 +- .../Biography/scripts/bio-wikipedia.js | 118 +- .../scripts/Library/assets/html/config.html | 234 +- .../scripts/Library/lib-main.js | 96 +- .../scripts/Library/scripts/lib-buttons.js | 330 +- .../scripts/Library/scripts/lib-callbacks.js | 631 +- .../scripts/Library/scripts/lib-helpers.js | 75 +- .../scripts/Library/scripts/lib-images.js | 584 +- .../scripts/Library/scripts/lib-initialise.js | 63 +- .../scripts/Library/scripts/lib-interface.js | 424 +- .../scripts/Library/scripts/lib-library.js | 612 +- .../scripts/Library/scripts/lib-menu.js | 649 +- .../scripts/Library/scripts/lib-panel.js | 1152 +- .../scripts/Library/scripts/lib-populate.js | 1091 +- .../scripts/Library/scripts/lib-popupbox.js | 46 +- .../scripts/Library/scripts/lib-properties.js | 46 +- .../scripts/Library/scripts/lib-scrollbar.js | 342 +- .../scripts/Library/scripts/lib-search.js | 466 +- .../scripts/Library/scripts/lib-timers.js | 10 +- .../scripts/Library/scripts/lib-utils.js | 15 +- .../scripts/Playlist/pl-control-list.js | 743 -- .../scripts/Playlist/pl-control-scrollbar.js | 898 -- .../scripts/Playlist/pl-linked-list.js | 272 - .../scripts/Playlist/pl-main.js | 9506 +---------------- .../scripts/Playlist/scripts/pl-callbacks.js | 1153 ++ .../scripts/Playlist/scripts/pl-components.js | 2414 +++++ .../scripts/Playlist/scripts/pl-controls.js | 2322 ++++ .../scripts/Playlist/scripts/pl-helpers.js | 115 + .../Playlist/scripts/pl-list-content.js | 427 + .../Playlist/scripts/pl-list-header.js | 1589 +++ .../scripts/Playlist/scripts/pl-list-row.js | 541 + .../scripts/Playlist/scripts/pl-list.js | 733 ++ .../scripts/Playlist/scripts/pl-playlist.js | 1804 ++++ .../scripts/Playlist/scripts/pl-properties.js | 97 + .../scripts/Playlist/scripts/pl-setup.js | 109 + 87 files changed, 50666 insertions(+), 49693 deletions(-) create mode 100644 profile/georgia-reborn/scripts/Base/gr-config-defaults.js create mode 100644 profile/georgia-reborn/scripts/Base/gr-config.js delete mode 100644 profile/georgia-reborn/scripts/Base/gr-configuration.js delete mode 100644 profile/georgia-reborn/scripts/Base/gr-context-menu.js delete mode 100644 profile/georgia-reborn/scripts/Base/gr-defaults.js create mode 100644 profile/georgia-reborn/scripts/Base/gr-initialize.js delete mode 100644 profile/georgia-reborn/scripts/Base/gr-main-functions.js create mode 100644 profile/georgia-reborn/scripts/Base/gr-menu-context.js create mode 100644 profile/georgia-reborn/scripts/Base/gr-theme-colors.js delete mode 100644 profile/georgia-reborn/scripts/Base/gr-themes.js delete mode 100644 profile/georgia-reborn/scripts/Playlist/pl-control-list.js delete mode 100644 profile/georgia-reborn/scripts/Playlist/pl-control-scrollbar.js delete mode 100644 profile/georgia-reborn/scripts/Playlist/pl-linked-list.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-callbacks.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-components.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-controls.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-helpers.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-list-content.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-list-header.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-list-row.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-list.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-playlist.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-properties.js create mode 100644 profile/georgia-reborn/scripts/Playlist/scripts/pl-setup.js diff --git a/profile/configuration/foo_ui_columns.dll.cfg b/profile/configuration/foo_ui_columns.dll.cfg index fe358ae4b7819d29dd67f6f9896d09a74bed5ee6..6beff63041726c4884292351e180cc8d55181297 100644 GIT binary patch delta 1730 zcmaJ>OH30{6y+8QSR}TEcA$JZHZk~t0t#yIgQWx#DFkQ?3(;hx12jxK&2%U!2~n4R zK;Qu{hQ!2`F+{LTOgA9Jpetj*wINXxH*R2IqKOU8Ohu@^aaM26Iq$xE&wcN1FX_L& z(mOUTI2P9|-9~6COTlfFA^tPhSZi_VAy<$e6 z?lhtGkX5?MZ)T#1lUAal)kvXqFVdAK)!vPyg9q)5)}y&mqof~O0V(ldzQOK|hDA0| zM|=5RaflIEx{Vk8JuL0+XL|-%AKLy=}?5ntuUz-z)3+&i+h=_&E3q<<}T^AImm=qF*)I8fsN9q@q`_Y;;l@0K_z8w zKEeunCA%AyqOACB7ED`+3LMIYXN5${-AJFpw*ltJ^sZ%AoM8a`AI+*+&R|W}6cLY6 z;>ZVWQ&6ayuaT$Xy#}RNuR+Pq+wfX0%>3^AU;cRfef^?RV?J)T!67_W2$LF-%5Csk zgG$(dikBFn0L)5-0?+TibuJSulJFf9#7Ov(nXDhq14(r`HNIPrUj@S{{#o#qe3()% zXsV`}pNeabx5-yug#NQRc za>2iCXde_p9GiR%QZYPK-%F}Xssyi?Wt+qjm{FUZs_0~W$q1FDFhfdeqZAehc>KN{ zYAqJx)WWVBI2>QCfzP?P%?;VP9(iavL-Hi`2m73Ot^tmyxG4qa*j^Dmewv5ZTOmi~ z%_+Q~T{!0Uup8Ij%@ccIqlVy5CJt&k4g+f)&?X&e(qZb`I^zq1$%3W21x{tch^A0^X(a~w*R!_bv;0$Ni zG&*N{cpw-KM#oSx*1%Pa_LW6&N=e@mo8va@d6(V8`P*pvLbtz-|qwNdpqOTLId&@F}0~+d{_z%)hyC zlMZg1Bw|Y|PFR4%JM}PQmU-!4rh|~-_#Etb&Hz~Ebmc+PDuK09%;4tGQ#N2_kAwha zqL81Il!?!RFXlsB_NbD8hcfXG0ncQjqX@{|$s-vjJ4!pu?SB0=F4(f!G}h zY??oKGcVi3H!ZLrRwyfQB76%o7Ku_J0dK|4g)l>86WT)bIh-Wz^kcUg3UNg##7z>1U#2cCl;3xP)|xt82kSNY(Fnbt_`^MD~!;Pyi)p7w%0b;=8q#++byfX1dSs7#d{hgra<{Sbx-zSjeF{PC>^ RYWZWg9}Yq!wc>{x`oAt&yO#g} diff --git a/profile/georgia-reborn/scripts/Base/gr-async-loader.js b/profile/georgia-reborn/scripts/Base/gr-async-loader.js index 39fa4630..16f5d6d7 100644 --- a/profile/georgia-reborn/scripts/Base/gr-async-loader.js +++ b/profile/georgia-reborn/scripts/Base/gr-async-loader.js @@ -1,13 +1,13 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Preloader * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Preloader * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; @@ -22,95 +22,102 @@ window.DefineScript('Georgia-ReBORN', { features: { drag_n_drop: true } }); -/** @type {string} */ -const basePath = `${fb.ProfilePath}georgia-reborn\\`; -/** @type {{ loading: string; fileName: string; fileIndex: number }} */ -const loadStrs = { loading: 'Loading:', fileName: '', fileIndex: 0 }; -/** @type {number} */ +/** @global @type {{ loading: string; fileName: string; fileIndex: number }} */ +const loadStr = { loading: 'Loading:', fileName: '', fileIndex: 0 }; +/** @global @type {number} */ const startTime = Date.now(); //////////////////////////// // * SYSTEM FILE LOADER * // //////////////////////////// -include(`${basePath}scripts\\base\\gr-helpers.js`); -include(`${basePath}scripts\\base\\gr-common.js`); -include(`${basePath}scripts\\base\\gr-configuration.js`); -include(`${basePath}scripts\\base\\gr-display.js`); -include(`${basePath}scripts\\base\\gr-defaults.js`); -include(`${basePath}scripts\\base\\gr-settings.js`); -include(`${basePath}scripts\\base\\gr-setup.js`); +include(`${fb.ProfilePath}georgia-reborn\\scripts\\base\\gr-helpers.js`); +include(`${fb.ProfilePath}georgia-reborn\\scripts\\base\\gr-common.js`); +include(`${fb.ProfilePath}georgia-reborn\\scripts\\base\\gr-config.js`); +include(`${fb.ProfilePath}georgia-reborn\\scripts\\base\\gr-config-defaults.js`); +include(`${fb.ProfilePath}georgia-reborn\\scripts\\base\\gr-settings.js`); +include(`${fb.ProfilePath}georgia-reborn\\scripts\\base\\gr-setup.js`); /////////////////////////// // * ASYNC FILE LOADER * // /////////////////////////// -/** @type {string} */ +/** @global @type {string} */ const fileList = [ - 'scripts\\playlist\\pl-control-scrollbar.js', - 'scripts\\playlist\\pl-control-list.js', - 'scripts\\playlist\\pl-linked-list.js', - 'scripts\\playlist\\pl-main.js', - 'scripts\\library\\lib-main.js', - 'scripts\\library\\scripts\\lib-helpers.js', - 'scripts\\library\\scripts\\lib-properties.js', - 'scripts\\library\\scripts\\lib-interface.js', - 'scripts\\library\\scripts\\lib-panel.js', - 'scripts\\library\\scripts\\lib-scrollbar.js', - 'scripts\\library\\scripts\\lib-library.js', - 'scripts\\library\\scripts\\lib-populate.js', - 'scripts\\library\\scripts\\lib-search.js', - 'scripts\\library\\scripts\\lib-buttons.js', - 'scripts\\library\\scripts\\lib-popupbox.js', - 'scripts\\library\\scripts\\lib-timers.js', - 'scripts\\library\\scripts\\lib-menu.js', - 'scripts\\library\\scripts\\lib-initialise.js', - 'scripts\\library\\scripts\\lib-images.js', - 'scripts\\library\\scripts\\lib-callbacks.js', - 'scripts\\biography\\bio-main.js', - 'scripts\\biography\\scripts\\bio-helpers.js', - 'scripts\\biography\\scripts\\bio-properties.js', - 'scripts\\biography\\scripts\\bio-settings.js', - 'scripts\\biography\\scripts\\bio-interface.js', - 'scripts\\biography\\scripts\\bio-language.js', - 'scripts\\biography\\scripts\\bio-panel.js', - 'scripts\\biography\\scripts\\bio-server.js', - 'scripts\\biography\\scripts\\bio-allmusic.js', - 'scripts\\biography\\scripts\\bio-lastfm.js', - 'scripts\\biography\\scripts\\bio-wikipedia.js', - 'scripts\\biography\\scripts\\bio-names.js', - 'scripts\\biography\\scripts\\bio-scrollbar.js', - 'scripts\\biography\\scripts\\bio-buttons.js', - 'scripts\\biography\\scripts\\bio-menu.js', - 'scripts\\biography\\scripts\\bio-text.js', - 'scripts\\biography\\scripts\\bio-lyrics.js', - 'scripts\\biography\\scripts\\bio-tagger.js', - 'scripts\\biography\\scripts\\bio-resize.js', - 'scripts\\biography\\scripts\\bio-library.js', - 'scripts\\biography\\scripts\\bio-images.js', - 'scripts\\biography\\scripts\\bio-filmstrip.js', - 'scripts\\biography\\scripts\\bio-timers.js', - 'scripts\\biography\\scripts\\bio-popupbox.js', - 'scripts\\biography\\scripts\\bio-initialise.js', - 'scripts\\biography\\scripts\\bio-callbacks.js', - 'scripts\\base\\gr-color.js', - 'scripts\\base\\gr-themes.js', - 'scripts\\base\\gr-theme-presets.js', - 'scripts\\base\\gr-buttons.js', - 'scripts\\base\\gr-main-components.js', - 'scripts\\base\\gr-lyrics.js', - 'scripts\\base\\gr-callbacks.js', - 'scripts\\base\\gr-main-functions.js', - 'scripts\\base\\gr-context-menu.js', - 'scripts\\base\\gr-menu.js', - 'scripts\\base\\gr-menu-custom.js', - 'scripts\\base\\gr-main.js' + 'playlist\\pl-main.js', + 'playlist\\scripts\\pl-helpers.js', + 'playlist\\scripts\\pl-setup.js', + 'playlist\\scripts\\pl-properties.js', + 'playlist\\scripts\\pl-components.js', + 'playlist\\scripts\\pl-controls.js', + 'playlist\\scripts\\pl-list.js', + 'playlist\\scripts\\pl-list-content.js', + 'playlist\\scripts\\pl-list-header.js', + 'playlist\\scripts\\pl-list-row.js', + 'playlist\\scripts\\pl-callbacks.js', + 'playlist\\scripts\\pl-playlist.js', + 'library\\lib-main.js', + 'library\\scripts\\lib-helpers.js', + 'library\\scripts\\lib-properties.js', + 'library\\scripts\\lib-interface.js', + 'library\\scripts\\lib-panel.js', + 'library\\scripts\\lib-scrollbar.js', + 'library\\scripts\\lib-library.js', + 'library\\scripts\\lib-populate.js', + 'library\\scripts\\lib-search.js', + 'library\\scripts\\lib-buttons.js', + 'library\\scripts\\lib-popupbox.js', + 'library\\scripts\\lib-timers.js', + 'library\\scripts\\lib-menu.js', + 'library\\scripts\\lib-initialise.js', + 'library\\scripts\\lib-images.js', + 'library\\scripts\\lib-callbacks.js', + 'biography\\bio-main.js', + 'biography\\scripts\\bio-helpers.js', + 'biography\\scripts\\bio-properties.js', + 'biography\\scripts\\bio-settings.js', + 'biography\\scripts\\bio-interface.js', + 'biography\\scripts\\bio-language.js', + 'biography\\scripts\\bio-panel.js', + 'biography\\scripts\\bio-server.js', + 'biography\\scripts\\bio-allmusic.js', + 'biography\\scripts\\bio-lastfm.js', + 'biography\\scripts\\bio-wikipedia.js', + 'biography\\scripts\\bio-names.js', + 'biography\\scripts\\bio-scrollbar.js', + 'biography\\scripts\\bio-buttons.js', + 'biography\\scripts\\bio-menu.js', + 'biography\\scripts\\bio-text.js', + 'biography\\scripts\\bio-lyrics.js', + 'biography\\scripts\\bio-tagger.js', + 'biography\\scripts\\bio-resize.js', + 'biography\\scripts\\bio-library.js', + 'biography\\scripts\\bio-images.js', + 'biography\\scripts\\bio-filmstrip.js', + 'biography\\scripts\\bio-timers.js', + 'biography\\scripts\\bio-popupbox.js', + 'biography\\scripts\\bio-initialise.js', + 'biography\\scripts\\bio-callbacks.js', + 'base\\gr-display.js', + 'base\\gr-color.js', + 'base\\gr-theme-colors.js', + 'base\\gr-theme-presets.js', + 'base\\gr-menu-context.js', + 'base\\gr-menu-custom.js', + 'base\\gr-menu.js', + 'base\\gr-buttons.js', + 'base\\gr-main.js', + 'base\\gr-main-components.js', + 'base\\gr-lyrics.js', + 'base\\gr-callbacks.js', + 'base\\gr-initialize.js' ]; /** * Loads script files asynchronously on foobar startup or reload. - * @param {string} filePath The path to the file to load. + * @global + * @param {string} filePath - The path to the file to load. * @returns {Promise} A promise that resolves when the file has been loaded. */ function loadAsyncFile(filePath) { @@ -126,26 +133,29 @@ function loadAsyncFile(filePath) { /** * Loads a list of files asynchronously if pref.asyncThemePreloader is true, otherwise synchronously. * Throttles UI updates approximately every 16 milliseconds during async loading. - * @param {string[]} fileList The list of files to load. - * @param {number} startTime The timestamp marking the start of the operation, used for calculating total load time and UI update intervals. + * @global + * @param {string[]} fileList - The list of files to load. + * @param {number} startTime - The timestamp marking the start of the operation, used for calculating total load time and UI update intervals. */ async function includeFiles(fileList, startTime) { - if (pref.asyncThemePreloader) { + if (grSet.asyncThemePreloader) { const refreshTime = 16; // ~60Hz let lastRepaintTime = startTime; for (let i = 0; i < fileList.length; i++) { - loadStrs.fileName = `${fileList[i]} ...`; - loadStrs.fileIndex = i; + loadStr.fileName = `${fileList[i]} ...`; + loadStr.fileIndex = i; const currentTime = Date.now(); if (currentTime - lastRepaintTime > refreshTime) { lastRepaintTime = currentTime; window.Repaint(); } - await loadAsyncFile(`${basePath}${fileList[i]}`); + await loadAsyncFile(`${fb.ProfilePath}georgia-reborn\\scripts\\${fileList[i]}`); } return; } - for (const filePath of fileList) include(`${basePath}${filePath}`) + for (const filePath of fileList) { + include(`${fb.ProfilePath}georgia-reborn\\scripts\\${filePath}`); + } } @@ -155,8 +165,8 @@ async function includeFiles(fileList, startTime) { includeFiles(fileList, startTime).then(() => { console.log(`Georgia-ReBORN loaded in ${Date.now() - startTime}ms`); - if (pref.checkForUpdates) { - scheduleUpdateCheck(0); + if (grSet.checkForUpdates) { + grCfg.scheduleUpdateCheck(0); } }); @@ -171,24 +181,25 @@ includeFiles(fileList, startTime).then(() => { * The value 'false' disables the day/night theme feature, which is the default setting. * If the feature is disabled or if other theme-related preferences are set, the function exits without changing the theme. * This function has a side effect of modifying pref.theme. - * @param {Date} date The `Date` object that represents the current date and time. - * @returns {string} The current time in the format "hours:minutes AM/PM". + * @global + * @param {Date} date - The `Date` object that represents the current date and time. + * @returns {string|null} The current time in the format "hours:minutes AM/PM" or null. */ function initThemeDayNightMode(date) { - if (!pref.themeDayNightMode) return; + if (!grSet.themeDayNightMode) return null; // * Safeguard to handle number values and convert them to string with default end hour - if (typeof pref.themeDayNightMode === 'number') { - pref.themeDayNightMode = `${pref.themeDayNightMode}-18`; // Defaulting end hour to 18 (6 PM) - } else if (typeof pref.themeDayNightMode === 'string' && !pref.themeDayNightMode.includes('-')) { - pref.themeDayNightMode = `${pref.themeDayNightMode}-${pref.themeDayNightMode}`; // Same start and end hour + if (typeof grSet.themeDayNightMode === 'number') { + grSet.themeDayNightMode = `${grSet.themeDayNightMode}-18`; // Defaulting end hour to 18 (6 PM) + } else if (typeof grSet.themeDayNightMode === 'string' && !grSet.themeDayNightMode.includes('-')) { + grSet.themeDayNightMode = `${grSet.themeDayNightMode}-${grSet.themeDayNightMode}`; // Same start and end hour } const hours = date.getHours(); const minutes = date.getMinutes(); // * Parse the start and end times from the themeDayNightMode string - const [startHourStr, endHourStr] = pref.themeDayNightMode.split('-').map(Number); + const [startHourStr, endHourStr] = grSet.themeDayNightMode.split('-').map(Number); const startHour = parseInt(startHourStr, 10); const rawEndHour = parseInt(endHourStr, 10); const endHour = rawEndHour === 0 ? 24 : rawEndHour; @@ -204,7 +215,7 @@ function initThemeDayNightMode(date) { const timeSuffix = hours >= 12 ? 'PM' : 'AM'; // * Set theme based on day time - pref.themeDayNightTime = isDayTime ? 'day' : 'night'; + grSet.themeDayNightTime = isDayTime ? 'day' : 'night'; setThemeDayNightTheme(isDayTime); return `${formattedHours}:${formattedMinutes} ${timeSuffix}`; @@ -214,92 +225,94 @@ function initThemeDayNightMode(date) { /** * Sets the theme based on the time of day. * Used to switch between day and night mode by applying the corresponding theme settings. - * @param {boolean} isDaytime When true, the daytime theme is applied; otherwise, the nighttime theme is applied. + * @global + * @param {boolean} isDaytime - When true, the daytime theme is applied; otherwise, the nighttime theme is applied. */ function setThemeDayNightTheme(isDaytime) { const _day_night = isDaytime ? '_day' : '_night'; - pref.theme = pref[`theme${_day_night}`]; - pref.styleNighttime = pref[`styleNighttime${_day_night}`]; - pref.styleBevel = pref[`styleBevel${_day_night}`]; - pref.styleBlend = pref[`styleBlend${_day_night}`]; - pref.styleBlend2 = pref[`styleBlend2${_day_night}`]; - pref.styleGradient = pref[`styleGradient${_day_night}`]; - pref.styleGradient2 = pref[`styleGradient2${_day_night}`]; - pref.styleAlternative = pref[`styleAlternative${_day_night}`]; - pref.styleAlternative2 = pref[`styleAlternative2${_day_night}`]; - pref.styleBlackAndWhite = pref[`styleBlackAndWhite${_day_night}`]; - pref.styleBlackAndWhite2 = pref[`styleBlackAndWhite2${_day_night}`]; - pref.styleBlackAndWhiteReborn = pref[`styleBlackAndWhiteReborn${_day_night}`]; - pref.styleBlackReborn = pref[`styleBlackReborn${_day_night}`]; - pref.styleRebornWhite = pref[`styleRebornWhite${_day_night}`]; - pref.styleRebornBlack = pref[`styleRebornBlack${_day_night}`]; - pref.styleRebornFusion = pref[`styleRebornFusion${_day_night}`]; - pref.styleRebornFusion2 = pref[`styleRebornFusion2${_day_night}`]; - pref.styleRebornFusionAccent = pref[`styleRebornFusionAccent${_day_night}`]; - pref.styleRandomPastel = pref[`styleRandomPastel${_day_night}`]; - pref.styleRandomDark = pref[`styleRandomDark${_day_night}`]; - pref.styleRandomAutoColor = pref[`styleRandomAutoColor${_day_night}`]; - pref.styleTopMenuButtons = pref[`styleTopMenuButtons${_day_night}`]; - pref.styleTransportButtons = pref[`styleTransportButtons${_day_night}`]; - pref.styleProgressBarDesign = pref[`styleProgressBarDesign${_day_night}`]; - pref.styleProgressBar = pref[`styleProgressBar${_day_night}`]; - pref.styleProgressBarFill = pref[`styleProgressBarFill${_day_night}`]; - pref.styleVolumeBarDesign = pref[`styleVolumeBarDesign${_day_night}`]; - pref.styleVolumeBar = pref[`styleVolumeBar${_day_night}`]; - pref.styleVolumeBarFill = pref[`styleVolumeBarFill${_day_night}`]; - pref.themeBrightness = pref[`themeBrightness${_day_night}`]; - pref.preset = pref[`preset${_day_night}`]; + grSet.theme = grSet[`theme${_day_night}`]; + grSet.styleNighttime = grSet[`styleNighttime${_day_night}`]; + grSet.styleBevel = grSet[`styleBevel${_day_night}`]; + grSet.styleBlend = grSet[`styleBlend${_day_night}`]; + grSet.styleBlend2 = grSet[`styleBlend2${_day_night}`]; + grSet.styleGradient = grSet[`styleGradient${_day_night}`]; + grSet.styleGradient2 = grSet[`styleGradient2${_day_night}`]; + grSet.styleAlternative = grSet[`styleAlternative${_day_night}`]; + grSet.styleAlternative2 = grSet[`styleAlternative2${_day_night}`]; + grSet.styleBlackAndWhite = grSet[`styleBlackAndWhite${_day_night}`]; + grSet.styleBlackAndWhite2 = grSet[`styleBlackAndWhite2${_day_night}`]; + grSet.styleBlackAndWhiteReborn = grSet[`styleBlackAndWhiteReborn${_day_night}`]; + grSet.styleBlackReborn = grSet[`styleBlackReborn${_day_night}`]; + grSet.styleRebornWhite = grSet[`styleRebornWhite${_day_night}`]; + grSet.styleRebornBlack = grSet[`styleRebornBlack${_day_night}`]; + grSet.styleRebornFusion = grSet[`styleRebornFusion${_day_night}`]; + grSet.styleRebornFusion2 = grSet[`styleRebornFusion2${_day_night}`]; + grSet.styleRebornFusionAccent = grSet[`styleRebornFusionAccent${_day_night}`]; + grSet.styleRandomPastel = grSet[`styleRandomPastel${_day_night}`]; + grSet.styleRandomDark = grSet[`styleRandomDark${_day_night}`]; + grSet.styleRandomAutoColor = grSet[`styleRandomAutoColor${_day_night}`]; + grSet.styleTopMenuButtons = grSet[`styleTopMenuButtons${_day_night}`]; + grSet.styleTransportButtons = grSet[`styleTransportButtons${_day_night}`]; + grSet.styleProgressBarDesign = grSet[`styleProgressBarDesign${_day_night}`]; + grSet.styleProgressBar = grSet[`styleProgressBar${_day_night}`]; + grSet.styleProgressBarFill = grSet[`styleProgressBarFill${_day_night}`]; + grSet.styleVolumeBarDesign = grSet[`styleVolumeBarDesign${_day_night}`]; + grSet.styleVolumeBar = grSet[`styleVolumeBar${_day_night}`]; + grSet.styleVolumeBarFill = grSet[`styleVolumeBarFill${_day_night}`]; + grSet.themeBrightness = grSet[`themeBrightness${_day_night}`]; + grSet.preset = grSet[`preset${_day_night}`]; } /** * Sets and updates the theme style to the daytime or nighttime theme when selecting a theme style in top menu Options > Style. * Used when daytime or nighttime theme setup is active. + * @global */ function setThemeDayNightStyle() { - const _day_night = pref.themeSetupDay ? '_day' : '_night'; - - pref[`theme${_day_night}`] = pref.theme; - pref[`styleNighttime${_day_night}`] = pref.styleNighttime; - pref[`styleBevel${_day_night}`] = pref.styleBevel; - pref[`styleBlend${_day_night}`] = pref.styleBlend; - pref[`styleBlend2${_day_night}`] = pref.styleBlend2; - pref[`styleGradient${_day_night}`] = pref.styleGradient; - pref[`styleGradient2${_day_night}`] = pref.styleGradient2; - pref[`styleAlternative${_day_night}`] = pref.styleAlternative; - pref[`styleAlternative2${_day_night}`] = pref.styleAlternative2; - pref[`styleBlackAndWhite${_day_night}`] = pref.styleBlackAndWhite; - pref[`styleBlackAndWhite2${_day_night}`] = pref.styleBlackAndWhite2; - pref[`styleBlackAndWhiteReborn${_day_night}`] = pref.styleBlackAndWhiteReborn; - pref[`styleBlackReborn${_day_night}`] = pref.styleBlackReborn; - pref[`styleRebornWhite${_day_night}`] = pref.styleRebornWhite; - pref[`styleRebornBlack${_day_night}`] = pref.styleRebornBlack; - pref[`styleRebornFusion${_day_night}`] = pref.styleRebornFusion; - pref[`styleRebornFusion2${_day_night}`] = pref.styleRebornFusion2; - pref[`styleRebornFusionAccent${_day_night}`] = pref.styleRebornFusionAccent; - pref[`styleRandomPastel${_day_night}`] = pref.styleRandomPastel; - pref[`styleRandomDark${_day_night}`] = pref.styleRandomDark; - pref[`styleRandomAutoColor${_day_night}`] = pref.styleRandomAutoColor; - pref[`styleTopMenuButtons${_day_night}`] = pref.styleTopMenuButtons; - pref[`styleTransportButtons${_day_night}`] = pref.styleTransportButtons; - pref[`styleProgressBarDesign${_day_night}`] = pref.styleProgressBarDesign; - pref[`styleProgressBar${_day_night}`] = pref.styleProgressBar; - pref[`styleProgressBarFill${_day_night}`] = pref.styleProgressBarFill; - pref[`styleVolumeBarDesign${_day_night}`] = pref.styleVolumeBarDesign; - pref[`styleVolumeBar${_day_night}`] = pref.styleVolumeBar; - pref[`styleVolumeBarFill${_day_night}`] = pref.styleVolumeBarFill; - pref[`themeBrightness${_day_night}`] = pref.themeBrightness; - pref[`preset${_day_night}`] = pref.preset; + const _day_night = grSet.themeSetupDay ? '_day' : '_night'; + + grSet[`theme${_day_night}`] = grSet.theme; + grSet[`styleNighttime${_day_night}`] = grSet.styleNighttime; + grSet[`styleBevel${_day_night}`] = grSet.styleBevel; + grSet[`styleBlend${_day_night}`] = grSet.styleBlend; + grSet[`styleBlend2${_day_night}`] = grSet.styleBlend2; + grSet[`styleGradient${_day_night}`] = grSet.styleGradient; + grSet[`styleGradient2${_day_night}`] = grSet.styleGradient2; + grSet[`styleAlternative${_day_night}`] = grSet.styleAlternative; + grSet[`styleAlternative2${_day_night}`] = grSet.styleAlternative2; + grSet[`styleBlackAndWhite${_day_night}`] = grSet.styleBlackAndWhite; + grSet[`styleBlackAndWhite2${_day_night}`] = grSet.styleBlackAndWhite2; + grSet[`styleBlackAndWhiteReborn${_day_night}`] = grSet.styleBlackAndWhiteReborn; + grSet[`styleBlackReborn${_day_night}`] = grSet.styleBlackReborn; + grSet[`styleRebornWhite${_day_night}`] = grSet.styleRebornWhite; + grSet[`styleRebornBlack${_day_night}`] = grSet.styleRebornBlack; + grSet[`styleRebornFusion${_day_night}`] = grSet.styleRebornFusion; + grSet[`styleRebornFusion2${_day_night}`] = grSet.styleRebornFusion2; + grSet[`styleRebornFusionAccent${_day_night}`] = grSet.styleRebornFusionAccent; + grSet[`styleRandomPastel${_day_night}`] = grSet.styleRandomPastel; + grSet[`styleRandomDark${_day_night}`] = grSet.styleRandomDark; + grSet[`styleRandomAutoColor${_day_night}`] = grSet.styleRandomAutoColor; + grSet[`styleTopMenuButtons${_day_night}`] = grSet.styleTopMenuButtons; + grSet[`styleTransportButtons${_day_night}`] = grSet.styleTransportButtons; + grSet[`styleProgressBarDesign${_day_night}`] = grSet.styleProgressBarDesign; + grSet[`styleProgressBar${_day_night}`] = grSet.styleProgressBar; + grSet[`styleProgressBarFill${_day_night}`] = grSet.styleProgressBarFill; + grSet[`styleVolumeBarDesign${_day_night}`] = grSet.styleVolumeBarDesign; + grSet[`styleVolumeBar${_day_night}`] = grSet.styleVolumeBar; + grSet[`styleVolumeBarFill${_day_night}`] = grSet.styleVolumeBarFill; + grSet[`themeBrightness${_day_night}`] = grSet.themeBrightness; + grSet[`preset${_day_night}`] = grSet.preset; } /** * Start the theme day/night mode initialization before drawing the preloader. */ -if (pref.themeDayNightMode) { +if (grSet.themeDayNightMode) { initThemeDayNightMode(new Date()); - const [dayStart, nightStart] = pref.themeDayNightMode.split('-'); + const [dayStart, nightStart] = grSet.themeDayNightMode.split('-'); console.log(`Theme day/night mode is active, current time is: ${initThemeDayNightMode(new Date())}. The schedule has been set to ${To12HourTimeFormat(dayStart)} (day) - ${To12HourTimeFormat(nightStart)} (night).`); } @@ -311,115 +324,135 @@ if (pref.themeDayNightMode) { * Draws the preloader on foobar startup or reload. * * This callback will be overridden by the main UI once the theme loads. - * @param {GdiGraphics} gr + * @global + * @param {GdiGraphics} gr - The GDI graphics object. */ function on_paint(gr) { // * SYSTEM * // + const col = {}; const ww = window.Width; const wh = window.Height; - const col = {}; - const RES_4K = pref.displayRes === '4K' || (ww > 3000 || wh > 1300); + const RES_4K = grSet.displayRes === '4K' || ww > 2560 && wh > 1600; + const SCALE = (val) => RES_4K ? val * 2 : val; // * FONTS * // - const fontLowerBarBold = pref.customThemeFonts ? customFont.fontLowerBarArtist : 'HelveticaNeueLT Pro 65 Md'; - const fontLowerBarLight = pref.customThemeFonts ? customFont.fontLowerBarTitle : 'HelveticaNeueLT Pro 45 Lt'; + const fontLowerBarBold = grSet.customThemeFonts ? grCfg.customFont.fontLowerBarArtist : 'HelveticaNeueLT Pro 65 Md'; + const fontLowerBarLight = grSet.customThemeFonts ? grCfg.customFont.fontLowerBarTitle : 'HelveticaNeueLT Pro 45 Lt'; const ft_lower_bar_bold = Font(fontLowerBarBold, - pref.layout === 'compact' ? pref.lowerBarFontSize_compact || 16 : - pref.layout === 'artwork' ? pref.lowerBarFontSize_artwork || 16 : - pref.lowerBarFontSize_default || 18, 0); + grSet.layout === 'compact' ? SCALE(grSet.lowerBarFontSize_compact) || SCALE(16) : + grSet.layout === 'artwork' ? SCALE(grSet.lowerBarFontSize_artwork) || SCALE(16) : + SCALE(grSet.lowerBarFontSize_default) || SCALE(18), 0); const ft_lower_bar_light = Font(fontLowerBarLight, - pref.layout === 'compact' ? pref.lowerBarFontSize_compact || 16 : - pref.layout === 'artwork' ? pref.lowerBarFontSize_artwork || 16 : - pref.lowerBarFontSize_default || 18, 0); + grSet.layout === 'compact' ? SCALE(grSet.lowerBarFontSize_compact) || SCALE(16) : + grSet.layout === 'artwork' ? SCALE(grSet.lowerBarFontSize_artwork) || SCALE(16) : + SCALE(grSet.lowerBarFontSize_default) || SCALE(18), 0); // * GEOMETRY * // + const layoutNotDefault = grSet.layout !== 'default'; + const correction = layoutNotDefault ? (RES_4K ? 33 : 18) : (RES_4K ? 65 : 35); const lowerBarHeight = SCALE(120); - const lowerBarTop = pref.layout !== 'default' ? wh - lowerBarHeight + (RES_4K ? 33 : 18) : wh - lowerBarHeight + (RES_4K ? 65 : 35); - const loadingWidth = Math.ceil(gr.MeasureString(loadStrs.loading, ft_lower_bar_light, 0, 0, 0, 0).Width); - const titleMeasurements = gr.MeasureString(loadStrs.fileName, ft_lower_bar_light, 0, 0, 0, 0); + const lowerBarTop = wh - lowerBarHeight + correction; + const loadingWidth = Math.ceil(gr.MeasureString(loadStr.loading, ft_lower_bar_light, 0, 0, 0, 0).Width); + const titleMeasurements = gr.MeasureString(loadStr.fileName, ft_lower_bar_light, 0, 0, 0, 0); const progressBar = { - x: pref.layout !== 'default' ? SCALE(20) : SCALE(40), - y: pref.layout !== 'default' ? Math.round(lowerBarTop + titleMeasurements.Height + SCALE(10) + (ww > 1920 ? 2 : 0)) : Math.round(lowerBarTop + titleMeasurements.Height + SCALE(12) + (ww > 1920 ? 2 : 0)), - w: pref.layout !== 'default' ? ww - SCALE(40) : ww - SCALE(80), - h: pref.layout !== 'default' ? SCALE(10) + (ww > 1920 ? 2 : 0) : SCALE(12) + (ww > 1920 ? 2 : 0) + x: SCALE(layoutNotDefault ? 20 : 40), + y: Math.round(lowerBarTop + titleMeasurements.Height + SCALE(layoutNotDefault ? 10 : 12) + (ww > 1920 ? 2 : 0)), + w: ww - SCALE(layoutNotDefault ? 40 : 80), + h: SCALE(layoutNotDefault ? 10 : 12) + (ww > 1920 ? 2 : 0) }; // * COLORS * // - const themeNight = pref.themeDayNightMode && pref.themeDayNightTime === 'night' && !pref.styleRebornWhite; - const styleNight = pref.styleNighttime && !pref.styleRebornWhite; - const pref_theme_neon = ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme); - const pref_theme_custom = pref.theme && pref.theme.startsWith('custom'); - const customThemes = { custom01: customTheme01, custom02: customTheme02, custom03: customTheme03, custom04: customTheme04, custom05: customTheme05, custom06: customTheme06, custom07: customTheme07, custom08: customTheme08, custom09: customTheme09, custom10: customTheme10 }; - const customTheme = customThemes[pref.theme]; - - col.bg = - pref.theme === 'white' ? pref.styleBlackAndWhite ? RGB(230, 230, 230) : pref.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGB(245, 245, 245) : - pref.theme === 'black' ? RGB(25, 25, 25) : - pref.theme === 'reborn' || pref.theme === 'random' ? styleNight || pref.styleRebornBlack || themeNight ? RGB(25, 25, 25) : RGB(245, 245, 245) : - pref.theme === 'blue' ? RGB(5, 110, 195) : - pref.theme === 'darkblue' ? RGB(22, 40, 63) : - pref.theme === 'red' ? RGB(100, 20, 20) : - pref.theme === 'cream' ? RGB(255, 247, 240) : - pref_theme_neon ? RGB(20, 20, 20) : - pref_theme_custom ? customTheme.preloaderBg !== '' ? HEXtoRGB(customTheme.preloaderBg) : styleNight || themeNight ? RGB(25, 25, 25) : RGB(245, 245, 245) : ''; - - col.lowerBarTitle = - pref.theme === 'white' ? RGB(120, 120, 120) : - pref.theme === 'black' ? RGB(200, 200, 200) : - pref.theme === 'reborn' || pref.theme === 'random' ? themeNight ? RGB(200, 200, 200) : RGB(120, 120, 120) : - pref.theme === 'blue' ? RGB(255, 255, 255) : - pref.theme === 'darkblue' ? RGB(255, 255, 255) : - pref.theme === 'red' ? RGB(220, 220, 220) : - pref.theme === 'cream' ? RGB(100, 100, 100) : - pref_theme_neon ? RGB(200, 200, 200) : - pref_theme_custom ? customTheme.preloaderLowerBarTitle !== '' ? HEXtoRGB(customTheme.preloaderLowerBarTitle) : styleNight || themeNight ? RGB(200, 200, 200) : RGB(120, 120, 120) : ''; - - col.progressBar = - pref.theme === 'white' ? pref.styleBlackAndWhite ? RGB(210, 210, 210) : pref.styleBlackAndWhite2 ? RGB(40, 40, 40) : RGB(220, 220, 220) : - pref.theme === 'black' ? RGB(35, 35, 35) : - pref.theme === 'reborn' || pref.theme === 'random' ? styleNight || pref.styleRebornBlack ? RGB(50, 50, 50) : themeNight ? RGB(35, 35, 35) : RGB(220, 220, 220) : - pref.theme === 'blue' ? RGB(10, 130, 220) : - pref.theme === 'darkblue' ? RGB(27, 55, 90) : - pref.theme === 'red' ? RGB(140, 25, 25) : - pref.theme === 'cream' ? RGB(255, 255, 255) : - pref_theme_neon ? RGB(35, 35, 35) : - pref_theme_custom ? customTheme.preloaderProgressBar !== '' ? HEXtoRGB(customTheme.preloaderProgressBar) : styleNight || themeNight ? RGB(35, 35, 35) : RGB(220, 220, 220) : ''; - - col.progressBarFill = - pref.theme === 'white' ? pref.styleBlackAndWhite ? RGB(255, 255, 255) : pref.styleBlackAndWhite2 ? RGB(210, 210, 210) : RGB(25, 160, 240) : - pref.theme === 'black' ? RGB(175, 205, 225) : - pref.theme === 'reborn' ? styleNight ? RGB(195, 225, 230) : pref.styleRebornBlack || themeNight ? RGB(255, 255, 255) : RGB(90, 90, 90) : - pref.theme === 'random' ? styleNight ? RGB(140, 215, 215) : themeNight ? RGB(255, 255, 255) : RGB(70, 70, 70) : - pref.theme === 'blue' ? RGB(242, 230, 170) : - pref.theme === 'darkblue' ? RGB(255, 202, 128) : - pref.theme === 'red' ? RGB(245, 212, 165) : - pref.theme === 'cream' ? RGB(120, 170, 130) : - pref.theme === 'nblue' ? RGB(0, 200, 255) : - pref.theme === 'ngreen' ? RGB(0, 200, 0) : - pref.theme === 'nred' ? RGB(240, 10, 60) : - pref.theme === 'ngold' ? RGB(255, 205, 5) : - pref_theme_custom ? customTheme.preloaderProgressBarFill !== '' ? HEXtoRGB(customTheme.preloaderProgressBarFill) : styleNight || themeNight ? RGB(225, 225, 195) : RGB(50, 25, 70) : ''; - - col.progressBarFrame = - pref.theme === 'blue' ? RGB(22, 107, 186) : - pref.theme === 'darkblue' ? RGB(22, 37, 54) : - pref.theme === 'red' ? RGB(92, 21, 21) : - pref.theme === 'cream' ? RGB(230, 230, 230) : - pref_theme_custom ? customTheme.preloaderProgressBarFrame !== '' ? HEXtoRGB(customTheme.preloaderProgressBarFrame) : styleNight || themeNight ? RGB(25, 25, 25) : RGB(255, 255, 255) : ''; - - col.uiHacksFrame = - pref.theme === 'white' ? pref.styleBlackAndWhite ? RGB(230, 230, 230) : pref.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGB(245, 245, 245) : - pref.theme === 'black' ? RGB(35, 35, 35) : - pref.theme === 'reborn' || pref.theme === 'random' ? styleNight || pref.styleRebornBlack || themeNight ? RGB(25, 25, 25) : RGB(245, 245, 245) : - pref.theme === 'blue' ? RGB(63, 155, 202) : - pref.theme === 'darkblue' ? RGB(27, 55, 90) : - pref.theme === 'red' ? RGB(125, 0, 0) : - pref.theme === 'cream' ? RGB(255, 247, 240) : - pref_theme_neon ? RGB(30, 30, 30) : - pref_theme_custom ? customTheme.preloaderUIHacksFrame !== '' ? HEXtoRGB(customTheme.preloaderUIHacksFrame) : styleNight || themeNight ? RGB(25, 25, 25) : RGB(245, 245, 245) : ''; + const themeNight = grSet.themeDayNightMode && grSet.themeDayNightTime === 'night' && !grSet.styleRebornWhite; + const styleNight = grSet.styleNighttime && !grSet.styleRebornWhite; + const pref_theme_neon = ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme); + const pref_theme_custom = grSet.theme && grSet.theme.startsWith('custom'); + const customThemes = { custom01: grCfg.customTheme01, custom02: grCfg.customTheme02, custom03: grCfg.customTheme03, custom04: grCfg.customTheme04, custom05: grCfg.customTheme05, custom06: grCfg.customTheme06, custom07: grCfg.customTheme07, custom08: grCfg.customTheme08, custom09: grCfg.customTheme09, custom10: grCfg.customTheme10 }; + const customTheme = customThemes[grSet.theme]; + + col.bg = ({ + white: grSet.styleBlackAndWhite ? RGB(230, 230, 230) : grSet.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGB(245, 245, 245), + black: RGB(25, 25, 25), + reborn: (styleNight || grSet.styleRebornBlack || themeNight) ? RGB(25, 25, 25) : RGB(245, 245, 245), + random: (styleNight || grSet.styleRebornBlack || themeNight) ? RGB(25, 25, 25) : RGB(245, 245, 245), + blue: RGB(5, 110, 195), + darkblue: RGB(22, 40, 63), + red: RGB(100, 20, 20), + cream: RGB(255, 247, 240) + }[grSet.theme] || + (pref_theme_neon ? RGB(20, 20, 20) : + (pref_theme_custom && customTheme.grCol_preloaderBg !== '' ? HEXtoRGB(customTheme.grCol_preloaderBg) : (styleNight || themeNight ? RGB(25, 25, 25) : RGB(245, 245, 245)))) + ); + + col.lowerBarTitle = ({ + white: RGB(120, 120, 120), + black: RGB(200, 200, 200), + reborn: themeNight ? RGB(200, 200, 200) : RGB(120, 120, 120), + random: themeNight ? RGB(200, 200, 200) : RGB(120, 120, 120), + blue: RGB(255, 255, 255), + darkblue: RGB(255, 255, 255), + red: RGB(220, 220, 220), + cream: RGB(100, 100, 100) + }[grSet.theme] || + (pref_theme_neon ? RGB(200, 200, 200) : + (pref_theme_custom && customTheme.grCol_preloaderLowerBarTitle !== '' ? HEXtoRGB(customTheme.grCol_preloaderLowerBarTitle) : (styleNight || themeNight ? RGB(200, 200, 200) : RGB(120, 120, 120)))) + ); + + col.progressBar = ({ + white: grSet.styleBlackAndWhite ? RGB(210, 210, 210) : grSet.styleBlackAndWhite2 ? RGB(40, 40, 40) : RGB(220, 220, 220), + black: RGB(35, 35, 35), + reborn: (styleNight || grSet.styleRebornBlack) ? RGB(50, 50, 50) : themeNight ? RGB(35, 35, 35) : RGB(220, 220, 220), + random: (styleNight || grSet.styleRebornBlack) ? RGB(50, 50, 50) : themeNight ? RGB(35, 35, 35) : RGB(220, 220, 220), + blue: RGB(10, 130, 220), + darkblue: RGB(27, 55, 90), + red: RGB(140, 25, 25), + cream: RGB(255, 255, 255) + }[grSet.theme] || + (pref_theme_neon ? RGB(35, 35, 35) : + (pref_theme_custom && customTheme.grCol_preloaderProgressBar !== '' ? HEXtoRGB(customTheme.grCol_preloaderProgressBar) : (styleNight || themeNight ? RGB(35, 35, 35) : RGB(220, 220, 220)))) + ); + + col.progressBarFill = ({ + white: grSet.styleBlackAndWhite ? RGB(255, 255, 255) : grSet.styleBlackAndWhite2 ? RGB(210, 210, 210) : RGB(25, 160, 240), + black: RGB(175, 205, 225), + reborn: styleNight ? RGB(195, 225, 230) : (grSet.styleRebornBlack || themeNight) ? RGB(255, 255, 255) : RGB(90, 90, 90), + random: styleNight ? RGB(140, 215, 215) : themeNight ? RGB(255, 255, 255) : RGB(70, 70, 70), + blue: RGB(242, 230, 170), + darkblue: RGB(255, 202, 128), + red: RGB(245, 212, 165), + cream: RGB(120, 170, 130), + nblue: RGB(0, 200, 255), + ngreen: RGB(0, 200, 0), + nred: RGB(240, 10, 60), + ngold: RGB(255, 205, 5) + }[grSet.theme] || + (pref_theme_custom && customTheme.grCol_preloaderProgressBarFill !== '' ? HEXtoRGB(customTheme.grCol_preloaderProgressBarFill) : (styleNight || themeNight ? RGB(225, 225, 195) : RGB(50, 25, 70))) + ); + + col.progressBarFrame = ({ + blue: RGB(22, 107, 186), + darkblue: RGB(22, 37, 54), + red: RGB(92, 21, 21), + cream: RGB(230, 230, 230) + }[grSet.theme] || + (pref_theme_custom && customTheme.grCol_preloaderProgressBarFrame !== '' ? HEXtoRGB(customTheme.grCol_preloaderProgressBarFrame) : (styleNight || themeNight ? RGB(25, 25, 25) : RGB(255, 255, 255))) + ); + + col.uiHacksFrame = ({ + white: grSet.styleBlackAndWhite ? RGB(230, 230, 230) : grSet.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGB(245, 245, 245), + black: RGB(35, 35, 35), + reborn: (styleNight || grSet.styleRebornBlack || themeNight) ? RGB(25, 25, 25) : RGB(245, 245, 245), + random: (styleNight || grSet.styleRebornBlack || themeNight) ? RGB(25, 25, 25) : RGB(245, 245, 245), + blue: RGB(63, 155, 202), + darkblue: RGB(27, 55, 90), + red: RGB(125, 0, 0), + cream: RGB(255, 247, 240) + }[grSet.theme] || + (pref_theme_neon ? RGB(30, 30, 30) : + (pref_theme_custom && customTheme.grCol_preloaderUIHacksFrame !== '' ? HEXtoRGB(customTheme.grCol_preloaderUIHacksFrame) : (styleNight || themeNight ? RGB(25, 25, 25) : RGB(245, 245, 245)))) + ); gr.SetSmoothingMode(3); @@ -431,17 +464,17 @@ function on_paint(gr) { gr.DrawLine(ww, wh - 1, 0, wh - 1, 1, col.uiHacksFrame); // * LOGO/TEXT * // - if (pref.showPreloaderLogo) { + if (grSet.showPreloaderLogo) { drawLogo(gr); } else { - gr.DrawString(loadStrs.loading, ft_lower_bar_bold, col.lowerBarTitle, progressBar.x, lowerBarTop, progressBar.w, titleMeasurements.Height); - gr.DrawString(loadStrs.fileName, ft_lower_bar_light, col.lowerBarTitle, progressBar.x + loadingWidth + SCALE(20), lowerBarTop, progressBar.w - loadingWidth - SCALE(20), titleMeasurements.Height); + gr.DrawString(loadStr.loading, ft_lower_bar_bold, col.lowerBarTitle, progressBar.x, lowerBarTop, progressBar.w, titleMeasurements.Height); + gr.DrawString(loadStr.fileName, ft_lower_bar_light, col.lowerBarTitle, progressBar.x + loadingWidth + SCALE(20), lowerBarTop, progressBar.w - loadingWidth - SCALE(20), titleMeasurements.Height); } // * PROGRESS BAR * // gr.FillSolidRect(progressBar.x, progressBar.y, progressBar.w, progressBar.h, col.progressBar); - gr.FillSolidRect(progressBar.x, progressBar.y, progressBar.w * (loadStrs.fileIndex + 1) / fileList.length, progressBar.h, col.progressBarFill); - if ((['blue', 'darkblue', 'red', 'cream'].includes(pref.theme) || pref_theme_custom) && !pref.systemFirstLaunch) { + gr.FillSolidRect(progressBar.x, progressBar.y, progressBar.w * (loadStr.fileIndex + 1) / fileList.length, progressBar.h, col.progressBarFill); + if ((['blue', 'darkblue', 'red', 'cream'].includes(grSet.theme) || pref_theme_custom) && !grSet.systemFirstLaunch) { gr.DrawRect(progressBar.x - 2, progressBar.y - 2, progressBar.w + 3, progressBar.h + 3, 1, col.progressBarFrame); } } @@ -452,65 +485,71 @@ function on_paint(gr) { ///////////////////////// /** * Draws the logo in the preloader. - * @param {GdiGraphics} gr + * @global + * @param {GdiGraphics} gr - The GDI graphics object. */ function drawLogo(gr) { // * SYSTEM * // const ww = window.Width; const wh = window.Height; - const RES_4K = pref.displayRes === '4K' || (ww > 3000 || wh > 1300); - const plus4k = RES_4K ? '4K-' : ''; - const pref_theme_custom = pref.theme && pref.theme.startsWith('custom'); + const RES_4K = grSet.displayRes === '4K' || (ww > 3000 || wh > 1300); + const plus4K = RES_4K ? '4K-' : ''; + const custom = grSet.theme && grSet.theme.startsWith('custom'); // * CUSTOM LOGO * // - const logoNight = pref.styleNighttime ? '-night' : ''; + const logoNight = grSet.styleNighttime ? '-night' : ''; const customLogo = `custom-logo${logoNight}.png`; // * CUSTOM THEME LOGO * // - const customThemes = { custom01: customTheme01, custom02: customTheme02, custom03: customTheme03, custom04: customTheme04, custom05: customTheme05, custom06: customTheme06, custom07: customTheme07, custom08: customTheme08, custom09: customTheme09, custom10: customTheme10 }; - const customTheme = customThemes[pref.theme]; - const customThemeLogoConfig = customTheme && customTheme.preloaderLogo !== ''; - const cThemeLogo = customTheme && customTheme.preloaderLogo; + const customThemes = { custom01: grCfg.customTheme01, custom02: grCfg.customTheme02, custom03: grCfg.customTheme03, custom04: grCfg.customTheme04, custom05: grCfg.customTheme05, custom06: grCfg.customTheme06, custom07: grCfg.customTheme07, custom08: grCfg.customTheme08, custom09: grCfg.customTheme09, custom10: grCfg.customTheme10 }; + const customTheme = customThemes[grSet.theme]; + const customThemeLogoConfig = customTheme && customTheme.grCol_preloaderLogo !== ''; + const cThemeLogo = customTheme && customTheme.grCol_preloaderLogo; // * NIGHTTIME LOGOS * // - const nighttime = (pref.styleNighttime || pref.themeDayNightMode && pref.themeDayNightTime === 'night') && !pref.styleRebornWhite; - const nighttimeReborn = nighttime && pref.theme === 'reborn'; - const nighttimeRandom = nighttime && pref.theme === 'random'; - const nighttimeCustom = nighttime && pref_theme_custom; + const nighttime = (grSet.styleNighttime || grSet.themeDayNightMode && grSet.themeDayNightTime === 'night') && !grSet.styleRebornWhite; + const nighttimeReborn = nighttime && grSet.theme === 'reborn'; + const nighttimeRandom = nighttime && grSet.theme === 'random'; + const nighttimeCustom = nighttime && custom; // * PATHS * // const paths = {}; const logoPath = `${fb.ProfilePath}georgia-reborn\\images\\logo\\`; const logoPathCustom = `${fb.ProfilePath}georgia-reborn\\images\\custom\\logo\\`; - switch (true) { - // Nighttime logos - case nighttimeReborn: paths.logo = `${logoPath}${plus4k}logo-reborn-night.png`; break; - case nighttimeRandom: paths.logo = `${logoPath}${plus4k}logo-random-night.png`; break; - case nighttimeCustom: paths.logo = `${logoPath}${plus4k}logo-custom-night.png`; break; - // Style logos - case pref.styleBlackAndWhite: paths.logo = `${logoPath}${plus4k}logo-black-white.png`; break; - case pref.styleBlackAndWhite2: paths.logo = `${logoPath}${plus4k}logo-black-white2.png`; break; - case pref.styleBlackReborn: paths.logo = `${logoPath}${plus4k}logo-black-reborn.png`; break; - case pref.styleRebornBlack: paths.logo = `${logoPath}${plus4k}logo-reborn-night.png`; break; + const logoName = { // Standard logos - case pref.theme === 'white': paths.logo = `${logoPath}${plus4k}logo-white.png`; break; - case pref.theme === 'black': paths.logo = `${logoPath}${plus4k}logo-black.png`; break; - case pref.theme === 'reborn': paths.logo = `${logoPath}${plus4k}logo-reborn.png`; break; - case pref.theme === 'random': paths.logo = `${logoPath}${plus4k}logo-random.png`; break; - case pref.theme === 'blue': paths.logo = `${logoPath}${plus4k}logo-blue.png`; break; - case pref.theme === 'darkblue': paths.logo = `${logoPath}${plus4k}logo-dark-blue.png`; break; - case pref.theme === 'red': paths.logo = `${logoPath}${plus4k}logo-red.png`; break; - case pref.theme === 'cream': paths.logo = `${logoPath}${plus4k}logo-cream.png`; break; - case pref.theme === 'nblue': paths.logo = `${logoPath}${plus4k}logo-neon-blue.png`; break; - case pref.theme === 'ngreen': paths.logo = `${logoPath}${plus4k}logo-neon-green.png`; break; - case pref.theme === 'nred': paths.logo = `${logoPath}${plus4k}logo-neon-red.png`; break; - case pref.theme === 'ngold': paths.logo = `${logoPath}${plus4k}logo-neon-gold.png`; break; - case pref_theme_custom: paths.logo = `${logoPath}${plus4k}logo-custom.png`; break; + [grSet.theme === 'white']: 'logo-white.png', + [grSet.theme === 'black']: 'logo-black.png', + [grSet.theme === 'reborn']: 'logo-reborn.png', + [grSet.theme === 'random']: 'logo-random.png', + [grSet.theme === 'blue']: 'logo-blue.png', + [grSet.theme === 'darkblue']: 'logo-dark-blue.png', + [grSet.theme === 'red']: 'logo-red.png', + [grSet.theme === 'cream']: 'logo-cream.png', + [grSet.theme === 'nblue']: 'logo-neon-blue.png', + [grSet.theme === 'ngreen']: 'logo-neon-green.png', + [grSet.theme === 'nred']: 'logo-neon-red.png', + [grSet.theme === 'ngold']: 'logo-neon-gold.png', + [custom]: 'logo-custom.png', + // Style logos + [grSet.styleBlackAndWhite]: 'logo-black-white.png', + [grSet.styleBlackAndWhite2]: 'logo-black-white2.png', + [grSet.styleBlackReborn]: 'logo-black-reborn.png', + [grSet.styleRebornBlack]: 'logo-reborn-night.png', + // Nighttime logos + [nighttimeReborn]: 'logo-reborn-night.png', + [nighttimeRandom]: 'logo-random-night.png', + [nighttimeCustom]: 'logo-custom-night.png' + }; + + if (grSet.customPreloaderLogo) { + paths.logo = `${logoPathCustom}_${plus4K}${customLogo}`; + } else if (customThemeLogoConfig) { + paths.logo = `${logoPathCustom}${plus4K}${cThemeLogo}`; + } else { + paths.logo = `${logoPath}${plus4K}${logoName.true}`; } - // Custom logos - if (pref.customPreloaderLogo) paths.logo = `${logoPathCustom}_${plus4k}${customLogo}`; - if (customThemeLogoConfig) paths.logo = `${logoPathCustom}${plus4k}${cThemeLogo}`; // * LOGO * // if (typeof drawLogo.logoErrorShown === 'undefined') { diff --git a/profile/georgia-reborn/scripts/Base/gr-buttons.js b/profile/georgia-reborn/scripts/Base/gr-buttons.js index 97180ab9..ff7eaded 100644 --- a/profile/georgia-reborn/scripts/Base/gr-buttons.js +++ b/profile/georgia-reborn/scripts/Base/gr-buttons.js @@ -1,87 +1,83 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Button Control * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Button Control * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////// -// * VARIABLES * // -/////////////////// -/** @type {Button} */ -let thisButton = null; -/** @type {Button} */ -let oldButton; -/** @type {Button} */ -let downButton; -/** @type {Button} */ -let lastOverButton = null; -/** @type {Button[]} */ -const activatedBtns = []; -/** @type {number} */ -let buttonTimer = null; -/** @type {boolean} */ -let mainMenuOpen = false; -/** @type {boolean} */ -let mouseInControl = false; - -/** @enum {number} */ -const ButtonState = { - Default: 0, - Hovered: 1, - Down: 2, // Happens on click - Enabled: 3 -}; - -/** @enum {number} */ -const WindowState = { - Normal: 0, - Minimized: 1, - Maximized: 2 -}; - - -/////////////////////// -// * BUTTON OBJECT * // -/////////////////////// +///////////////////////// +// * BUTTON CONTROLS * // +///////////////////////// /** - * The Button class represents a clickable element with an image, tooltip and state. + * A class that controls clickable UI button elements with customizable visuals and behavior. + * The button can display an image, show a tooltip on hover, and maintain an enabled/disabled state. */ class Button { /** - * Constructor for a clickable element with an image, tooltip, and state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} id The id. - * @param {GdiBitmap[]} img The image that will be displayed for the button. - * @param {string} tip The tooltip text for the button. - * @param {boolean} isEnabled A callback function that determines whether the button is enabled or disabled. + * Creates the `Button` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} id - The id. + * @param {GdiBitmap[]} img - The image that will be displayed for the button. + * @param {string} tip - The tooltip text for the button. + * @param {boolean} isEnabled - A callback function that determines whether the button is enabled or disabled. */ constructor(x, y, w, h, id, img, tip = undefined, isEnabled = undefined) { + /** @public @type {number} */ this.x = x; + /** @public @type {number} */ this.y = y; + /** @public @type {number} */ this.w = w; + /** @public @type {number} */ this.h = h; + /** @public @type {number} */ this.id = id; + /** @public @type {number} */ this.img = img; + /** @public @type {number} */ this.tooltip = typeof tip !== 'undefined' ? tip : ''; + /** @public @type {number} */ this.state = 0; + /** @public @type {number} */ this.hoverAlpha = 0; + /** @public @type {number} */ this.downAlpha = 0; + /** @public @type {boolean} */ this.isEnabled = isEnabled; + /** @public @type {boolean} */ this.enabled = false; - } + /** @private @type {Button} */ + this.button = null; + /** @private @type {Button} */ + this.oldButton = null; + /** @public @type {Button} */ + this.downButton = null; + /** @private @type {Button} */ + this.lastOverButton = null; + /** @private @type {Button[]} */ + this.activatedBtns = []; + /** @private @type {number} */ + this.buttonTimer = null; + /** @public @type {boolean} */ + this.mainMenuOpen = false; + /** @public @type {boolean} */ + this.mouseInControl = false; + } + + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS /** * Gets the enabled state of a button. * @returns {boolean} True or false. @@ -92,7 +88,7 @@ class Button { /** * Sets the enabled state of a button and changes its state accordingly. - * @param {boolean} val Whether the button should be enabled or disabled. + * @param {boolean} val - Whether the button should be enabled or disabled. */ set enable(val) { this.enabled = val; @@ -102,11 +98,69 @@ class Button { this.changeState(ButtonState.Enabled); } } + // #endregion + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Handles button action events based on menu, panel and button type. + * @param {Button} btn - The instance of the button. + * @private + */ + _actionHandler(btn) { + const buttons = { + // * TOP MENU COMPACT * // + Menu: () => this.topMenu(), + + // * TOP MENU MAIN - DEFAULT FOOBAR2000 * // + File: () => grm.topMenu.topMenuMain(btn.x, btn.y + btn.h, btn.id), + Edit: () => grm.topMenu.topMenuMain(btn.x, btn.y + btn.h, btn.id), + View: () => grm.topMenu.topMenuMain(btn.x, btn.y + btn.h, btn.id), + Playback: () => grm.topMenu.topMenuMain(btn.x, btn.y + btn.h, btn.id), + Library: () => grm.topMenu.topMenuMain(btn.x, btn.y + btn.h, btn.id), + Help: () => grm.topMenu.topMenuMain(btn.x, btn.y + btn.h, btn.id), + Playlists: () => grm.topMenu.topMenuPlaylists(btn.x, btn.y + btn.h), + + // * TOP MENU THEME BUTTONS * // + Options: () => grm.topMenu.topMenuOptions(btn.x, btn.y + btn.h), + Details: () => this.topDetails(), + PlaylistArtworkLayout: () => this.topPlaylistArtwork(), + library: () => this.topLibrary(), + Biography: () => this.topBiography(), + Lyrics: () => this.topLyrics(), + Rating: () => this.topRating(btn.x, btn.y + btn.h), + + // * TOP MENU 🗕 🗖 ✖ CAPTION BUTTONS * // + Minimize: () => fb.RunMainMenuCommand('View/Hide'), + Maximize: () => this.topMaximize(), + Close: () => fb.Exit(), + + // * LOWER BAR TRANSPORT BUTTONS * // + Stop: () => { fb.Stop(); grm.ui.displayPanelControl(); }, + Previous: () => fb.Prev(), + PlayPause: () => { fb.PlayOrPause(); grm.ui.displayPanelControl(); }, + Next: () => fb.Next(), + PlaybackOrder: () => this.lowerPlaybackOrder(), + Reload: () => window.Reload(), + AddTracks: () => this.lowerAddTracks(), + Volume: () => this.lowerVolume(), + Mute: () => fb.VolumeMute(), + PlaybackTime: () => this.lowerPlaybackTime(), + + // * PLAYLIST HISTORY BUTTONS * // + Back: () => pl.history.buttons(btn), + Forward: () => pl.history.buttons(btn) + }; + + const btnAction = buttons[btn.id]; + if (btnAction) btnAction(); + } /** * Controls the alpha values of buttons during different states (hover, down) and repaints them accordingly. + * @private */ - btnAlphaTimer() { + _alphaTimer() { const trace = false; const buttonHoverInStep = 40; const buttonHoverOutStep = 15; @@ -114,39 +168,39 @@ class Button { const buttonDownOutStep = 50; const buttonTimerDelay = 25; - if (!buttonTimer) { - buttonTimer = setInterval(() => { - for (const i in activatedBtns) { - switch (activatedBtns[i].state) { + if (!this.buttonTimer) { + this.buttonTimer = setInterval(() => { + for (const i in this.activatedBtns) { + switch (this.activatedBtns[i].state) { case 0: - activatedBtns[i].hoverAlpha = Math.max(0, activatedBtns[i].hoverAlpha -= buttonHoverOutStep); - activatedBtns[i].downAlpha = Math.max(0, activatedBtns[i].downAlpha -= Math.max(0, buttonDownOutStep)); - activatedBtns[i].repaint(); + this.activatedBtns[i].hoverAlpha = Math.max(0, this.activatedBtns[i].hoverAlpha -= buttonHoverOutStep); + this.activatedBtns[i].downAlpha = Math.max(0, this.activatedBtns[i].downAlpha -= Math.max(0, buttonDownOutStep)); + this.activatedBtns[i].repaint(); break; case 1: - activatedBtns[i].hoverAlpha = Math.min(255, activatedBtns[i].hoverAlpha += buttonHoverInStep); - activatedBtns[i].downAlpha = Math.max(0, activatedBtns[i].downAlpha -= buttonDownOutStep); - activatedBtns[i].repaint(); + this.activatedBtns[i].hoverAlpha = Math.min(255, this.activatedBtns[i].hoverAlpha += buttonHoverInStep); + this.activatedBtns[i].downAlpha = Math.max(0, this.activatedBtns[i].downAlpha -= buttonDownOutStep); + this.activatedBtns[i].repaint(); break; case 2: - activatedBtns[i].downAlpha = Math.min(255, activatedBtns[i].downAlpha += buttonDownInStep); - activatedBtns[i].hoverAlpha = Math.max(0, activatedBtns[i].hoverAlpha -= buttonDownInStep); - activatedBtns[i].repaint(); + this.activatedBtns[i].downAlpha = Math.min(255, this.activatedBtns[i].downAlpha += buttonDownInStep); + this.activatedBtns[i].hoverAlpha = Math.max(0, this.activatedBtns[i].hoverAlpha -= buttonDownInStep); + this.activatedBtns[i].repaint(); break; } } // Test button alpha values and turn button timer off when it's not required; - for (let i = activatedBtns.length - 1; i >= 0; i--) { - if ((!activatedBtns[i].hoverAlpha && !activatedBtns[i].downAlpha) || - activatedBtns[i].hoverAlpha === 255 || activatedBtns[i].downAlpha === 255) { - activatedBtns.splice(i, 1); + for (let i = this.activatedBtns.length - 1; i >= 0; i--) { + if ((!this.activatedBtns[i].hoverAlpha && !this.activatedBtns[i].downAlpha) || + this.activatedBtns[i].hoverAlpha === 255 || this.activatedBtns[i].downAlpha === 255) { + this.activatedBtns.splice(i, 1); } } - if (!activatedBtns.length) { - clearInterval(buttonTimer); - buttonTimer = null; + if (!this.activatedBtns.length) { + clearInterval(this.buttonTimer); + this.buttonTimer = null; trace && console.log('buttonTimerStarted = false'); } }, buttonTimerDelay); @@ -154,938 +208,636 @@ class Button { trace && console.log('buttonTimerStarted = true'); } } + // #endregion + // * PUBLIC METHODS - TOP MENU BUTTONS * // + // #region PUBLIC METHODS - TOP MENU BUTTONS /** - * Updates the state of the button. - * @param {number} state The new state to set for the button. + * Collapses the top menu to compact mode or expands it to normal. + * @param {boolean} collapse - Wether the top menu should be collapsed or not. */ - changeState(state) { - this.state = state; - activatedBtns.push(this); - this.btnAlphaTimer(); + topMenu(collapse) { + grSet.showTopMenuCompact = !grSet.showTopMenuCompact; + if (collapse) { + grSet.showTopMenuCompact = true; + } + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + this.initButtonState(); + RepaintWindow(); } /** - * Passes in the current button as an argument via the btnActionHandler. + * Handles the Details panel button action in the top menu. */ - onClick() { - buttonActionHandler(this); - } + topDetails() { + grm.ui.displayPlaylist = !grm.ui.displayPlaylist; + grm.ui.displayDetails = grSet.layout === 'artwork' ? grm.ui.displayPlaylist : !grm.ui.displayPlaylist; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = grSet.lyricsLayout === 'full' || grSet.lyricsPanelState; + + if (grm.ui.displayPlaylist) { + if (grSet.layout === 'artwork') { + if (grSet.lyricsPanelState) grm.ui.displayLyrics = false; + grm.ui.displayPlaylistArtwork = false; + pl.playlist.x = grm.ui.ww; // Move hidden Playlist offscreen to disable Playlist mouse functions in Details + grm.ui.resizeArtwork(true); + } else { + pl.call.on_size(grm.ui.ww, grm.ui.wh); + } + } - /** - * Currently does not have any functionality. - */ - onDblClick() { - // We don't do anything with dblClick currently - } + if (grm.ui.displayLibrary) { + grm.ui.displayLibrary = false; + if (grSet.layout === 'default') { + grm.ui.displayPlaylist = grSet.libraryLayout === 'split' ? false : !grm.ui.displayPlaylist; // * Library Playlist split layout + grm.ui.displayDetails = grSet.layout === 'artwork' ? grm.ui.displayPlaylist : !grm.ui.displayPlaylist; + } + } - /** - * Checks if the mouse is within the boundaries of a button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - mouseInThis(x, y) { - return (this.x <= x) && (x <= this.x + this.w) && (this.y <= y) && (y <= this.y + this.h); - } + if (grSet.lyricsLayout === 'full' && grm.ui.displayLyrics) { + if (grSet.lyricsPanelState) grSet.lyricsLayout = 'normal'; + if (!grSet.lyricsPanelState) grm.ui.displayLyrics = false; + if (grSet.layout === 'default' && grm.ui.displayPlaylist) grm.ui.displayPlaylist = !grm.ui.displayPlaylist; + if (!grm.ui.displayPlaylist) grm.ui.displayDetails = true; + } else { + grm.ui.restoreLyricsLayout(); + } - /** - * Repaints the button to update its state. - */ - repaint() { - window.RepaintRect(this.x, this.y, this.w, this.h); + grm.ui.resizeArtwork(false); + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + if (grm.ui.displayCustomThemeMenu) { + grm.ui.displayPanel(grm.ui.displayPlaylist ? 'playlist' : 'details'); + grm.cthMenu.reinitCustomThemeMenu(); + } + grm.ui.setDiscArtRotationTimer(); + this.initButtonState(); + window.Repaint(); } -} - -////////////////////////////// -// * BUTTON EVENT HANDLER * // -////////////////////////////// -/** - * Handles various mouse button events. - */ -class ButtonEventHandler { /** - * Handles mouse double click events on a button and changes its state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * Handles the Playlist panel button action for artwork layout in the top menu. */ - on_mouse_lbtn_dblclk(x, y, m) { - if (!thisButton) return; - thisButton.changeState(ButtonState.Down); - downButton = thisButton; - downButton.onDblClick(); - } + topPlaylistArtwork() { + grm.ui.displayPlaylistArtwork = !grm.ui.displayPlaylistArtwork; + grm.ui.displayPlaylist = false; + grm.ui.displayDetails = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = grSet.lyricsPanelState && !grm.ui.displayPlaylistArtwork; - /** - * Handles left mouse click down events on a button and changes its state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - */ - on_mouse_lbtn_down(x, y, m) { - if (!thisButton) return; - thisButton.changeState(ButtonState.Down); - downButton = thisButton; + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.resizeArtwork(false); + this.initButtonState(); + window.Repaint(); } /** - * Handles left mouse click up events on a button and changes its state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * Handles the Library panel button action in the top menu. */ - on_mouse_lbtn_up(x, y, m) { - if (!downButton) return; - - downButton.onClick(); + topLibrary() { + grm.ui.displayLibrary = !grm.ui.displayLibrary; + grm.ui.displayDetails = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = grSet.lyricsPanelState; + + if (grm.ui.displayCustomThemeMenu) grm.cthMenu.reinitCustomThemeMenu(); + + if (grm.ui.displayLibrary) { + if (grSet.layout === 'default') { + grm.ui.displayPlaylist = true; + grm.ui.displayLyrics = grSet.libraryLayout === 'full' ? false : grSet.lyricsPanelState; + } + else if (grSet.layout === 'artwork') { + grm.ui.displayPlaylistArtwork = false; + grm.ui.displayLyrics = false; + grm.ui.resizeArtwork(true); + } + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + window.Repaint(); + } + } - if (mainMenuOpen) { - thisButton = undefined; - mainMenuOpen = false; + if (grm.ui.displayPlaylist) { + grm.ui.displayPlaylist = false; + } else if (grSet.layout === 'default') { + grm.ui.displayPlaylist = true; + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.restoreLyricsLayout(); } - if (thisButton) { - thisButton.changeState(ButtonState.Hovered); + // * Library Playlist split layout + if (grm.ui.displayLibrarySplit()) { + grm.ui.displayPlaylist = true; + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.setLibrarySize(); + } else if (grSet.layout === 'default' && grSet.libraryLayout === 'split') { + grm.ui.displayPlaylist = true; + grm.ui.initLibraryLayout(); } - else if (downButton && downButton === thisButton) { - downButton.changeState(ButtonState.Default); + + if (grSet.layout === 'default' && grSet.libraryLayout !== 'split' && + grSet.libraryLayoutSplitPreset || grSet.libraryLayoutSplitPreset2 || + grSet.libraryLayoutSplitPreset3 || grSet.libraryLayoutSplitPreset4) { + plSet.auto_collapse = false; + pl.playlist.header_expand(); } - thisButton = downButton; + // The Library's on_playback_new_track in gr-callbacks.js is only called when Library panel is active to improve performance. + // Therefore, we need to call it now and update the Library's nowPlaying state when a new song is played from the active Playlist panel. + lib.call.on_playback_new_track(); + grm.ui.resizeArtwork(false); + this.initButtonState(); + window.Repaint(); } /** - * Handles mouse leave events on a button and changes its state to default. + * Handles the Biography panel button action in the top menu. */ - on_mouse_leave() { - oldButton = undefined; + topBiography() { + grm.ui.displayPlaylist = grSet.layout === 'default'; + grm.ui.displayDetails = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = !grm.ui.displayBiography; + grm.ui.displayLyrics = false; + + if (grm.ui.displayCustomThemeMenu) grm.cthMenu.reinitCustomThemeMenu(); + + // Switch playlist to normal width to prevent panel overlaying + grSet.playlistLayoutNormal = grSet.playlistLayout === 'full' && grm.ui.displayBiography; - if (downButton) return; + if (!grm.ui.displayBiography && grSet.lyricsPanelState) { + grm.ui.displayLyrics = true; + grm.ui.restoreLyricsLayout(); + // Switch playlist to normal width to prevent panel overlaying + grSet.playlistLayoutNormal = grSet.playlistLayout === 'full' && grm.ui.displayLyrics; + } - for (const i in btns) { - if (btns[i].state !== 0) { - btns[i].changeState(ButtonState.Default); + if (grm.ui.displayBiography && (grSet.biographyLayout === 'full' || grSet.layout === 'artwork')) { + grSet.layout === 'artwork' ? grm.ui.displayPlaylistArtwork = false : grm.ui.displayPlaylist = false; + } else { + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); } + pl.call.on_size(grm.ui.ww, grm.ui.wh); } + + // The Biography's on_playback_new_track in gr-callbacks.js is only called when Biography panel is active to improve performance. + // Therefore, we need to call it now and update the Biography's nowPlaying state when a new song is played from the active Playlist panel. + bio.call.on_playback_new_track(); + grm.ui.resizeArtwork(false); + this.initButtonState(); + window.Repaint(); } /** - * Handles mouse move tracking events on a button and changes its state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * Handles the Lyrics panel button action in the top menu. */ - on_mouse_move(x, y, m) { - oldButton = thisButton; + topLyrics() { + grm.ui.displayPlaylist = grSet.layout === 'default'; + grm.ui.displayPlaylistArtwork = false; + grm.ui.displayDetails = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = !grm.ui.displayLyrics; - for (const i in btns) { - if (typeof btns[i] === 'object' && btns[i].mouseInThis(x, y)) { - mouseInControl = true; - thisButton = btns[i]; - break; - } else { - mouseInControl = false; - thisButton = null; - } - } + if (grm.ui.displayCustomThemeMenu) grm.cthMenu.reinitCustomThemeMenu(); - if (oldButton && oldButton !== thisButton) { - oldButton.changeState(oldButton.enabled ? ButtonState.Enabled : ButtonState.Default); - } - if (thisButton && thisButton !== oldButton) { - thisButton.changeState(ButtonState.Hovered); - } - downButton = thisButton; + // Switch playlist to normal width to prevent panel overlaying + grSet.playlistLayoutNormal = grSet.playlistLayout === 'full' && grm.ui.displayLyrics; + // Save lyric active state + grSet.lyricsPanelState = grm.ui.displayLyrics && grSet.lyricsRememberPanelState; - if (lastOverButton !== thisButton) { - tt.stop(); + if (grSet.lyricsLayout === 'full' && grm.ui.displayLyrics) { + grm.ui.displayPlaylist = false; + grm.ui.resizeArtwork(true); + } else { + pl.call.on_size(grm.ui.ww, grm.ui.wh); } - lastOverButton = thisButton; - if (pref.showTooltipMain && lastOverButton) { - if (lastOverButton.tooltip) { - tt.showDelayed(lastOverButton.tooltip); - } - else if (lastOverButton.id === 'Volume' && !volumeBtn.show_volume_bar) { - tt.showDelayed(btnTransportTooltip('volume')); - } - else if (lastOverButton.id === 'PlaybackOrder') { - tt.showDelayed(btnTransportTooltip('pbo')); - } + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); } - } -} - -/////////////////////////////// -// * BUTTON ACTION HANDLER * // -/////////////////////////////// -/** - * Handles button action events based on menu, panel and button type. - * @param {Button} btn The instance of the button. - */ -function buttonActionHandler(btn) { - switch (btn.id) { - // * TOP MENU COMPACT * // - case 'Menu': topMenuCompact(); break; - - // * TOP MENU MAIN - DEFAULT FOOBAR2000 * // - case 'File': - case 'Edit': - case 'View': - case 'Playback': - case 'Library': - case 'Help': - topMenuMain(btn.x, btn.y + btn.h, btn.id); - break; - case 'Playlists': topMenuPlaylists(btn.x, btn.y + btn.h); break; - - // * TOP MENU THEME BUTTONS * // - case 'Options': topMenuOptions(btn.x, btn.y + btn.h); break; - case 'Details': btnDetails(); break; - case 'PlaylistArtworkLayout': btnPlaylistArtwork(); break; - case 'library': btnLibrary(); break; - case 'Biography': btnBiography(); break; - case 'Lyrics': btnLyrics(); break; - case 'Rating': topMenuRating(btn.x, btn.y + btn.h); break; - - // * TOP MENU 🗕 🗖 ✖ CAPTION BUTTONS * // - case 'Minimize': fb.RunMainMenuCommand('View/Hide'); break; - case 'Maximize': btnMaximize(); break; - case 'Close': fb.Exit(); break; - - // * LOWER BAR TRANSPORT BUTTONS * // - case 'Stop': fb.Stop(); btnStop(); break; - case 'Previous': fb.Prev(); break; - case 'PlayPause': fb.PlayOrPause(); break; - case 'Next': fb.Next(); break; - case 'PlaybackOrder': btnPlaybackOrder(); break; - case 'Reload': window.Reload(); break; - case 'Volume': btnVolume(); break; - case 'Mute': fb.VolumeMute(); break; - case 'PlaybackTime': btnPlaybackTime(); break; - - // * PLAYLIST HISTORY BUTTONS * // - case 'Back': case 'Forward': btnPlaylistHistory(btn); break; + grm.lyrics.initLyrics(); + grm.ui.resizeArtwork(grSet.layout === 'artwork'); + this.initButtonState(); + window.Repaint(); } -} + /** + * Handles the Rating button action in the top menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + topRating(x, y) { + const handle = new FbMetadbHandleList(); + const metadb = fb.GetFocusItem(); + if (!metadb) { + fb.ShowPopupMessage('No track selected, it seems like the playlist is empty.', 'Empty playlist'); + return; + } + const fileInfo = metadb.GetFileInfo(); + const ratingMetaIdx = fileInfo.MetaFind('RATING'); + const ratingMeta = ratingMetaIdx === -1 ? 0 : fileInfo.MetaValue(ratingMetaIdx, 0); + const ratingTags = plSet.use_rating_from_tags; + const rating = ratingTags ? ratingMeta : $('$if2(%rating%,0)', metadb); + const selectedItems = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); + const menu = new Menu(); + grm.ui.activeMenu = true; + + menu.addRadioItems(['No rating', '1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'], parseInt(rating), [0, 1, 2, 3, 4, 5], (rating) => { + pl.album_ratings.clear(); + + for (let i = 0; i < selectedItems.Count; i++) { + const metadb = selectedItems[i]; + const noStream = !metadb.RawPath.startsWith('http'); + + if (rating === 0) { + if (ratingTags && noStream) { + handle.Add(metadb); + handle.UpdateFileInfoFromJSON(JSON.stringify({ RATING: '' })); + } else { + fb.RunContextCommandWithMetadb('Playback Statistics/Rating/', metadb); + } + } + else if (ratingTags && noStream) { + handle.Add(metadb); + handle.UpdateFileInfoFromJSON(JSON.stringify({ RATING: rating })); + } + else { + fb.RunContextCommandWithMetadb(`Playback Statistics/Rating/${rating}`, metadb); + } -////////////////////////// -// * TOP MENU COMPACT * // -////////////////////////// -/** - * Collapses the top menu to compact mode or expands it to normal. - * @param {boolean} collapse Wether the top menu should be collapsed or not. - */ -function topMenuCompact(collapse) { - pref.showTopMenuCompact = !pref.showTopMenuCompact; - if (collapse) { - if (topMenuCompactExpanded) return; - pref.showTopMenuCompact = true; - } - createButtonImages(); - createButtonObjects(ww, wh); - initButtonState(); - RepaintWindow(); -} + const trackId = $('%rating%', metadb); + pl.track_ratings.set(trackId, rating); + } + }); + const idx = menu.trackPopupMenu(x, y); + menu.doCallback(idx); + grm.ui.activeMenu = false; + } -//////////////////////////// -// * TOP MENU MAIN MENU * // -//////////////////////////// -/** - * Opens the main menu and handles different menu options based on the provided name. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} name The name of the menu. - */ -function topMenuMain(x, y, name) { - mainMenuOpen = true; - activeMenu = true; - - if (name) { - const menu = new Menu(name); - - if (name === 'Help') { - const themeMenu = new Menu('Theme'); - - const statusMenu = new Menu('Status'); - statusMenu.addItem('All fonts installed', fontsInstalled, undefined, true); - statusMenu.addItem('Artist logos found', IsFile(`${paths.artistlogos}Metallica.png`), undefined, true); - statusMenu.addItem('Record label logos found', IsFile(`${paths.labelsBase}Republic.png`), undefined, true); - statusMenu.addItem('Flag images found', IsFile(`${paths.flagsBase + (RES_4K ? '64\\' : '32\\')}United-States.png`), undefined, true); - statusMenu.addItem('foo_enhanced_playcount installed', componentEnhancedPlaycount, () => { RunCmd('https://www.foobar2000.org/components/view/foo_enhanced_playcount'); }); - statusMenu.appendTo(themeMenu); - - const updatesMenu = new Menu('Updates'); - updatesMenu.addToggleItem('Auto-check for theme updates', pref, 'checkForUpdates', () => { scheduleUpdateCheck(1000); }); - updatesMenu.addItem('Check for latest theme update', false, () => { checkForUpdates(true); }); - updatesMenu.appendTo(themeMenu); - - themeMenu.addItem('Releases', false, () => { RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/releases'); }); - themeMenu.addItem('Changelog', false, () => { RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/blob/master/profile/georgia-reborn/docs/CHANGELOG.md'); }); - themeMenu.addItem('Bug tracker', false, () => { RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/issues'); }); - themeMenu.appendTo(menu); + /** + * Handles the maximize button action in the top menu, player goes into fullscreen and resumes player size. + */ + topMaximize() { + if (grSet.maximizeToFullscreen) { // F11 shortcut ( on_key_down() ) for going into/out fullscreen mode, disabled/not supported in Artwork layout, ESC also exits fullscreen mode + UIHacks.FullScreen = !UIHacks.FullScreen; + } else { + UIHacks.MainWindowState = UIHacks.MainWindowState === WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; } - menu.initFoobarMenu(name); - - const ret = menu.trackPopupMenu(x, y); - menu.doCallback(ret); } + // #endregion - activeMenu = false; -} + // * PUBLIC METHODS - LOWER BAR BUTTONS * // + // #region PUBLIC METHODS - LOWER BAR BUTTONS + /** + * Handles the play button action in the lower bar, toggles between playback play or pause state. + */ + lowerPlayPause() { + const showTransportControls = grSet[`showTransportControls_${grSet.layout}`]; + if (!showTransportControls) return; + grm.ui.btn.play.img = !fb.IsPlaying || fb.IsPaused ? grm.ui.btnImg.Play : grm.ui.btnImg.Pause; + grm.ui.btn.play.repaint(); + } + /** + * Handles the playback order button action in the lower bar, toggles the current playback order. + */ + lowerPlaybackOrder() { + const showTransportControls = grSet[`showTransportControls_${grSet.layout}`]; + if (!showTransportControls) return; -//////////////////////////// -// * TOP MENU PLAYLISTS * // -//////////////////////////// -/** - * Creates a context menu for playlists allowing users to perform various actions such as - * creating new playlists, saving and loading playlists, locking and unlocking playlists, - * and creating auto playlists based on different criteria. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ -function topMenuPlaylists(x, y) { - mainMenuOpen = true; - activeMenu = true; - - const playlist_count = plman.PlaylistCount; - const playlistId = 21; - const cpm = window.CreatePopupMenu(); - const pltools = window.CreatePopupMenu(); - const autopl = window.CreatePopupMenu(); - const isAutoPl = !plman.PlaylistCount ? '' : plman.IsAutoPlaylist(plman.ActivePlaylist); - const isLocked = !plman.PlaylistCount ? '' : plman.IsPlaylistLocked(plman.ActivePlaylist); - - pltools.AppendTo(cpm, MF_STRING, 'Playlist tools'); - pltools.AppendMenuItem(MF_STRING, 1, 'Playlist manager \tCtrl+M'); - pltools.AppendMenuItem(MF_STRING, 2, 'Playlist search \tCtrl+F'); - pltools.AppendMenuSeparator(); - pltools.AppendMenuItem(MF_STRING, 3, 'Create new playlist \tCtrl+N'); - autopl.AppendTo(pltools, MF_STRING, 'Create new auto playlist'); - autopl.AppendMenuItem(MF_STRING, 4, 'Custom auto playlist'); - autopl.AppendMenuSeparator(); - autopl.AppendMenuItem(MF_STRING, 5, 'Tracks from the library'); - autopl.AppendMenuSeparator(); - autopl.AppendMenuItem(MF_STRING, 6, 'Tracks most played'); - autopl.AppendMenuItem(MF_STRING, 7, 'Tracks never played'); - autopl.AppendMenuItem(MF_STRING, 8, 'Tracks played in the last week'); - autopl.AppendMenuItem(MF_STRING, 9, 'Tracks played in the last month'); - autopl.AppendMenuItem(MF_STRING, 10, 'Tracks played in the last year'); - autopl.AppendMenuSeparator(); - autopl.AppendMenuItem(MF_STRING, 11, 'Tracks unrated'); - autopl.AppendMenuItem(MF_STRING, 12, 'Tracks rated 1 star'); - autopl.AppendMenuItem(MF_STRING, 13, 'Tracks rated 2 stars'); - autopl.AppendMenuItem(MF_STRING, 14, 'Tracks rated 3 stars'); - autopl.AppendMenuItem(MF_STRING, 15, 'Tracks rated 4 stars'); - autopl.AppendMenuItem(MF_STRING, 16, 'Tracks rated 5 stars'); - autopl.AppendMenuSeparator(); - autopl.AppendMenuItem(MF_STRING, 17, 'Loved tracks'); - pltools.AppendMenuSeparator(); - pltools.AppendMenuItem(MF_STRING, 18, 'Save playlist \tCtrl+S'); - pltools.AppendMenuItem(MF_STRING, 19, 'Load playlist'); - pltools.AppendMenuItem(isAutoPl ? MF_DISABLED : MF_STRING, 20, isLocked ? isAutoPl ? 'Unlock playlist (N/A for auto playlists)' : 'Unlock playlist' : 'Lock playlist'); - cpm.AppendMenuSeparator(); - for (let i = 0; i !== playlist_count; i++) { - cpm.AppendMenuItem(MF_STRING, playlistId + i, `${plman.GetPlaylistName(i).replace(/&/g, '&&')} [${plman.PlaylistItemCount(i)}]${plman.IsAutoPlaylist(i) ? ' (Auto)' : ''}${i === plman.PlayingPlaylist ? ' (Now Playing)' : ''}`); - } + switch (plman.PlaybackOrder) { + case PlaybackOrder.Default: + this.setPlaybackOrder(grm.ui.btnImg.PlaybackRepeatPlaylist, 'repeatPlaylist', PlaybackOrder.RepeatPlaylist, 'Repeat (playlist)'); + break; - const id = cpm.TrackPopupMenu(x, y); - const playlist_idx = id - playlistId; - - switch (id) { - case 1: - fb.RunMainMenuCommand('View/Playlist Manager'); - break; - case 2: - fb.RunMainMenuCommand('View/Playlist search'); - break; - case 3: - plman.CreatePlaylist(playlist_count, ''); - plman.ActivePlaylist = playlist_count; - break; - case 4: - plman.CreateAutoPlaylist(playlist_count, '(Auto) New custom auto playlist', '', '', 0); - plman.ActivePlaylist = playlist_count; - plman.ShowAutoPlaylistUI(playlist_count); - break; - case 5: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks from the library', 'ALL', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 6: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks most played', '%play_count% GREATER 9', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 7: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks never played', '%play_count% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 8: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks played in the last week', '%last_played% DURING LAST 1 WEEK', '%last_played%', 0); - plman.ActivePlaylist = playlist_count; - break; - case 9: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks played in the last month', '%last_played% DURING LAST 4 WEEKS', '%last_played%', 0); - plman.ActivePlaylist = playlist_count; - break; - case 10: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks played in the last year', '%last_played% DURING LAST 52 WEEKS', '%last_played%', 0); - plman.ActivePlaylist = playlist_count; - break; - case 11: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks unrated', '%rating% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 12: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 1', '%rating% IS 1', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 13: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 2', '%rating% IS 2', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 14: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 3', '%rating% IS 3', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 15: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 4', '%rating% IS 4', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 16: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 5', '%rating% IS 5', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 17: - plman.CreateAutoPlaylist(playlist_count, '(Auto) Loved tracks', '%mood% GREATER 0', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - break; - case 18: - fb.RunMainMenuCommand('File/Save playlist...'); - break; - case 19: - fb.RunMainMenuCommand('File/Load playlist...'); - break; - case 20: - if (plman.GetPlaylistLockName(plman.ActivePlaylist) && !isAutoPl) { - plman.SetPlaylistLockedActions(plman.ActivePlaylist, null); - } else if (!isAutoPl) { - plman.SetPlaylistLockedActions(plman.ActivePlaylist, ['ExecuteDefaultAction']); - } - break; - } + case PlaybackOrder.RepeatPlaylist: + this.setPlaybackOrder(grm.ui.btnImg.PlaybackRepeatTrack, 'repeatTrack', PlaybackOrder.RepeatTrack, 'Repeat (track)'); + break; + case PlaybackOrder.RepeatTrack: + this.setPlaybackOrder(grm.ui.btnImg.PlaybackShuffle, 'shuffle', PlaybackOrder.ShuffleTracks, 'Shuffle (tracks)'); + break; - if (playlist_idx < playlist_count && playlist_idx >= 0) { - plman.ActivePlaylist = playlist_idx; - } + case PlaybackOrder.Random: + case PlaybackOrder.ShuffleTracks: + case PlaybackOrder.ShuffleAlbums: + case PlaybackOrder.ShuffleFolders: + this.setPlaybackOrder(grm.ui.btnImg.PlaybackDefault, 'default', PlaybackOrder.Default, 'Default'); + break; + } - for (let i = 0; i !== playlist_count; i++) { - if (id === (playlistId + i)) plman.ActivePlaylist = i; // Playlist switch + grm.ui.btn.playbackOrder.repaint(); } - activeMenu = false; - return true; -} + /** + * Handles the AddTracks button action in the lower bar. + */ + lowerAddTracks() { + const addTracks = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); + const addTrackPl = plman.FindOrCreatePlaylist(`${grCfg.themeControls.addTracksPlaylist}`, true); + if (grm.ui.displayPlaylist) { + plman.SetPlaylistSelection(pl.playlist.cur_playlist_idx, pl.playlist.selection_handler.selected_indexes, true); + plman.InsertPlaylistItems(addTrackPl, plman.PlaylistItemCount(addTrackPl) || 0, addTracks); + } + else if (grm.ui.displayLibrary) { + plman.ActivePlaylist = addTrackPl; + lib.pop.load(lib.pop.sel_items, true, true, false, false, false); + lib.lib.treeState(false, libSet.rememberTree); + if (grSet.addTracksPlaylistSwitch) { + grm.ui.btn.library.enabled = false; + grm.ui.btn.library.changeState(ButtonState.Default); + grm.ui.displayLibrary = false; + grm.ui.displayPlaylist = true; + if (!grSet.playlistAutoScrollNowPlaying) pl.call.on_size(grm.ui.ww, grm.ui.wh); + } + } -////////////////////////// -// * TOP MENU BUTTONS * // -////////////////////////// -/** - * Handles the Details panel button action in the top menu. - */ -function btnDetails() { - displayPlaylist = !displayPlaylist; - displayDetails = pref.layout === 'artwork' ? displayPlaylist : !displayPlaylist; - displayBiography = false; - if (pref.lyricsLayout !== 'full') pref.displayLyrics = false; - if (pref.lyricsActiveState) { pref.displayLyrics = true; initLyrics(); } - - if (displayPlaylist) { - if (pref.layout === 'artwork') { - if (pref.lyricsActiveState) pref.displayLyrics = false; - displayPlaylistArtwork = false; - playlist.x = ww; // Move hidden Playlist offscreen to disable Playlist mouse functions in Details - resizeArtwork(true); - } else { - playlist.on_size(ww, wh); + if (grSet.addTracksPlaylistSwitch) { + plman.ActivePlaylist = addTrackPl; + setTimeout(() => { + if (pl.playlist.is_scrollbar_available) { + pl.playlist.scrollbar.scroll_to_end(); + } + }, 500); } + window.Repaint(); } - if (displayLibrary) { - displayLibrary = false; - if (pref.layout === 'default') { - displayPlaylist = pref.libraryLayout === 'split' ? false : !displayPlaylist; // * Library Playlist split layout - displayDetails = pref.layout === 'artwork' ? displayPlaylist : !displayPlaylist; + /** + * Handles the volume button action in the lower bar, toggles the volume bar. + */ + lowerVolume() { + if (grSet.autoHideVolumeBar) { + grm.volBtn.toggleVolumeBar(); + } else { + fb.VolumeMute(); } } - if (pref.lyricsLayout === 'full' && pref.displayLyrics) { - if (pref.lyricsActiveState) pref.lyricsLayout = 'normal'; - if (!pref.lyricsActiveState) pref.displayLyrics = false; - if (pref.layout === 'default' && displayPlaylist) displayPlaylist = !displayPlaylist; - } else { - restoreLyricsLayout(); + /** + * Handles the playback time button action in the lower bar, toggles the playback time to remaining or normal. + */ + lowerPlaybackTime() { + grSet.switchPlaybackTime = !grSet.switchPlaybackTime; + on_playback_time(); } - resizeArtwork(false); - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - if (displayCustomThemeMenu) { - if (customThemeMenuCall) displayPanel('details'); - reinitCustomThemeMenu(); - customThemeMenuCall = false; + /** + * Handles the lower bar playback transport button tooltips. + * @param {string} btn - The playback transport button. + * @returns {string} The tooltip text for the given button. + */ + lowerTransportTooltip(btn) { + const pbModeTooltipText = + (fb.StopAfterCurrent ? 'Stop after current\n' : '') + + (fb.PlaybackFollowCursor ? 'Playback follows cursor\n' : '') + + (fb.CursorFollowPlayback ? 'Cursor follows playback\n' : ''); + + const pboTooltipText = + plman.PlaybackOrder === PlaybackOrder.Default ? 'Default' : + plman.PlaybackOrder === PlaybackOrder.RepeatPlaylist ? 'Repeat (playlist)' : + plman.PlaybackOrder === PlaybackOrder.RepeatTrack ? 'Repeat (track)' : + plman.PlaybackOrder === PlaybackOrder.ShuffleTracks ? 'Shuffle (tracks)' : ''; + + const volumeTooltipText = `${fb.Volume.toFixed(2)} dB`; + + switch (btn) { + case 'stop': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nStop` : 'Stop'; + case 'prev': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nPrevious` : 'Previous'; + case 'play': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nPlay` : 'Play'; + case 'next': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nNext` : 'Next'; + case 'pbo': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nPlayback order: ${pboTooltipText}` : `Playback order: ${pboTooltipText}`; + case 'reload': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nReload` : 'Reload'; + case 'addTracks': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nAdd tracks to playlist` : 'Add tracks to playlist'; + case 'volume': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\n${volumeTooltipText}` : volumeTooltipText; + } } - setDiscArtRotationTimer(); - initButtonState(); - window.Repaint(); -} - + // #endregion -/** - * Handles the Playlist panel button action for artwork layout in the top menu. - */ -function btnPlaylistArtwork() { - displayPlaylistArtwork = !displayPlaylistArtwork; - displayPlaylist = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = pref.lyricsActiveState && !displayPlaylistArtwork; - - playlist.on_size(ww, wh); - resizeArtwork(false); - initButtonState(); - window.Repaint(); -} - - -/** - * Handles the Library panel button action in the top menu. - */ -function btnLibrary() { - displayLibrary = !displayLibrary; - displayBiography = false; - pref.displayLyrics = pref.lyricsActiveState; - - if (displayCustomThemeMenu) reinitCustomThemeMenu(); + // * PUBLIC METHODS - GENERAL * // + // #region PUBLIC METHODS - GENERAL + /** + * Initializes the top menu button state. + */ + initButtonState() { + const buttons = [grm.ui.btn.details, grm.ui.btn.library, grm.ui.btn.biography, grm.ui.btn.lyrics, grm.ui.btn.playlistArtworkLayout]; + for (const button of buttons) this.setButtonState(false, button); - if (displayLibrary) { - if (pref.layout === 'default') { - displayPlaylist = true; - pref.displayLyrics = pref.libraryLayout === 'full' ? false : pref.lyricsActiveState; + if (grSet.layout === 'default' && !grm.ui.displayPlaylist && !grm.ui.displayLibrary && !grm.ui.displayBiography && (!grm.ui.displayLyrics || grm.ui.displayLyrics && grSet.lyricsLayout === 'normal')) { + this.setButtonState(grm.ui.btn.details); } - else if (pref.layout === 'artwork') { - displayPlaylistArtwork = false; - pref.displayLyrics = false; - resizeArtwork(true); + else if (grSet.layout === 'artwork' && (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) && !grm.ui.displayLibrary && !grm.ui.displayBiography && !grm.ui.displayLyrics) { + if (grm.ui.displayPlaylist) { + this.setButtonState(grm.ui.btn.details); + } else if (grm.ui.displayPlaylistArtwork) { + grm.ui.displayPlaylist = false; + this.setButtonState(grm.ui.btn.playlistArtworkLayout); + } } - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - window.Repaint(); + else if (grm.ui.displayLibrary && (!grm.ui.displayPlaylist || grm.ui.displayLibrarySplit())) { + this.setButtonState(grm.ui.btn.library); + } + else if (grm.ui.displayBiography) { + this.setButtonState(grm.ui.btn.biography); + } + if (grm.ui.displayLyrics) { + this.setButtonState(grm.ui.btn.lyrics); } } - if (displayPlaylist) { - displayPlaylist = false; - } else if (pref.layout === 'default') { - displayPlaylist = true; - playlist.on_size(ww, wh); - restoreLyricsLayout(); - } - - // * Library Playlist split layout - if (displayLibrarySplit()) { - displayPlaylist = true; - playlist.on_size(ww, wh); - setLibrarySize(); - } else if (pref.layout === 'default' && pref.libraryLayout === 'split') { - displayPlaylist = true; - initLibraryLayout(); + /** + * Updates the state of the button. + * @param {number} state - The new state to set for the button. + * @private + */ + changeState(state) { + this.state = state; + this.activatedBtns.push(this); + this._alphaTimer(); } - if (pref.layout === 'default' && pref.libraryLayout !== 'split' && - pref.libraryLayoutSplitPreset || pref.libraryLayoutSplitPreset2 || - pref.libraryLayoutSplitPreset3 || pref.libraryLayoutSplitPreset4) { - g_properties.auto_collapse = false; - playlist.expand_header(); + /** + * Sets the button state based on passed args. + * @param {Button} activeButton - The button to activate. + * @param {Button} deactivateButton - The button to deactivate. + */ + setButtonState(activeButton, deactivateButton) { + if (activeButton) { + activeButton.enabled = true; + activeButton.changeState(ButtonState.Down); + } + if (deactivateButton) { + deactivateButton.enabled = false; + deactivateButton.changeState(ButtonState.Default); + } } - // Update Library nowPlaying state if song was played from the Playlist - lib.treeState(false, 2); - if (pref.libraryAutoScrollNowPlaying) { - pop.getNowplaying(); - pop.nowPlayingShow(); + /** + * Sets the button image, playback order preference, and foobar2000 playback order. + * @param {GdiBitmap} imgValue - The value of grMain.ui.btnImg.PlaybackDefault, grMain.ui.btnImg.PlaybackRepeatTrack, or grMain.ui.btnImg.PlaybackShuffle. + * @param {string} prefValue - The value of 'default', 'repeatPlaylist', 'repeatTrack', or 'shuffle'. + * @param {string} fbValue - The value of PlaybackOrder.Default, PlaybackOrder.RepeatPlaylist, PlaybackOrder.RepeatTrack, or PlaybackOrder.ShuffleTracks. + * @param {string} cmd - The value of top menu Playback > Order. + */ + setPlaybackOrder(imgValue, prefValue, fbValue, cmd) { + grm.ui.btn.playbackOrder.img = imgValue; + grSet.playbackOrder = prefValue; + fb.PlaybackOrder = fbValue; + fb.RunMainMenuCommand(`Playback/Order/${cmd}`); } - resizeArtwork(false); - setDiscArtRotationTimer(); - initButtonState(); - window.Repaint(); -} - - -/** - * Handles the Biography panel button action in the top menu. - */ -function btnBiography() { - displayPlaylist = pref.layout === 'default'; - displayLibrary = false; - displayBiography = !displayBiography; - pref.displayLyrics = false; - - if (displayCustomThemeMenu) reinitCustomThemeMenu(); - - // Switch playlist to normal width to prevent panel overlaying - pref.playlistLayoutNormal = pref.playlistLayout === 'full' && displayBiography; - - if (!displayBiography && pref.lyricsActiveState) { - pref.displayLyrics = true; - restoreLyricsLayout(); - // Switch playlist to normal width to prevent panel overlaying - pref.playlistLayoutNormal = pref.playlistLayout === 'full' && pref.displayLyrics; + /** + * Passes in the current button as an argument via the btnActionHandler. + */ + onClick() { + this._actionHandler(this); } - if (displayBiography && (pref.biographyLayout === 'full' || pref.layout === 'artwork')) { - pref.layout === 'artwork' ? displayPlaylistArtwork = false : displayPlaylist = false; - } else { - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - playlist.on_size(ww, wh); + /** + * Currently does not have any functionality. + */ + onDblClick() { + // We don't do anything with dblClick currently } - biography.on_playback_new_track(); // Update Biography state - resizeArtwork(false); - setDiscArtRotationTimer(); - initButtonState(); - window.Repaint(); -} - - -/** - * Handles the Lyrics panel button action in the top menu. - */ -function btnLyrics() { - displayPlaylist = pref.layout === 'default'; - displayPlaylistArtwork = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = !pref.displayLyrics; - - if (displayCustomThemeMenu) reinitCustomThemeMenu(); - - // Switch playlist to normal width to prevent panel overlaying - pref.playlistLayoutNormal = pref.playlistLayout === 'full' && pref.displayLyrics; - // Save lyric active state - pref.lyricsActiveState = pref.displayLyrics && pref.lyricsRememberActiveState; - - if (pref.lyricsLayout === 'full' && pref.displayLyrics) { - displayPlaylist = false; - resizeArtwork(true); - } else { - playlist.on_size(ww, wh); + /** + * Repaints the button to update its state. + */ + repaint() { + window.RepaintRect(this.x, this.y, this.w, this.h); } + // #endregion - if (pref.panelWidthAuto) { - initPanelWidthAuto(); + // * CALLBACKS * // + // #region CALLBACKS + /** + * Checks if the mouse is within the boundaries of a button. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + mouseInThis(x, y) { + return (this.x <= x) && (x <= this.x + this.w) && (this.y <= y) && (y <= this.y + this.h); } - initLyrics(); - resizeArtwork(pref.layout === 'artwork'); - initButtonState(); - window.Repaint(); -} - - -/** - * Handles the maximize button action in the top menu, player goes into fullscreen and resumes player size. - */ -function btnMaximize() { - if (pref.maximizeToFullscreen) { // F11 shortcut ( on_key_down() ) for going into/out fullscreen mode, disabled/not supported in Artwork layout, ESC also exits fullscreen mode - UIHacks.FullScreen = !UIHacks.FullScreen; - } else { - UIHacks.MainWindowState = UIHacks.MainWindowState === WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + /** + * Handles mouse double click events on a button and changes its state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_lbtn_dblclk(x, y, m) { + if (!this.button) return; + this.button.changeState(ButtonState.Down); + this.downButton = this.button; + this.downButton.onDblClick(); } -} - -/////////////////////////// -// * LOWER BAR BUTTONS * // -/////////////////////////// -/** - * Handles the stop button action in the lower bar, displays the set panel based on pref.showPanelOnStartup when playback stops. - */ -function btnStop() { - if (pref.returnToHomeOnPlaybackStop && pref.layout !== 'compact') { - switch (pref.showPanelOnStartup) { - case 'playlist': - displayPlaylist = true; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - playlist.on_size(ww, wh); - break; - case 'details': - displayPlaylist = pref.layout === 'artwork'; - displayPlaylistArtwork = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - break; - case 'library': - displayPlaylist = displayLibrarySplit(); - displayPlaylistArtwork = false; - displayLibrary = true; - displayBiography = false; - pref.displayLyrics = false; - break; - case 'biography': - displayPlaylist = true; - displayPlaylistArtwork = false; - displayLibrary = true; - displayBiography = true; - pref.displayLyrics = false; - playlist.on_size(ww, wh); - break; - case 'lyrics': - displayPlaylist = true; - displayPlaylistArtwork = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = true; - playlist.on_size(ww, wh); - break; - case 'cover': // Artwork layout - displayPlaylist = false; - displayPlaylistArtwork = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - break; - } + /** + * Handles left mouse click down events on a button and changes its state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_lbtn_down(x, y, m) { + if (!this.button) return; + this.button.changeState(ButtonState.Down); + this.downButton = this.button; } - window.Repaint(); - initButtonState(); -} - - -/** - * Handles the play button action in the lower bar, toggles between playback play or pause state. - */ -function btnPlayPause() { - const showTransportControls = pref[`showTransportControls_${pref.layout}`]; - if (!showTransportControls) return; - btns.play.img = !fb.IsPlaying || fb.IsPaused ? btnImg.Play : btnImg.Pause; - btns.play.repaint(); -} - - -/** - * Handles the playback order button action in the lower bar, toggles the current playback order. - */ -function btnPlaybackOrder() { - const showTransportControls = pref[`showTransportControls_${pref.layout}`]; - if (!showTransportControls) return; - /** - * Sets the button image, playback order preference, and foobar2000 playback order. - * @param {GdiBitmap} imgValue The value of btnImg.PlaybackDefault, btnImg.PlaybackRepeatTrack, or btnImg.PlaybackShuffle. - * @param {string} prefValue The value of 'default', 'repeatPlaylist', 'repeatTrack', or 'shuffle'. - * @param {string} fbValue The value of PlaybackOrder.Default, PlaybackOrder.RepeatPlaylist, PlaybackOrder.RepeatTrack, or PlaybackOrder.ShuffleTracks. - * @param {string} cmd The value of top menu Playback > Order. + * Handles left mouse click up events on a button and changes its state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ - const setPlaybackOrder = (imgValue, prefValue, fbValue, cmd) => { - btns.playbackOrder.img = imgValue; - pref.playbackOrder = prefValue; - fb.PlaybackOrder = fbValue; - fb.RunMainMenuCommand(`Playback/Order/${cmd}`); - } + on_mouse_lbtn_up(x, y, m) { + if (!this.downButton) return; - switch (plman.PlaybackOrder) { - case PlaybackOrder.Default: - setPlaybackOrder(btnImg.PlaybackRepeatPlaylist, 'repeatPlaylist', PlaybackOrder.RepeatPlaylist, 'Repeat (playlist)'); - break; - - case PlaybackOrder.RepeatPlaylist: - setPlaybackOrder(btnImg.PlaybackRepeatTrack, 'repeatTrack', PlaybackOrder.RepeatTrack, 'Repeat (track)'); - break; - case PlaybackOrder.RepeatTrack: - setPlaybackOrder(btnImg.PlaybackShuffle, 'shuffle', PlaybackOrder.ShuffleTracks, 'Shuffle (tracks)'); - break; - - case PlaybackOrder.Random: - case PlaybackOrder.ShuffleTracks: - case PlaybackOrder.ShuffleAlbums: - case PlaybackOrder.ShuffleFolders: - setPlaybackOrder(btnImg.PlaybackDefault, 'default', PlaybackOrder.Default, 'Default'); - break; - } + this.downButton.onClick(); - btns.playbackOrder.repaint(); -} + if (this.mainMenuOpen) { + this.button = undefined; + this.mainMenuOpen = false; + } + if (this.button) { + this.button.changeState(ButtonState.Hovered); + } + else if (this.downButton && this.downButton === this.button) { + this.downButton.changeState(ButtonState.Default); + } -/** - * Handles the volume button action in the lower bar, toggles the volume bar. - */ -function btnVolume() { - if (pref.autoHideVolumeBar) { - volumeBtn.toggleVolumeBar(); - } else { - fb.VolumeMute(); + this.button = this.downButton; } -} + /** + * Handles mouse leave events on a button and changes its state to default. + */ + on_mouse_leave() { + this.oldButton = undefined; -/** - * Handles the playback time button action in the lower bar, toggles the playback time to remaining or normal. - */ -function btnPlaybackTime() { - pref.switchPlaybackTime = !pref.switchPlaybackTime; - on_playback_time(); -} - - -/** - * Handles the lower bar playback transport button tooltips. - * @param {string} btn The playback transport button. - */ -function btnTransportTooltip(btn) { - const pbModeTooltipText = - (fb.StopAfterCurrent ? 'Stop after current\n' : '') + - (fb.PlaybackFollowCursor ? 'Playback follows cursor\n' : '') + - (fb.CursorFollowPlayback ? 'Cursor follows playback\n' : ''); - - const pboTooltipText = - plman.PlaybackOrder === PlaybackOrder.Default ? 'Default' : - plman.PlaybackOrder === PlaybackOrder.RepeatPlaylist ? 'Repeat (playlist)' : - plman.PlaybackOrder === PlaybackOrder.RepeatTrack ? 'Repeat (track)' : - plman.PlaybackOrder === PlaybackOrder.ShuffleTracks ? 'Shuffle (tracks)' : ''; - - const volumeTooltipText = `${fb.Volume.toFixed(2)} dB`; - - switch (btn) { - case 'stop': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nStop` : 'Stop'; - case 'prev': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nPrevious` : 'Previous'; - case 'play': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nPlay` : 'Play'; - case 'next': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nNext` : 'Next'; - case 'pbo': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nPlayback order: ${pboTooltipText}` : `Playback order: ${pboTooltipText}`; - case 'reload': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\nReload` : 'Reload'; - case 'volume': return pbModeTooltipText.length > 0 ? `Active playback modes:\n${pbModeTooltipText}\n${volumeTooltipText}` : volumeTooltipText; - } -} - + if (this.downButton) return; -////////////////////////////////// -// * PLAYLIST HISTORY BUTTONS * // -////////////////////////////////// -/** - * Handles the playlist history button action in the playlist manager bar. - * @param {string} btn The playlist history back or forward button. - */ -function btnPlaylistHistory(btn) { - if (btn.isEnabled && btn.isEnabled()) { - if (btn.id === 'Back') { - playlistHistory.back(); - } else { - playlistHistory.forward(); + for (const i in grm.ui.btn) { + if (grm.ui.btn[i].state !== 0) { + grm.ui.btn[i].changeState(ButtonState.Default); + } } } -} + /** + * Handles mouse move tracking events on a button and changes its state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_move(x, y, m) { + this.oldButton = this.button; -////////////////////////////// -// * BUTTON & PANEL STATE * // -////////////////////////////// -/** - * Displays the Library and Playlist side by side, called when Library layout is in split mode. - * @param {boolean} control Limits the area to the width and height of the playlist panel. - * @returns {boolean} True if Library and Playlist are being displayed. - */ -function displayLibrarySplit(control) { - return pref.layout === 'default' && pref.libraryLayout === 'split' && displayLibrary && displayPlaylist && - (control ? state.mouse_x > playlist.x && state.mouse_x <= playlist.x + playlist.w && - state.mouse_y > playlist.y && state.mouse_y <= playlist.y + playlist.h : ww); -} - + for (const i in grm.ui.btn) { + if (typeof grm.ui.btn[i] === 'object' && grm.ui.btn[i].mouseInThis(x, y)) { + this.mouseInControl = true; + this.button = grm.ui.btn[i]; + break; + } else { + this.mouseInControl = false; + this.button = null; + } + } -/** - * Initializes and sets the top menu button state. - */ -function initButtonState() { - try { - // These buttons do not exist in Compact layout - if (pref.layout !== 'compact') { - btns.details.enabled = false; - btns.details.changeState(ButtonState.Default); - btns.library.enabled = false; - btns.library.changeState(ButtonState.Default); - btns.biography.enabled = false; - btns.biography.changeState(ButtonState.Default); - btns.lyrics.enabled = false; - btns.lyrics.changeState(ButtonState.Default); + if (this.oldButton && this.oldButton !== this.button) { + this.oldButton.changeState(this.oldButton.enabled ? ButtonState.Enabled : ButtonState.Default); } - if (pref.layout === 'artwork') { - btns.playlistArtworkLayout.enabled = false; - btns.playlistArtworkLayout.changeState(ButtonState.Default); + if (this.button && this.button !== this.oldButton) { + this.button.changeState(ButtonState.Hovered); } - if (!displayPlaylist && !displayLibrary && !displayBiography && !pref.displayLyrics && pref.layout === 'default') { - btns.details.enabled = true; - btns.details.changeState(ButtonState.Down); + this.downButton = this.button; + + if (this.lastOverButton !== this.button) { + grm.ttip.stop(); } - else if (displayPlaylist && !displayLibrary && !displayBiography && !pref.displayLyrics && pref.layout === 'artwork') { - btns.details.enabled = true; - btns.details.changeState(ButtonState.Down); - if (displayPlaylistArtwork) { - displayPlaylist = false; - btns.details.enabled = false; - btns.details.changeState(ButtonState.Default); - btns.playlistArtworkLayout.enabled = true; - btns.playlistArtworkLayout.changeState(ButtonState.Down); + this.lastOverButton = this.button; + + if (grSet.showTooltipMain && this.lastOverButton) { + if (this.lastOverButton.tooltip) { + grm.ttip.showDelayed(this.lastOverButton.tooltip); } - } - else if (displayPlaylistArtwork && !displayLibrary && !displayBiography && !pref.displayLyrics && pref.layout === 'artwork') { - btns.playlistArtworkLayout.enabled = true; - btns.playlistArtworkLayout.changeState(ButtonState.Down); - } - else if (displayLibrary) { - if (!displayPlaylist || displayLibrarySplit()) { // Fixes active library button state when switching from Artwork layout to Default layout and pref.showPanelOnStartup === 'playlist' is active - btns.library.enabled = true; - btns.library.changeState(ButtonState.Down); + else if (this.lastOverButton.id === 'Volume' && !grm.volBtn.show_volume_bar) { + grm.ttip.showDelayed(this.lowerTransportTooltip('volume')); + } + else if (this.lastOverButton.id === 'PlaybackOrder') { + grm.ttip.showDelayed(this.lowerTransportTooltip('pbo')); } - } - else if (displayBiography) { - btns.biography.enabled = true; - btns.biography.changeState(ButtonState.Down); - } - if (pref.displayLyrics) { - btns.lyrics.enabled = true; - btns.lyrics.changeState(ButtonState.Down); - } - // For Control_ContextMenu -> Display/Hide lyrics - if (pref.displayLyrics && !displayPlaylist && !displayLibrary && !displayBiography && pref.lyricsLayout === 'normal' && pref.layout === 'default') { - btns.details.enabled = true; - btns.details.changeState(ButtonState.Down); } } - catch (e) {} -} - - -/** - * Restores the Lyrics layout to full width. - */ -function restoreLyricsLayout() { - if (!pref.displayLyrics || !lyricsLayoutFullWidth) return; - if (!displayBiography) displayPlaylist = false; - pref.lyricsLayout = 'full'; + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-callbacks.js b/profile/georgia-reborn/scripts/Base/gr-callbacks.js index 589dfd84..63464299 100644 --- a/profile/georgia-reborn/scripts/Base/gr-callbacks.js +++ b/profile/georgia-reborn/scripts/Base/gr-callbacks.js @@ -1,13 +1,13 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Callbacks * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-13 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Callbacks * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; @@ -18,43 +18,46 @@ //////////////////////// /** * Called when thread created by utils.GetAlbumArtAsync is done. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @param {number} art_id See Flags.js > AlbumArtId. - * @param {GdiBitmap} image Null on failure. - * @param {string} image_path The path to image file (or music file if image is embedded). + * @global + * @param {FbMetadbHandle} metadb - The metadb of the track. + * @param {number} art_id - See Flags.js > AlbumArtId. + * @param {GdiBitmap} image - Null on failure. + * @param {string} image_path - The path to image file (or music file if image is embedded). */ function on_get_album_art_done(metadb, art_id, image, image_path) { - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_get_album_art_done'); - playlist.on_get_album_art_done(metadb, art_id, image, image_path); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_get_album_art_done'); + pl.call.on_get_album_art_done(metadb, art_id, image, image_path); } - else if (displayLibrary) { - trace_call && console.log('Library => on_get_album_art_done'); - library.on_get_album_art_done(metadb, art_id, image, image_path); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_get_album_art_done'); + lib.call.on_get_album_art_done(metadb, art_id, image, image_path); } - if (displayBiography) { - trace_call && console.log('Biography => on_get_album_art_done'); - biography.on_get_album_art_done(metadb, art_id, image, image_path); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_get_album_art_done'); + bio.call.on_get_album_art_done(metadb, art_id, image, image_path); } } /** * Called when thread created by gdi.LoadImageAsync is done. - * @param {number} cookie The return value from the gdi.LoadImageAsync call. - * @param {GdiBitmap} imagenullable Null on failure (invalid path/not an image). - * @param {string} image_path The path that was originally supplied to gdi.LoadImageAsync. + * @global + * @param {number} cookie - The return value from the gdi.LoadImageAsync call. + * @param {GdiBitmap} imagenullable - Null on failure (invalid path/not an image). + * @param {string} image_path - The path that was originally supplied to gdi.LoadImageAsync. */ function on_load_image_done(cookie, imagenullable, image_path) { - trace_call && console.log('Biography => on_load_image_done'); - biography.on_load_image_done(cookie, imagenullable, image_path); + grm.ui.traceCall && console.log('Biography => on_load_image_done'); + bio.call.on_load_image_done(cookie, imagenullable, image_path); } /** * Called when metadb contents change, i.e tag or database updates. - * @param {FbMetadbHandleList=} handle_list Can be undefined when called manually from on_playback_new_track. - * @param {boolean=} fromhook True if notification is not from tag update, but a component that provides tag-like data from a database. + * @global + * @param {FbMetadbHandleList} [handle_list] - Can be undefined when called manually from on_playback_new_track. + * @param {boolean} [fromhook] - True if notification is not from tag update, but a component that provides tag-like data from a database. */ function on_metadb_changed(handle_list, fromhook) { console.log(`on_metadb_changed(): ${handle_list ? handle_list.Count : '0'} handles, fromhook: ${fromhook}`); @@ -73,25 +76,24 @@ function on_metadb_changed(handle_list, fromhook) { if (nowPlayingUpdated) { // * The handle_list contains the currently playing song so update - const title = $(tf.title); - const artist = $(tf.artist); - const composer = $(tf.composer); - const originalArtist = $(tf.original_artist); - let tracknum = ''; - tracknum = pref.showVinylNums ? $(globals.vinyl_track) : $(tf.tracknum); - - str.tracknum = tracknum.trim(); - str.title = title + originalArtist; - str.title_lower = title; - str.original_artist = originalArtist; - str.artist = artist; - str.composer = composer; - str.year = $(tf.year); - if (str.year === '0000') { - str.year = ''; + const title = $(grTF.title); + const artist = $(grTF.artist); + const composer = $(grTF.composer); + const originalArtist = $(grTF.original_artist); + const tracknum = grSet.showVinylNums ? $(grTF.vinyl_track) : $(grTF.tracknum); + + grStr.tracknum = tracknum.trim(); + grStr.title = title + originalArtist; + grStr.titleLower = title; + grStr.original_artist = originalArtist; + grStr.artist = artist; + grStr.composer = composer; + grStr.year = $(grTF.year); + if (grStr.year === '0000') { + grStr.year = ''; } - str.album = $(`[%album%][ '['${tf.album_translation}']']`); - str.album_subtitle = $(`[ '['${tf.album_subtitle}']']`); + grStr.album = $(`[%album%][ '['${grTF.album_translation}']']`); + grStr.album_subtitle = $(`[ '['${grTF.album_subtitle}']']`); let codec = $('$lower($if2(%codec%,$ext(%path%)))'); if (codec === 'dca (dts coherent acoustics)') { codec = 'dts'; @@ -109,57 +111,57 @@ function on_metadb_changed(handle_list, fromhook) { else if ($('$info(encoding)') === 'lossy') { codec = $('$info(codec_profile)') === 'CBR' ? `${codec}-${$('%bitrate%')} kbps` : `${codec}-${$('$info(codec_profile)')}`; } - str.trackInfo = $(codec + settings.extraTrackInfo); - str.disc = fb.TitleFormat(tf.disc).Eval(); + grStr.trackInfo = $(codec + grCfg.settings.extraTrackInfo); + grStr.disc = fb.TitleFormat(grTF.disc).Eval(); const h = Math.floor(fb.PlaybackLength / 3600); const m = Math.floor(fb.PlaybackLength % 3600 / 60); const s = Math.floor(fb.PlaybackLength % 60); - str.length = `${h > 0 ? `${h}:${m < 10 ? '0' : ''}${m}` : m}:${s < 10 ? '0' : ''}${s}`; + grStr.length = `${h > 0 ? `${h}:${m < 10 ? '0' : ''}${m}` : m}:${s < 10 ? '0' : ''}${s}`; const lastfmCount = $('%lastfm_play_count%'); - playCountVerifiedByLastFm = lastfmCount !== '0' && lastfmCount !== '?'; + grm.ui.playCountVerifiedByLastFm = lastfmCount !== '0' && lastfmCount !== '?'; - const lastPlayed = $(tf.last_played); - if (str.timeline) { // TODO: figure out why this is null for foo_input_spotify - str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - // No need to call calcDateRatios if str.timeline is undefined - calcDateRatios($Date(currentLastPlayed) !== $Date(lastPlayed), currentLastPlayed); // lastPlayed has probably changed and we want to update the date bar + const lastPlayed = $(grTF.last_played); + if (grm.timeline) { // TODO: figure out why this is null for foo_input_spotify + grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + // No need to call calcDateRatios if grMain.timeline is undefined + grm.ui.calcDateRatios($Date(grm.ui.currentLastPlayed) !== $Date(lastPlayed), grm.ui.currentLastPlayed); // lastPlayed has probably changed and we want to update the date bar } if (lastPlayed.length) { const today = DateToYMD(new Date()); - if (!currentLastPlayed.length || $Date(lastPlayed) !== today) { - currentLastPlayed = lastPlayed; + if (!grm.ui.currentLastPlayed.length || $Date(lastPlayed) !== today) { + grm.ui.currentLastPlayed = lastPlayed; } } - updateMetadataGrid(currentLastPlayed, playingPlaylist); + grm.ui.updateMetadataGrid(grm.ui.currentLastPlayed, grm.ui.playingPlaylist); - const showGridArtistFlags = pref[`showGridArtistFlags_${pref.layout}`]; - const showGridReleaseFlags = pref[`showGridReleaseFlags_${pref.layout}`]; - const showLowerBarArtistFlags = pref[`showLowerBarArtistFlags_${pref.layout}`]; + const showGridArtistFlags = grSet[`showGridArtistFlags_${grSet.layout}`]; + const showGridReleaseFlags = grSet[`showGridReleaseFlags_${grSet.layout}`]; + const showLowerBarArtistFlags = grSet[`showLowerBarArtistFlags_${grSet.layout}`]; if (showGridArtistFlags || showLowerBarArtistFlags) { - loadCountryFlags(); + grm.ui.loadCountryFlags(); } if (showGridReleaseFlags) { - loadReleaseCountryFlag(); + grm.ui.loadReleaseCountryFlag(); } } } // * Not called manually from on_playback_new_track if (handle_list) { - if (displayPlaylist || displayPlaylistArtwork || !displayPlaylist) { - trace_call && console.log('Playlist => on_metadb_changed'); - playlist && playlist.on_metadb_changed(handle_list, fromhook); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork || !grm.ui.displayPlaylist) { + grm.ui.traceCall && console.log('Playlist => on_metadb_changed'); + pl.call && pl.call.on_metadb_changed(handle_list, fromhook); } - if (displayLibrary) { - trace_call && console.log('Library => on_metadb_changed'); - library && library.on_metadb_changed(handle_list, fromhook); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_metadb_changed'); + lib.call && lib.call.on_metadb_changed(handle_list, fromhook); } - if (displayBiography) { - trace_call && console.log('Biography => on_metadb_changed'); - biography && biography.on_metadb_changed(handle_list, fromhook); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_metadb_changed'); + bio.call && bio.call.on_metadb_changed(handle_list, fromhook); } } RepaintWindow(); @@ -168,103 +170,112 @@ function on_metadb_changed(handle_list, fromhook) { /** * Called when playing a new track. - * @param {FbMetadbHandle} metadb The metadb of the track. + * @global + * @param {FbMetadbHandle} metadb - The metadb of the track. */ function on_playback_new_track(metadb) { if (!metadb) return; // Solve weird corner case - const newTrackProfiler = timings.showDebugTiming && fb.CreateProfiler('on_playback_new_track'); + const newTrackProfiler = grm.ui.showDebugTiming && fb.CreateProfiler('on_playback_new_track'); DebugLog('in on_playback_new_track()'); - - lastLeftEdge = 0; - newTrackFetchingArtwork = true; - newTrackFetchingDone = false; - themeColorSet = true; - initThemeFull = false; UpdateTimezoneOffset(); - isPlayingCD = metadb ? metadb.RawPath.startsWith('cdda://') : false; - isStreaming = metadb ? metadb.RawPath.startsWith('http://') : false; - currentAlbumFolder = !isStreaming ? metadb.Path.substring(0, metadb.Path.lastIndexOf('\\')) : ''; - setProgressBarRefresh(); + grm.ui.lastLeftEdge = 0; + grm.ui.newTrackFetchingArtwork = true; + grm.ui.newTrackFetchingDone = false; + grm.ui.themeColorSet = true; + grm.ui.initThemeFull = false; + grm.ui.isPlayingCD = metadb ? metadb.RawPath.startsWith('cdda://') : false; + grm.ui.isStreaming = metadb ? metadb.RawPath.startsWith('http://') : false; + grm.ui.currentAlbumFolder = !grm.ui.isStreaming ? metadb.Path.substring(0, metadb.Path.lastIndexOf('\\')) : ''; - if (albumArtTimeout) { - clearTimeout(albumArtTimeout); - albumArtTimeout = 0; - } + grm.timeline = new Timeline(grm.ui.timelineHeight); + grm.gridTip = new MetadataGridTooltip(grm.ui.gridTooltipHeight); + grm.lowerTip = new LowerBarTooltip(); - str.timeline = new Timeline(geo.timelineHeight); - str.metadata_grid_tt = new MetadataGridTooltip(geo.metadataGridTooltipHeight); - str.lowerBar_tt = new LowerBarTooltip(); + grm.ui.setProgressBarRefresh(); - // * Fetch new albumArt - if ((pref.cycleArt && albumArtIndex !== 0) || isStreaming || embeddedArt || currentAlbumFolder !== lastAlbumFolder || albumArt == null || - $('%album%') !== lastAlbumFolderTag || $('$if2(%discnumber%,0)') !== lastAlbumDiscNumber || $(`$if2(${tf.vinyl_side},ZZ)`) !== lastAlbumVinylSide) { - clearPlaylistNowPlayingBg(); - fetchNewArtwork(metadb); + if (grm.ui.albumArtTimeout) { + clearTimeout(grm.ui.albumArtTimeout); + grm.ui.albumArtTimeout = 0; } - else if (pref.cycleArt && albumArtList.length > 1) { - // Need to do this here since we're no longer always fetching when albumArtList.length > 1 - albumArtTimeout = setTimeout(() => { - displayNextImage(); - }, settings.artworkDisplayTime * 1000); + + // * Fetch new albumArt + if ((grSet.cycleArt && grm.ui.albumArtIndex !== 0) + || grm.ui.isStreaming + || grm.ui.albumArtEmbedded + || grm.ui.currentAlbumFolder !== grm.ui.lastAlbumFolder + || grm.ui.albumArt == null + || $('%album%') !== grm.ui.lastAlbumFolderTag + || $('$if2(%discnumber%,0)') !== grm.ui.lastAlbumDiscNumber + || $(`$if2(${grTF.vinyl_side},ZZ)`) !== grm.ui.lastAlbumVinylSide) { + grm.ui.clearPlaylistNowPlayingBg(); + grm.ui.fetchNewArtwork(metadb); + } + else if (grSet.cycleArt && grm.ui.albumArtList.length > 1) { + // Need to do this here since we're no longer always fetching when grMain.ui.albumArtList.length > 1 + grm.ui.albumArtTimeout = setTimeout(() => { + grm.ui.displayNextImage(); + }, grCfg.settings.artworkDisplayTime * 1000); } // * Pick a new random theme preset on new track - if (pref.presetAutoRandomMode === 'track' && !doubleClicked) getRandomThemePreset(); + if (grSet.presetAutoRandomMode === 'track' && !grm.ui.doubleClicked) { + grm.preset.getRandomThemePreset(); + } // * Generate a new color in Random theme on new track - if (pref.styleRandomAutoColor === 'track' && !doubleClicked) getRandomThemeAutoColor(); + if (grSet.styleRandomAutoColor === 'track' && !grm.ui.doubleClicked) { + grm.color.getRandomThemeAutoColor(); + } - if (discArt) { - setDiscArtRotationTimer(); + if (grm.ui.discArt) { + grm.ui.setDiscArtRotationTimer(); } - if (pref.rotateDiscArt && !pref.spinDiscArt) { - createDiscArtRotation(); // We need to always setup the rotated image because it rotates on every track + if (grSet.rotateDiscArt && !grSet.spinDiscArt) { + grm.ui.createDiscArtRotation(); // We need to always setup the rotated image because it rotates on every track } - getBandLogo(); - getLabelLogo(metadb); + grm.ui.getBandLogo(); + grm.ui.getLabelLogo(metadb); - lastAlbumFolder = currentAlbumFolder; - lastAlbumFolderTag = $('%album%'); - lastAlbumDiscNumber = $('$if2(%discnumber%,0)'); - lastAlbumVinylSide = $(`$if2(${tf.vinyl_side},ZZ)`); - currentLastPlayed = $(tf.last_played); - playingPlaylist = pref.showGridPlayingPlaylist ? $(tf.playing_playlist = plman.GetPlaylistName(plman.PlayingPlaylist)) : ''; + grm.ui.lastAlbumFolder = grm.ui.currentAlbumFolder; + grm.ui.lastAlbumFolderTag = $('%album%'); + grm.ui.lastAlbumDiscNumber = $('$if2(%discnumber%,0)'); + grm.ui.lastAlbumVinylSide = $(`$if2(${grTF.vinyl_side},ZZ)`); + grm.ui.currentLastPlayed = $(grTF.last_played); + grm.ui.playingPlaylist = grSet.showGridPlayingPlaylist ? $(grTF.playing_playlist = plman.GetPlaylistName(plman.PlayingPlaylist)) : ''; - if (fb.GetNowPlaying()) { - on_metadb_changed(); // Refresh panel - } + if (fb.GetNowPlaying()) on_metadb_changed(); // Refresh panel on_playback_time(); - progressBar.progressLength = 0; - peakmeterBar.progressLength = 0; - if (pref.seekbar === 'peakmeterbar') { - peakmeterBar.on_playback_new_track(metadb); - } else if (pref.seekbar === 'waveformbar') { - waveformBar.on_playback_new_track_queue(metadb); + grm.progBar.progressLength = 0; + grm.peakBar.progressLength = 0; + if (grSet.seekbar === 'peakmeterbar') { + grm.peakBar.on_playback_new_track(metadb); + } else if (grSet.seekbar === 'waveformbar') { + grm.waveBar.on_playback_new_track_queue(metadb); } - if (displayPlaylist || displayPlaylistArtwork || !displayPlaylist) { - playlist.on_playback_new_track(metadb); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork || !grm.ui.displayPlaylist) { + pl.call.on_playback_new_track(metadb); } - if (displayLibrary) { - library.on_playback_new_track(metadb); + if (grm.ui.displayLibrary) { + lib.call.on_playback_new_track(metadb); } - if (displayBiography) { - biography.on_playback_new_track(); + if (grm.ui.displayBiography) { + bio.call.on_playback_new_track(); } - if (pref.displayLyrics) { // No need to try retrieving them if we aren't going to display them now - initLyrics(); + if (grm.ui.displayLyrics) { // No need to try retrieving them if we aren't going to display them now + grm.lyrics.initLyrics(); } // * Load finished, Playlist auto-scroll is ready - newTrackFetchingDone = true; + grm.ui.newTrackFetchingDone = true; if (newTrackProfiler) newTrackProfiler.Print(); - if (timings.showRamUsage) { + if (grm.ui.showRamUsage) { console.log( '\n' + 'Ram usage for current panel:', `${(window.JsMemoryStats.MemoryUsage / 1024 ** 2).toFixed()} MB\n` + @@ -279,67 +290,64 @@ function on_playback_new_track(metadb) { * Called when window is being resized. * * !IMPORTANT: Do NOT call window.Repaint from this callback! + * @global */ function on_size() { - ww = window.Width; - wh = window.Height; + grm.ui.ww = window.Width; + grm.ui.wh = window.Height; - console.log(`in on_size() => width: ${ww}, height: ${wh}`); + console.log(`in on_size() => width: ${grm.ui.ww}, height: ${grm.ui.wh}`); - if (ww <= 0 || wh <= 0) return; + if (grm.ui.ww <= 0 || grm.ui.wh <= 0) return; - display.checkRes(); + grm.display.checkRes(); - if (!sizeInitialized) { - createFonts(); - setGeometry(); + if (!grm.display.sizeInitialized) { + grm.ui.createFonts(); + grm.ui.initMetrics(); if (fb.IsPlaying) { - loadCountryFlags(); // Wrong size flag gets loaded on 4K systems + grm.ui.loadCountryFlags(); // Wrong size flag gets loaded on 4K systems } - rescalePlaylist(true); - initPlaylist(); - volumeBtn = new VolumeBtn(); - artCache && artCache.clear(); - artCache = new ArtCache(15); - sizeInitialized = true; - if (str.timeline) { - str.timeline.setHeight(geo.timelineHeight); + PlaylistRescale(true); + grm.ui.initPlaylist(); + grm.artCache && grm.artCache.clear(); + grm.display.sizeInitialized = true; + if (grm.timeline) { + grm.timeline.setHeight(grm.ui.timelineHeight); } - if (str.metadata_grid_tt) { - str.metadata_grid_tt.setHeight(geo.metadataGridTooltipHeight); + if (grm.gridTip) { + grm.gridTip.setHeight(grm.ui.gridTooltipHeight); } } - customMenu && customMenu.on_size(ww, wh); - jumpSearch && jumpSearch.on_size(ww, wh); - progressBar && progressBar.on_size(ww, wh); - waveformBar && waveformBar.on_size(ww, wh); - peakmeterBar && peakmeterBar.on_size(ww, wh); + grm.cusMenu && grm.cusMenu.on_size(grm.ui.ww, grm.ui.wh); + grm.jSearch && grm.jSearch.on_size(grm.ui.ww, grm.ui.wh); + grm.progBar && grm.progBar.on_size(grm.ui.ww, grm.ui.wh); + grm.peakBar && grm.peakBar.on_size(grm.ui.ww, grm.ui.wh); + grm.waveBar && grm.waveBar.on_size(grm.ui.ww, grm.ui.wh); - lastLeftEdge = 0; + grm.ui.lastLeftEdge = 0; - resizeArtwork(true); - createButtonObjects(ww, wh); - playlist.on_size(ww, wh); - setLibrarySize(); - setBiographySize(); + grm.ui.resizeArtwork(true); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.setLibrarySize(); + grm.ui.setBiographySize(); + if (grm.ui.displayLyrics) grm.lyrics.initLyrics(); - if (albumArt && (pref.styleBlend || pref.styleBlend2 || pref.styleProgressBarFill === 'blend')) setStyleBlend(); // Reposition all drawn blendedImg + if (grm.ui.albumArt && (grSet.styleBlend || grSet.styleBlend2 || grSet.styleProgressBarFill === 'blend')) { + grm.color.setStyleBlend(); // Reposition all drawn imgBlended + } - initButtonState(); + grm.button.initButtonState(); // * UIHacks double click on caption in fullscreen - if (!componentUIHacks) return; try { // Needed when double clicking on caption and UIHacks.FullScreen === true; also disabling maximize in Artwork layout if (!utils.IsKeyPressed(VK_CONTROL) && UIHacks.FullScreen && UIHacks.MainWindowState === WindowState.Normal || - pref.layout === 'artwork' && UIHacks.MainWindowState === WindowState.Maximized) { + grSet.layout === 'artwork' && UIHacks.MainWindowState === WindowState.Maximized) { UIHacks.MainWindowState = WindowState.Normal; } } catch (e) {} - - if (pref.displayLyrics) { - initLyrics(); - } } @@ -351,27 +359,29 @@ function on_size() { * * Note: in order to use this callback, use window.DlgCode(DLGC_WANTCHARS). * See Flags.js > DLGC_WANTCHARS. - * @param {number} code The character code. + * @global + * @param {number} code - The character code. */ function on_char(code) { - if (displayCustomThemeMenu || displayMetadataGridMenu) { - customMenu.on_char(code); + if (grm.ui.displayCustomThemeMenu || grm.ui.displayMetadataGridMenu) { + grm.ui.traceCall && console.log('Custom menu => on_char'); + grm.cusMenu.on_char(code); } - else if (displayPlaylist && !displayLibrary || !displayPlaylist && !displayLibrary || displayLibrarySplit(true)) { - trace_call && console.log('Playlist => on_char'); - jumpSearch.on_char(code); + else if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || !grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_char'); + grm.jSearch.on_char(code); // Switch back to Playlist - if (pref.layout === 'default' && !displayPlaylist && !displayLibrary) { - btns.details.onClick(); + if (grSet.layout === 'default' && grm.ui.displayDetails) { + grm.ui.btn.details.onClick(); } - else if (pref.layout === 'artwork' && !displayPlaylistArtwork && !displayLibrary) { - btns.playlistArtworkLayout.onClick(); + else if (grSet.layout === 'artwork' && !grm.ui.displayPlaylistArtwork && !grm.ui.displayLibrary) { + grm.ui.btn.playlistArtworkLayout.onClick(); } } - else if (displayLibrary) { - trace_call && console.log('Library => on_char'); - library.on_char(code); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_char'); + lib.call.on_char(code); } } @@ -381,16 +391,17 @@ function on_char(code) { * * 1. Called first. * - * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt - * @param {DropTargetAction} action The type of drag action being performed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. + * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt. + * @global + * @param {DropTargetAction} action - The type of drag action being performed. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. */ function on_drag_enter(action, x, y, mask) { - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_drag_enter'); - playlist.on_drag_enter(action, x, y, mask); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_drag_enter'); + pl.call.on_drag_enter(action, x, y, mask); } } @@ -400,16 +411,17 @@ function on_drag_enter(action, x, y, mask) { * * 2. Called after on_drag_enter. * - * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt - * @param {DropTargetAction} action The type of drag action being performed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. + * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt. + * @global + * @param {DropTargetAction} action - The type of drag action being performed. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. */ function on_drag_over(action, x, y, mask) { - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_drag_over'); - playlist.on_drag_over(action, x, y, mask); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_drag_over'); + pl.call.on_drag_over(action, x, y, mask); } } @@ -419,12 +431,13 @@ function on_drag_over(action, x, y, mask) { * * 3. Called after on_drag_over. * - * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt + * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt. + * @global */ function on_drag_leave() { - if (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) { - trace_call && console.log('Playlist => on_drag_leave'); - playlist.on_drag_leave(); + if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_drag_leave'); + pl.call.on_drag_leave(); } } @@ -434,63 +447,66 @@ function on_drag_leave() { * * 4. Called after on_drag_over. * - * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt - * @param {DropTargetAction} action The type of drag action being performed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. + * See fb.DoDragDrop documentation and samples/basic/DragnDrop.txt. + * @global + * @param {DropTargetAction} action - The type of drag action being performed. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. */ function on_drag_drop(action, x, y, mask) { - if (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) { - trace_call && console.log('Playlist => on_drag_drop'); - playlist.on_drag_drop(action, x, y, mask); + if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_drag_drop'); + pl.call.on_drag_drop(action, x, y, mask); } } /** * Called when the panel gets or loses focus. - * @param {boolean} is_focused Whether the panel is focused. + * @global + * @param {boolean} is_focused - Whether the panel is focused. */ function on_focus(is_focused) { - if (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) { - trace_call && console.log('Playlist => on_focus'); - playlist.on_focus(is_focused); + if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_focus'); + pl.call.on_focus(is_focused); } - else if (displayLibrary) { - trace_call && console.log('Library => on_focus'); - library.on_focus(is_focused); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_focus'); + lib.call.on_focus(is_focused); } - if (displayBiography) { - trace_call && console.log('Biography => on_focus'); - biography.on_focus(is_focused); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_focus'); + bio.call.on_focus(is_focused); } if (is_focused) { plman.SetActivePlaylistContext(); // When the panel gets focus but not on every click. } else { - clearTimeout(hideCursorTimeout); // Not sure this is required, but I think the mouse was occasionally disappearing + clearTimeout(grm.ui.hideCursorTimeout); // Not sure this is required, but I think the mouse was occasionally disappearing } } /** * Called when focused item in panel has been changed. - * @param {number} playlistIndex The index of the playlist. - * @param {number} from The index of the previously focused item. - * @param {number} to The index of the item that is gaining focus. + * @global + * @param {number} playlistIndex - The index of the playlist. + * @param {number} from - The index of the previously focused item. + * @param {number} to - The index of the item that is gaining focus. */ function on_item_focus_change(playlistIndex, from, to) { - if (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) { - trace_call && console.log('Playlist => on_item_focus_change'); - playlist.on_item_focus_change(playlistIndex, from, to); + if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_item_focus_change'); + pl.call.on_item_focus_change(playlistIndex, from, to); } - else if (displayLibrary) { - trace_call && console.log('Library => on_item_focus_change'); - library.on_item_focus_change(); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_item_focus_change'); + lib.call.on_item_focus_change(); } - if (displayBiography) { - trace_call && console.log('Biography => on_item_focus_change'); - biography.on_item_focus_change(); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_item_focus_change'); + bio.call.on_item_focus_change(); } } @@ -503,32 +519,34 @@ function on_item_focus_change(playlistIndex, from, to) { * In order to use arrow keys, use window.DlgCode(DLGC_WANTARROWS) (see Flags.js > DLGC_WANTARROWS). * * Note: keyboard shortcuts defined in the main preferences are always executed first and are not passed to the callback. - * @param {number} vkey The virtual key code. + * @global + * @param {number} vkey - The virtual key code. */ function on_key_down(vkey) { const CtrlKeyPressed = utils.IsKeyPressed(VK_CONTROL); const ShiftKeyPressed = utils.IsKeyPressed(VK_SHIFT); - if (displayCustomThemeMenu || displayMetadataGridMenu) { - customMenu.on_key_down(vkey); + if (grm.ui.displayCustomThemeMenu || grm.ui.displayMetadataGridMenu) { + grm.ui.traceCall && console.log('Custom menu => on_key_down'); + grm.cusMenu.on_key_down(vkey); } else { - if (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) { - trace_call && console.log('Playlist => on_key_down'); + if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_key_down'); - if (qwr_supress_key_down.is_supressed(vkey)) { + if (grm.utils.suppressKey(vkey)) { return; } - playlist.on_key_down(vkey); + pl.call.on_key_down(vkey); } - else if (displayLibrary) { - trace_call && console.log('Library => on_key_down'); - library.on_key_down(vkey); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_key_down'); + lib.call.on_key_down(vkey); } - if (displayBiography) { - trace_call && console.log('Biography => on_key_down'); - biography.on_key_down(vkey); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_key_down'); + bio.call.on_key_down(vkey); } } @@ -541,7 +559,7 @@ function on_key_down(vkey) { if (fb.IsPlaying) { fb.RunContextCommandWithMetadb(`Playback Statistics/Rating/${action}`, metadb); } - else if (!metadb && (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true))) { + else if (!metadb && (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true))) { const metadbList = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); if (metadbList.Count === 1) { fb.RunContextCommandWithMetadb(`Playback Statistics/Rating/${action}`, metadbList[0]); @@ -573,123 +591,119 @@ function on_key_down(vkey) { * Requires "Grab focus" enabled in the Configuration window. * * In order to use arrow keys, use window.DlgCode(DLGC_WANTARROWS) (see Flags.js > DLGC_WANTARROWS). - * @param {number} vkey The virtual key code. + * @global + * @param {number} vkey - The virtual key code. */ function on_key_up(vkey) { - if (displayLibrary) { - trace_call && console.log('Library => on_key_up'); - library.on_key_up(vkey); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_key_up'); + lib.call.on_key_up(vkey); } - else if (displayBiography) { - trace_call && console.log('Biography => on_key_up'); - biography.on_key_up(vkey); + else if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_key_up'); + bio.call.on_key_up(vkey); } } /** * Called when adding new songs to the media library index. - * @param {FbMetadbHandleList} handle_list The handle list of the library items. + * @global + * @param {FbMetadbHandleList} handle_list - The handle list of the library items. */ function on_library_items_added(handle_list) { - if (displayLibrary) { - trace_call && console.log('Library => on_library_items_added'); - library.on_library_items_added(handle_list); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_library_items_added'); + lib.call.on_library_items_added(handle_list); } - else if (displayBiography) { - trace_call && console.log('Biography => on_library_items_added'); - biography.on_library_items_added(handle_list); + else if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_library_items_added'); + bio.call.on_library_items_added(handle_list); } } /** * Called when media library is being changed, i.e updated by removing/adding tracks. - * @param {FbMetadbHandleList} handle_list The handle list of the library items. + * @global + * @param {FbMetadbHandleList} handle_list - The handle list of the library items. */ function on_library_items_changed(handle_list) { - if (displayLibrary) { - trace_call && console.log('Library => on_library_items_changed'); - library.on_library_items_changed(handle_list); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_library_items_changed'); + lib.call.on_library_items_changed(handle_list); } - else if (displayBiography) { - trace_call && console.log('Biography => on_library_items_changed'); - biography.on_library_items_changed(handle_list); + else if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_library_items_changed'); + bio.call.on_library_items_changed(handle_list); } } /** * Called when removing songs from the media library index. - * @param {FbMetadbHandleList} handle_list The handle list of the library items. + * @global + * @param {FbMetadbHandleList} handle_list - The handle list of the library items. */ function on_library_items_removed(handle_list) { - if (displayLibrary) { - trace_call && console.log('Library => on_library_items_removed'); - library.on_library_items_removed(handle_list); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_library_items_removed'); + lib.call.on_library_items_removed(handle_list); } - else if (displayBiography) { - trace_call && console.log('Biography => on_library_items_removed'); - biography.on_library_items_removed(handle_list); + else if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_library_items_removed'); + bio.call.on_library_items_removed(handle_list); } } -/** - * Refresh playback time plus playback time remaining every second. - */ -function on_playback_time() { - str.time = pref.switchPlaybackTime ? $('-%playback_time_remaining%') : $('%playback_time%'); - waveformBar.on_playback_time(fb.PlaybackTime); - biography.on_playback_time(); -} - - /** * Called when double clicking the left mouse button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_lbtn_dblclk(x, y, m) { - if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { - trace_call && console.log('Playlist => on_mouse_lbtn_dblclk'); - if (displayCustomThemeMenu && pref.displayLyrics) return; - playlist.on_mouse_lbtn_dblclk(x, y, m); - } - else if (displayLibrary && mouseInLibrary(x, y)) { - trace_call && console.log('Library => on_mouse_lbtn_dblclk'); - library.on_mouse_lbtn_dblclk(x, y, m); - } - else if (displayBiography && mouseInBiography(x, y)) { - trace_call && console.log('Biography => on_mouse_lbtn_dblclk'); - biography.on_mouse_lbtn_dblclk(x, y, m); - } - else if (!displayCustomThemeMenu && !displayMetadataGridMenu || displayCustomThemeMenu && mouseInPanel(x, y)) { - if (presetIndicatorTimer) { - clearTimeout(presetIndicatorTimer); - presetIndicatorTimer = null; + if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { + grm.ui.traceCall && console.log('Playlist => on_mouse_lbtn_dblclk'); + if (grm.ui.displayCustomThemeMenu && grm.ui.displayLyrics) return; + pl.call.on_mouse_lbtn_dblclk(x, y, m); + } + else if (grm.ui.displayLibrary && mouseInLibrary(x, y)) { + grm.ui.traceCall && console.log('Library => on_mouse_lbtn_dblclk'); + lib.call.on_mouse_lbtn_dblclk(x, y, m); + } + else if (grm.ui.displayBiography && mouseInBiography(x, y)) { + grm.ui.traceCall && console.log('Biography => on_mouse_lbtn_dblclk'); + bio.call.on_mouse_lbtn_dblclk(x, y, m); + } + else if (!grm.ui.displayCustomThemeMenu && !grm.ui.displayMetadataGridMenu || grm.ui.displayCustomThemeMenu && mouseInPanel(x, y)) { + if (grm.ui.presetIndicatorTimer) { + clearTimeout(grm.ui.presetIndicatorTimer); + grm.ui.presetIndicatorTimer = null; } - doubleClicked = true; - if (fb.IsPlaying && !mouseInControl && mouseInLowerBar(x, y)) { + grm.ui.doubleClicked = true; + if (fb.IsPlaying && !grm.button.mouseInControl && mouseInLowerBar(x, y)) { + grm.ui.traceCall && console.log('Lower bar => on_mouse_lbtn_dblclk'); // * Pick a new random theme preset - if (pref.presetAutoRandomMode === 'dblclick') { - themePresetIndicator = true; - getRandomThemePreset(); + if (grSet.presetAutoRandomMode === 'dblclick') { + grm.ui.themePresetIndicator = true; + grm.preset.getRandomThemePreset(); } // * Generate a new color in Random theme - else if (pref.theme === 'random') { - initTheme(); + else if (grSet.theme === 'random') { + grm.ui.initTheme(); DebugLog('\n>>> initTheme -> on_mouse_lbtn_dblclk -> random theme <<<\n'); } // * Refresh theme - else if (settings.doubleClickRefresh) { - albumArt = null; - artCache && artCache.clear(); - discArtArray = []; - discArtArrayCover = []; - discArt = null; - discArtCover = null; + else if (grCfg.settings.doubleClickRefresh) { + grm.ui.albumArt = null; + grm.artCache && grm.artCache.clear(); + grm.ui.discArtArray = []; + grm.ui.discArtArrayCover = []; + grm.ui.discArt = null; + grm.ui.discArtCover = null; RepaintWindow(); on_playback_new_track(fb.GetNowPlaying()); } @@ -700,55 +714,58 @@ function on_mouse_lbtn_dblclk(x, y, m) { /** * Called when left mouse button is pressed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_lbtn_down(x, y, m) { - const showProgressBar = pref[`showProgressBar_${pref.layout}`]; + const showProgressBar = grSet[`showProgressBar_${grSet.layout}`]; window.SetCursor(32512); // Arrow - if (topMenu) { - topMenu.on_mouse_lbtn_down(x, y, m); + if (grm.button) { + grm.button.on_mouse_lbtn_down(x, y, m); } - if (pref.seekbar === 'progressbar' && progressBar.mouseInThis(x, y)) { - progressBar.on_mouse_lbtn_down(x, y); + if (grSet.seekbar === 'progressbar' && grm.progBar.mouseInThis(x, y)) { + grm.ui.traceCall && console.log('Progress bar => on_mouse_lbtn_down'); + grm.progBar.on_mouse_lbtn_down(x, y); } - else if (pref.seekbar === 'peakmeterbar' && peakmeterBar.mouseInThis(x, y)) { - peakmeterBar.on_mouse_lbtn_down(x, y); + else if (grSet.seekbar === 'peakmeterbar' && grm.peakBar.mouseInThis(x, y)) { + grm.ui.traceCall && console.log('Peakmeter bar => on_mouse_lbtn_down'); + grm.peakBar.on_mouse_lbtn_down(x, y); } - else if (!volumeBtn.on_mouse_lbtn_down(x, y, m)) { - // Not handled by volumeBtn - + else if (!grm.volBtn.on_mouse_lbtn_down(x, y, m)) { // Not handled by volumeBtn // * Clicking on progress bar to seek playback if (showProgressBar && mouseInSeekbar(x, y)) { - let v = (x - 0.025 * ww) / (0.95 * ww); + let v = (x - 0.025 * grm.ui.ww) / (0.95 * grm.ui.ww); v = (v < 0) ? 0 : (v < 1) ? v : 1; if (fb.PlaybackTime !== v * fb.PlaybackLength) fb.PlaybackTime = v * fb.PlaybackLength; - window.RepaintRect(0, wh - geo.lowerBarHeight, ww, geo.lowerBarHeight); + window.RepaintRect(0, grm.ui.wh - grm.ui.lowerBarHeight, grm.ui.ww, grm.ui.lowerBarHeight); } - if (displayCustomThemeMenu || displayMetadataGridMenu) { - customMenu.on_mouse_lbtn_down(x, y, m); + if (grm.ui.displayCustomThemeMenu || grm.ui.displayMetadataGridMenu) { + grm.ui.traceCall && console.log('Custom menu => on_mouse_lbtn_down'); + grm.cusMenu.on_mouse_lbtn_down(x, y, m); } - if (updateHyperlink && !fb.IsPlaying && updateHyperlink.trace(x, y)) { - updateHyperlink.click(); + if (grCfg.updateHyperlink && !fb.IsPlaying && grCfg.updateHyperlink.trace(x, y)) { + grm.ui.traceCall && console.log('Hyperlink => on_mouse_lbtn_down'); + grCfg.updateHyperlink.click(); } - if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { - trace_call && console.log('Playlist => on_mouse_lbtn_down'); - if (displayCustomThemeMenu && displayBiography) return; - playlist.on_mouse_lbtn_down(x, y, m); + if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { + grm.ui.traceCall && console.log('Playlist => on_mouse_lbtn_down'); + if (grm.ui.displayCustomThemeMenu && grm.ui.displayBiography) return; + pl.call.on_mouse_lbtn_down(x, y, m); } - else if (displayLibrary && mouseInLibrary(x, y)) { - trace_call && console.log('Library => on_mouse_lbtn_down'); - library.on_mouse_lbtn_down(x, y, m); + else if (grm.ui.displayLibrary && mouseInLibrary(x, y)) { + grm.ui.traceCall && console.log('Library => on_mouse_lbtn_down'); + lib.call.on_mouse_lbtn_down(x, y, m); } - if (displayBiography && mouseInBiography(x, y)) { - trace_call && console.log('Biography => on_mouse_lbtn_down'); - biography.on_mouse_lbtn_down(x, y, m); + if (grm.ui.displayBiography && mouseInBiography(x, y)) { + grm.ui.traceCall && console.log('Biography => on_mouse_lbtn_down'); + bio.call.on_mouse_lbtn_down(x, y, m); } // * Clicking on album art or noAlbumArtStub to pause playback @@ -761,48 +778,53 @@ function on_mouse_lbtn_down(x, y, m) { /** * Called when left mouse button is released from pressed state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_lbtn_up(x, y, m) { - if (topMenu) { - topMenu.on_mouse_lbtn_up(x, y, m); + if (grm.button) { + grm.button.on_mouse_lbtn_up(x, y, m); } - if (pref.seekbar === 'progressbar') { - progressBar.on_mouse_lbtn_up(x, y); - } else if (pref.seekbar === 'peakmeterbar') { - peakmeterBar.on_mouse_lbtn_up(x, y); - } else if (pref.seekbar === 'waveformbar') { - waveformBar.on_mouse_lbtn_up(x, y, m); + if (grSet.seekbar === 'progressbar') { + grm.ui.traceCall && console.log('Progress bar => on_mouse_lbtn_up'); + grm.progBar.on_mouse_lbtn_up(x, y); + } else if (grSet.seekbar === 'peakmeterbar') { + grm.ui.traceCall && console.log('Peakmeter bar => on_mouse_lbtn_up'); + grm.peakBar.on_mouse_lbtn_up(x, y); + } else if (grSet.seekbar === 'waveformbar') { + grm.ui.traceCall && console.log('Waveform bar => on_mouse_lbtn_up'); + grm.waveBar.on_mouse_lbtn_up(x, y, m); } - if (displayCustomThemeMenu || displayMetadataGridMenu) { - customMenu.on_mouse_lbtn_up(x, y, m); + if (grm.ui.displayCustomThemeMenu || grm.ui.displayMetadataGridMenu) { + grm.ui.traceCall && console.log('Custom menu => on_mouse_lbtn_up'); + grm.cusMenu.on_mouse_lbtn_up(x, y, m); } - if (volumeBtn.on_mouse_lbtn_up(x, y, m)) return; + if (grm.volBtn.on_mouse_lbtn_up(x, y, m)) return; // Not handled by volumeBtn - if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit()) && mouseInPlaylist(x, y)) { - trace_call && console.log('Playlist => on_mouse_lbtn_up'); - if (displayCustomThemeMenu && displayBiography) return; - playlist.on_mouse_lbtn_up(x, y, m); + if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit()) && mouseInPlaylist(x, y)) { + grm.ui.traceCall && console.log('Playlist => on_mouse_lbtn_up'); + if (grm.ui.displayCustomThemeMenu && grm.ui.displayBiography) return; + pl.call.on_mouse_lbtn_up(x, y, m); - if (!pref.lockPlayerSize) qwr_utils.EnableSizing(m); + if (!grSet.lockPlayerSize) grm.utils.enableSizing(m); } - else if (displayLibrary && mouseInLibrary(x, y)) { - trace_call && console.log('Library => on_mouse_lbtn_up'); - library.on_mouse_lbtn_up(x, y, m); + else if (grm.ui.displayLibrary && mouseInLibrary(x, y)) { + grm.ui.traceCall && console.log('Library => on_mouse_lbtn_up'); + lib.call.on_mouse_lbtn_up(x, y, m); } - if (displayBiography && mouseInBiography(x, y)) { - trace_call && console.log('Biography => on_mouse_lbtn_up'); - biography.on_mouse_lbtn_up(x, y, m); + if (grm.ui.displayBiography && mouseInBiography(x, y)) { + grm.ui.traceCall && console.log('Biography => on_mouse_lbtn_up'); + bio.call.on_mouse_lbtn_up(x, y, m); } - if (doubleClicked) { - doubleClicked = false; // You just did a double-click, so do nothing + if (grm.ui.doubleClicked) { + grm.ui.doubleClicked = false; // You just did a double-click, so do nothing } on_mouse_move(x, y); @@ -811,174 +833,186 @@ function on_mouse_lbtn_up(x, y, m) { /** * Called when mouse leaves the window. + * @global */ function on_mouse_leave() { - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; - if (showVolumeBtn && volumeBtn) { - volumeBtn.on_mouse_leave(); + if (showVolumeBtn && grm.volBtn) { + grm.ui.traceCall && console.log('Volume button => on_mouse_leave'); + grm.volBtn.on_mouse_leave(); } - if (displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) { - playlist.on_mouse_leave(); + if (grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) { + grm.ui.traceCall && console.log('Playlist => on_mouse_leave'); + pl.call.on_mouse_leave(); } - else if (displayLibrary) { - library.on_mouse_leave(); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_mouse_leave'); + lib.call.on_mouse_leave(); } - if (displayBiography) { - biography.on_mouse_leave(); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_mouse_leave'); + bio.call.on_mouse_leave(); } } /** * Called when middle mouse (wheel) button is double clicked. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_mbtn_dblclk(x, y, m) { - if (displayLibrary) { - library.on_mouse_mbtn_dblclk(x, y, m); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_mouse_mbtn_dblclk'); + lib.call.on_mouse_mbtn_dblclk(x, y, m); } } /** * Called when middle mouse (wheel) button is pressed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_mbtn_down(x, y, m) { - if (displayLibrary) { - library.on_mouse_mbtn_down(x, y, m); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_mouse_mbtn_down'); + lib.call.on_mouse_mbtn_down(x, y, m); } } /** * Called when middle mouse (wheel) button is released from pressed state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_mbtn_up(x, y, m) { - if (displayLibrary) { - library.on_mouse_mbtn_up(x, y, m); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_mouse_mbtn_up'); + lib.call.on_mouse_mbtn_up(x, y, m); } - else if (displayBiography) { - biography.on_mouse_mbtn_up(x, y, m); + else if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_mouse_mbtn_up'); + bio.call.on_mouse_mbtn_up(x, y, m); } } /** * Called when mouse moves in the panel. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_move(x, y, m) { - const showGridTimeline = pref[`showGridTimeline_${pref.layout}`]; - const showTransportControls = pref[`showTransportControls_${pref.layout}`]; - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; + const showGridTimeline = grSet[`showGridTimeline_${grSet.layout}`]; + const showTransportControls = grSet[`showTransportControls_${grSet.layout}`]; + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; - if (x === state.mouse_x && y === state.mouse_y) return; + if (x === grm.ui.state.mouse_x && y === grm.ui.state.mouse_y) return; - display.setWindowDrag(x, y); + grm.display.setWindowDrag(x, y); if (!mouseInLibrarySearch(x, y)) window.SetCursor(32512); // Arrow - if (topMenu) { - topMenu.on_mouse_move(x, y, m); + if (grm.button) { + grm.button.on_mouse_move(x, y, m); } - if (progressBar && pref.seekbar === 'progressbar') { - progressBar.on_mouse_move(x, y); - } else if (peakmeterBar && pref.seekbar === 'peakmeterbar') { - peakmeterBar.on_mouse_move(x, y, m); - } else if (waveformBar && pref.seekbar === 'waveformbar') { - waveformBar.on_mouse_move(x, y, m); + if (grm.progBar && grSet.seekbar === 'progressbar') { + grm.progBar.on_mouse_move(x, y); + } else if (grm.peakBar && grSet.seekbar === 'peakmeterbar') { + grm.peakBar.on_mouse_move(x, y, m); + } else if (grm.waveBar && grSet.seekbar === 'waveformbar') { + grm.waveBar.on_mouse_move(x, y, m); } - state.mouse_x = x; - state.mouse_y = y; + grm.ui.state.mouse_x = x; + grm.ui.state.mouse_y = y; // * Top menu compact - collapse top menu to compact when mouse is out of top menu area - if (pref.topMenuCompact && !pref.showTopMenuCompact && state.mouse_y > geo.topMenuHeight * 2) { // Start collapse - pref.showTopMenuCompact = true; + if (grSet.topMenuCompact && !grSet.showTopMenuCompact && grm.ui.state.mouse_y > grm.ui.topMenuHeight * 2) { // Start collapse + grSet.showTopMenuCompact = true; setTimeout(() => { - topMenuCompact(true); - topMenuCompactExpanded = false; - }, 2000); - } - else if (pref.topMenuCompact && pref.showTopMenuCompact && state.mouse_y < geo.topMenuHeight * 2) { // Cancel collapse - topMenuCompactExpanded = true; + grm.button.topMenu(true); + }, 3000); } - if (settings.hideCursor && fb.IsPlaying) { - clearTimeout(hideCursorTimeout); - hideCursorTimeout = setTimeout(() => { + if (grCfg.settings.hideCursor && fb.IsPlaying) { + clearTimeout(grm.ui.hideCursorTimeout); + grm.ui.hideCursorTimeout = setTimeout(() => { // * If there's a menu id (i.e. a menu is down) we don't want the cursor to ever disappear - if (!activeMenu && fb.IsPlaying) { + if (!grm.ui.activeMenu && fb.IsPlaying) { window.SetCursor(-1); // Hide cursor } }, 10000); } - if (displayCustomThemeMenu || displayMetadataGridMenu) { - customMenu.on_mouse_move(x, y, m); + if (grm.ui.displayCustomThemeMenu || grm.ui.displayMetadataGridMenu) { + grm.ui.traceCall && console.log('Custom menu => on_mouse_move'); + grm.cusMenu.on_mouse_move(x, y, m); } - if (updateHyperlink) updateHyperlink.on_mouse_move(updateHyperlink, x, y); + if (grCfg.updateHyperlink) grCfg.updateHyperlink.on_mouse_move(grCfg.updateHyperlink, x, y); - if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { - trace_call && trace_on_move && console.log('Playlist => on_mouse_move'); + if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Playlist => on_mouse_move'); - if (qwr_supress_mouse_move.is_supressed(x, y, m)) { + if (grm.utils.suppressMouseMove(x, y, m)) { return; } - qwr_utils.DisableSizing(m); - playlist.on_mouse_move(x, y, m); + grm.utils.disableSizing(m); + pl.call.on_mouse_move(x, y, m); } - else if (displayLibrary && mouseInLibrary(x, y)) { - trace_call && trace_on_move && console.log('Library => on_mouse_move'); - library.on_mouse_move(x, y, m); + else if (grm.ui.displayLibrary && mouseInLibrary(x, y)) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Library => on_mouse_move'); + lib.call.on_mouse_move(x, y, m); } - else if (displayBiography && mouseInBiography(x, y)) { - trace_call && trace_on_move && console.log('Biography => on_mouse_move'); - biography.on_mouse_move(x, y, m); + else if (grm.ui.displayBiography && mouseInBiography(x, y)) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Biography => on_mouse_move'); + bio.call.on_mouse_move(x, y, m); } - else if (showGridTimeline && str.timeline && str.timeline.mouseInThis(x, y) && - (pref.layout === 'default' && !displayPlaylist && !displayLibrary && !displayBiography && !pref.displayLyrics || - pref.layout === 'artwork' && displayPlaylist && !displayLibrary && !displayBiography)) { // Prevent tooltips on album cover when Artwork layout is active - str.timeline.on_mouse_move(x, y, m); + else if (showGridTimeline && grm.timeline && grm.timeline.mouseInThis(x, y) && grm.ui.displayDetails) { + grm.ui.traceCall && console.log('Timeline => on_mouse_move'); + grm.timeline.on_mouse_move(x, y, m); } - else if (str.metadata_grid_tt && str.metadata_grid_tt.mouseInThis(x, y)) { - str.metadata_grid_tt.on_mouse_move(x, y, m); + else if (grm.gridTip && grm.gridTip.mouseInThis(x, y)) { + grm.ui.traceCall && console.log('Metadata Grid => on_mouse_move'); + grm.gridTip.on_mouse_move(x, y, m); } - else if (str.lowerBar_tt && str.lowerBar_tt.mouseInThis(x, y)) { - str.lowerBar_tt.on_mouse_move(x, y, m); + else if (grm.lowerTip && grm.lowerTip.mouseInThis(x, y)) { + grm.ui.traceCall && console.log('Lower bar tooltip => on_mouse_move'); + grm.lowerTip.on_mouse_move(x, y, m); } - else if (showTransportControls && showVolumeBtn && volumeBtn) { - volumeBtn.on_mouse_move(x, y, m); + else if (showTransportControls && showVolumeBtn && grm.volBtn) { + grm.volBtn.on_mouse_move(x, y, m); } } /** * Called when right mouse button is pressed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ function on_mouse_rbtn_down(x, y, m) { - if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) && mouseInPlaylist(x, y) && - !(displayCustomThemeMenu && displayBiography)) { - trace_call && console.log('Playlist => on_mouse_rbtn_down'); - playlist.on_mouse_rbtn_down(x, y, m); - } + if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) && mouseInPlaylist(x, y) && + !(grm.ui.displayCustomThemeMenu && grm.ui.displayBiography)) { + grm.ui.traceCall && console.log('Playlist => on_mouse_rbtn_down'); + pl.call.on_mouse_rbtn_down(x, y, m); + } } @@ -988,141 +1022,145 @@ function on_mouse_rbtn_down(x, y, m) { * You must return true, if you want to suppress the default context menu. * * Note: left shift + left windows key will bypass this callback and will open default context menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True to suppress the default context menu, otherwise depends on internal conditions. */ function on_mouse_rbtn_up(x, y, m) { const cmm = new ContextMainMenu(); const handleContextMenu = (x, y) => { - activeMenu = true; + grm.ui.activeMenu = true; cmm.execute(x, y); - activeMenu = false; + grm.ui.activeMenu = false; } if (mouseInTopMenu(x, y)) { - trace_call && console.log('Top menu => on_mouse_rbtn_up'); - qwr_utils.append_top_menu_context_menu_to(cmm); + grm.ui.traceCall && console.log('Top menu => on_mouse_rbtn_up'); + grm.ctxMenu.contextMenuTopBar(cmm); handleContextMenu(x, y); return true; } else if (mouseInAlbumArt(x, y) && fb.IsPlaying) { - trace_call && console.log('Album art => on_mouse_rbtn_up'); - qwr_utils.append_album_cover_context_menu_to(cmm); + grm.ui.traceCall && console.log('Album art => on_mouse_rbtn_up'); + grm.ctxMenu.contextMenuAlbumCover(cmm); handleContextMenu(x, y); return true; } else if (mouseInLowerBar(x, y) && !mouseInSeekbar(x, y)) { - trace_call && console.log('Lower bar => on_mouse_rbtn_up'); - qwr_utils.append_lower_bar_context_menu_to(cmm); + grm.ui.traceCall && console.log('Lower bar => on_mouse_rbtn_up'); + grm.ctxMenu.contextMenuLowerBar(cmm); handleContextMenu(x, y); return true; } else if (mouseInSeekbar(x, y)) { - trace_call && console.log('Seekbar => on_mouse_rbtn_up'); - qwr_utils.append_seekbar_context_menu_to(cmm); + grm.ui.traceCall && console.log('Seekbar => on_mouse_rbtn_up'); + grm.ctxMenu.contextMenuSeekbar(cmm); handleContextMenu(x, y); return true; } - if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { - trace_call && console.log('Playlist => on_mouse_rbtn_up'); - if (displayCustomThemeMenu && displayBiography) return; - return playlist.on_mouse_rbtn_up(x, y, m); + if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) && mouseInPlaylist(x, y)) { + grm.ui.traceCall && console.log('Playlist => on_mouse_rbtn_up'); + if (grm.ui.displayCustomThemeMenu && grm.ui.displayBiography) return; + return pl.call.on_mouse_rbtn_up(x, y, m); } - else if (displayLibrary && mouseInLibrary(x, y)) { - trace_call && console.log('Library => on_mouse_rbtn_up'); - return library.on_mouse_rbtn_up(x, y, m); + else if (grm.ui.displayLibrary && mouseInLibrary(x, y)) { + grm.ui.traceCall && console.log('Library => on_mouse_rbtn_up'); + return lib.call.on_mouse_rbtn_up(x, y, m); } - else if (displayBiography && mouseInBiography(x, y)) { - trace_call && console.log('Biography => on_mouse_rbtn_up'); - return biography.on_mouse_rbtn_up(x, y, m); + else if (grm.ui.displayBiography && mouseInBiography(x, y)) { + grm.ui.traceCall && console.log('Biography => on_mouse_rbtn_up'); + return bio.call.on_mouse_rbtn_up(x, y, m); } else { - return pref.disableRightClick; + return grSet.disableRightClick; } } /** * Called when using the mouse wheel, also used to cycle through album artworks and control the seekbar. - * @param {number} step The scroll direction: -1 or 1. + * @global + * @param {number} step - The scroll direction: -1 or 1. */ function on_mouse_wheel(step) { - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; - const displayAlbumArt = pref.layout !== 'compact' && - (!displayPlaylistArtwork && !displayBiography && !pref.displayLyrics || (displayLibrary && pref.libraryLayout === 'normal')); + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; + const displayAlbumArt = grSet.layout !== 'compact' && + (!grm.ui.displayPlaylistArtwork && !grm.ui.displayBiography && !grm.ui.displayLyrics || (grm.ui.displayLibrary && grSet.libraryLayout === 'normal')); - if (showVolumeBtn && volumeBtn.on_mouse_wheel(step)) return; + if (showVolumeBtn && grm.volBtn.on_mouse_wheel(step)) return; // * Seeking through playback if (mouseInSeekbar()) { - fb.PlaybackTime = fb.PlaybackTime - step * pref.progressBarWheelSeekSpeed; - refreshSeekbar(); - if (pref.seekbar === 'peakmeterbar') peakmeterBar.on_mouse_wheel(step); + fb.PlaybackTime = fb.PlaybackTime - step * grSet.progressBarWheelSeekSpeed; + grm.ui.refreshSeekbar(); + if (grSet.seekbar === 'peakmeterbar') grm.peakBar.on_mouse_wheel(step); return; } // * Cycling through album artwork - if (pref.cycleArtMWheel && albumArtList.length > 1 && displayAlbumArt && mouseInAlbumArt()) { + if (grSet.cycleArtMWheel && grm.ui.albumArtList.length > 1 && displayAlbumArt && mouseInAlbumArt()) { // Prev album art image if (step > 0) { - if (albumArtIndex !== 0) { - albumArtIndex = (albumArtIndex - 1) % albumArtList.length; + if (grm.ui.albumArtIndex !== 0) { + grm.ui.albumArtIndex = (grm.ui.albumArtIndex - 1) % grm.ui.albumArtList.length; } } // Next album art image - else if (albumArtIndex !== albumArtList.length - 1) { - albumArtIndex = (albumArtIndex + 1) % albumArtList.length; + else if (grm.ui.albumArtIndex !== grm.ui.albumArtList.length - 1) { + grm.ui.albumArtIndex = (grm.ui.albumArtIndex + 1) % grm.ui.albumArtList.length; } - loadImageFromAlbumArtList(albumArtIndex); + grm.ui.loadImageFromAlbumArtList(grm.ui.albumArtIndex); // Display embedded album art image - if (pref.loadEmbeddedAlbumArtFirst && albumArtIndex === 0) { - albumArt = utils.GetAlbumArtV2(fb.GetNowPlaying()); - albumArtList.unshift(albumArt); - albumArtIndex = 0; + if (grSet.loadEmbeddedAlbumArtFirst && grm.ui.albumArtIndex === 0) { + grm.ui.albumArt = utils.GetAlbumArtV2(fb.GetNowPlaying()); + grm.ui.albumArtList.unshift(grm.ui.albumArt); + grm.ui.albumArtIndex = 0; } // Update colors for dynamic themes - if (['white', 'black', 'reborn', 'random'].includes(pref.theme)) { - newTrackFetchingArtwork = true; - getThemeColors(albumArt); - initTheme(); + if (['white', 'black', 'reborn', 'random'].includes(grSet.theme)) { + grm.ui.newTrackFetchingArtwork = true; + grm.color.getThemeColors(grm.ui.albumArt); + grm.ui.initTheme(); DebugLog('\n>>> initTheme -> on_mouse_wheel <<<\n'); } // Update positions - resizeArtwork(true); // Re-adjust discArt shadow size if artwork size changes - if (pref.panelWidthAuto && albumArtSize.w !== albumArtSize.h) { // Re-adjust playlist if artwork size changes - playlist.on_size(ww, wh); + grm.ui.resizeArtwork(true); // Re-adjust discArt shadow size if artwork size changes + if (grSet.panelWidthAuto && grm.ui.albumArtSize.w !== grm.ui.albumArtSize.h) { // Re-adjust playlist if artwork size changes + pl.call.on_size(grm.ui.ww, grm.ui.wh); } - lastLeftEdge = 0; + grm.ui.lastLeftEdge = 0; RepaintWindow(); return; } - if (pref.displayLyrics && mouseInAlbumArt()) { - lyrics.on_mouse_wheel(step); + if (grm.ui.displayLyrics && mouseInAlbumArt()) { + grm.lyrics.on_mouse_wheel(step); } - else if (displayBiography && mouseInBiography()) { - trace_call && console.log('Biography => on_mouse_wheel'); - biography.on_mouse_wheel(step); + else if (grm.ui.displayBiography && mouseInBiography()) { + grm.ui.traceCall && console.log('Biography => on_mouse_wheel'); + bio.call.on_mouse_wheel(step); } - else if ((displayPlaylist && !displayLibrary || displayPlaylistArtwork || displayLibrarySplit(true)) && mouseInPlaylist()) { - trace_call && console.log('Playlist => on_mouse_wheel'); - playlist.on_mouse_wheel(step); + else if ((grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayPlaylistArtwork || grm.ui.displayLibrarySplit(true)) && mouseInPlaylist()) { + grm.ui.traceCall && console.log('Playlist => on_mouse_wheel'); + pl.call.on_mouse_wheel(step); } - else if (displayLibrary && mouseInLibrary()) { - trace_call && console.log('Library => on_mouse_wheel'); - library.on_mouse_wheel(step); + else if (grm.ui.displayLibrary && mouseInLibrary()) { + grm.ui.traceCall && console.log('Library => on_mouse_wheel'); + lib.call.on_mouse_wheel(step); } } /** * Called in other panels after window.NotifyOthers is executed. - * @param {string} name The name of the data that was updated. - * @param {*} info The data that was updated: + * @global + * @param {string} name - The name of the data that was updated. + * @param {*} info - The data that was updated: * * - 1. Data from `info` argument is only accessible inside `on_notify_data` callback: * if stored and accessed outside of the callback it will throw JS error. @@ -1131,22 +1169,22 @@ function on_mouse_wheel(step) { * - 2. If you want to store the data from `info` you have to perform a deep copy: * `String(info)` for strings. * `JSON.parse(JSON.stringify(info))` for serializable objects. - * `new ObjectType(info)` for objects that have an approppriate constructor available, e.g. `new GdiBitmap(info)` or `new FbMetadbHandleList(info)`. + * `new ObjectType(info)` for objects that have an appropriate constructor available, e.g. `new GdiBitmap(info)` or `new FbMetadbHandleList(info)`. * * - 3. `info` argument is shared between panels, so it should NOT be modified in any way. */ function on_notify_data(name, info) { - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_notify_data'); - playlist.on_notify_data(name, info); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_notify_data'); + pl.call.on_notify_data(name, info); } - else if (displayLibrary) { - trace_call && console.log('Library => on_notify_data'); - library.on_notify_data(name, info); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_notify_data'); + lib.call.on_notify_data(name, info); } - if (displayBiography) { - trace_call && console.log('Biography => on_notify_data'); - biography.on_notify_data(name, info); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_notify_data'); + bio.call.on_notify_data(name, info); } } @@ -1155,27 +1193,31 @@ function on_notify_data(name, info) { * Called when Per-track dynamic info (stream track titles etc) changes. * * Happens less often than on_playback_dynamic_info. + * @global */ function on_playback_dynamic_info_track() { // How frequently does this get called? const metadb = fb.IsPlaying ? fb.GetNowPlaying() : null; on_playback_new_track(metadb); - if (displayPlaylist || displayPlaylistArtwork) { - playlist.on_playback_dynamic_info_track(); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playback_dynamic_info_track'); + pl.call.on_playback_dynamic_info_track(); } - if (displayBiography) { - biography.on_playback_dynamic_info_track(); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playback_dynamic_info_track'); + bio.call.on_playback_dynamic_info_track(); } - if (pref.displayLyrics) { // No need to try retrieving them if we aren't going to display them now - initLyrics(); + if (grm.ui.displayLyrics) { // No need to try retrieving them if we aren't going to display them now + grm.lyrics.initLyrics(); } } /** * Called when playback order is changed via the transport playback order button or foobar's playback menu. - * @param {*} pbo The playback order has following settings: + * @global + * @param {*} pbo - The playback order has following settings: * - 0 - Default. * - 1 - Repeat (Playlist). * - 2 - Repeat (Track). @@ -1185,114 +1227,122 @@ function on_playback_dynamic_info_track() { */ function on_playback_order_changed(pbo) { // Repaint playback order - if (pbo !== lastPlaybackOrder) { + if (pbo !== grm.ui.lastPlaybackOrder) { DebugLog('Repainting on_playback_order_changed'); - window.RepaintRect(0.5 * ww, wh - geo.lowerBarHeight, 0.5 * ww, geo.lowerBarHeight); + window.RepaintRect(0.5 * grm.ui.ww, grm.ui.wh - grm.ui.lowerBarHeight, 0.5 * grm.ui.ww, grm.ui.lowerBarHeight); } - lastPlaybackOrder = pbo; + grm.ui.lastPlaybackOrder = pbo; // Link foobar's playback order menu functions with playback order button - const showTransportControls = pref[`showTransportControls_${pref.layout}`]; - const showPlaybackOrderBtn = pref[`showPlaybackOrderBtn_${pref.layout}`]; + const showTransportControls = grSet[`showTransportControls_${grSet.layout}`]; + const showPlaybackOrderBtn = grSet[`showPlaybackOrderBtn_${grSet.layout}`]; const showBtns = showTransportControls && showPlaybackOrderBtn; switch (pbo) { case PlaybackOrder.Default: - pref.playbackOrder = 'default'; - if (showBtns) btns.playbackOrder.img = btnImg.PlaybackDefault; + grSet.playbackOrder = 'default'; + if (showBtns) grm.ui.btn.playbackOrder.img = grm.ui.btnImg.PlaybackDefault; break; case PlaybackOrder.RepeatPlaylist: - pref.playbackOrder = 'repeatPlaylist'; - if (showBtns) btns.playbackOrder.img = btnImg.PlaybackRepeatPlaylist; + grSet.playbackOrder = 'repeatPlaylist'; + if (showBtns) grm.ui.btn.playbackOrder.img = grm.ui.btnImg.PlaybackRepeatPlaylist; break; case PlaybackOrder.RepeatTrack: - pref.playbackOrder = 'repeatTrack'; - if (showBtns) btns.playbackOrder.img = btnImg.PlaybackRepeatTrack; + grSet.playbackOrder = 'repeatTrack'; + if (showBtns) grm.ui.btn.playbackOrder.img = grm.ui.btnImg.PlaybackRepeatTrack; break; case PlaybackOrder.Random: case PlaybackOrder.ShuffleTracks: case PlaybackOrder.ShuffleAlbums: case PlaybackOrder.ShuffleFolders: - pref.playbackOrder = 'shuffle'; - if (showBtns) btns.playbackOrder.img = btnImg.PlaybackShuffle; + grSet.playbackOrder = 'shuffle'; + if (showBtns) grm.ui.btn.playbackOrder.img = grm.ui.btnImg.PlaybackShuffle; break; } + grm.ui.traceCall && console.log('Main => on_playback_order_changed'); } /** * Called when pausing current playing track. - * @param {boolean} state Whether the playback is paused or not. + * @global + * @param {boolean} state - Whether the playback is paused or not. */ function on_playback_pause(state) { - btnPlayPause(); + grm.button.lowerPlayPause(); if (state || fb.PlaybackLength < 0) { - clearInterval(progressBarTimer); - clearInterval(discArtRotationTimer); - window.RepaintRect(0, geo.topMenuHeight, Math.max(albumArtSize.x, SCALE(40)), wh - geo.topMenuHeight - geo.lowerBarHeight); + clearInterval(grm.ui.progressBarTimer); + clearInterval(grm.ui.discArtRotationTimer); + window.RepaintRect(0, grm.ui.topMenuHeight, Math.max(grm.ui.albumArtSize.x, SCALE(40)), grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight); } else { // Unpausing - clearInterval(progressBarTimer); // Clear to avoid multiple progressTimers which can happen depending on the playback state when theme is loaded - DebugLog(`on_playback_pause: creating refreshSeekbar() interval with delay = ${progressBarTimerInterval}`); - progressBarTimer = setInterval(() => { - refreshSeekbar(); - }, progressBarTimerInterval || 1000); - if (discArt && pref.spinDiscArt) setDiscArtRotationTimer(); + clearInterval(grm.ui.progressBarTimer); // Clear to avoid multiple progressTimers which can happen depending on the playback state when theme is loaded + DebugLog(`on_playback_pause: creating refreshSeekbar() interval with delay = ${grm.ui.progressBarTimerInterval}`); + grm.ui.progressBarTimer = setInterval(() => { + grm.ui.refreshSeekbar(); + }, grm.ui.progressBarTimerInterval || 1000); + if (grm.ui.discArt && grSet.spinDiscArt) grm.ui.setDiscArtRotationTimer(); } - pauseBtn.repaint(); + grm.pseBtn.repaint(); - if ((albumArt || noAlbumArtStub) && pref.displayLyrics) { // If we are displaying lyrics we need to refresh all the lyrics to avoid tearing at the edges of the pause button - lyrics.on_playback_pause(state); + if ((grm.ui.albumArt || grm.ui.noAlbumArtStub) && grm.ui.displayLyrics) { // If we are displaying lyrics we need to refresh all the lyrics to avoid tearing at the edges of the pause button + grm.ui.traceCall && console.log('Lyrics => on_playback_pause'); + grm.lyrics.on_playback_pause(state); } - if (displayPlaylist || displayPlaylistArtwork) { - playlist.on_playback_pause(state); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playback_pause'); + pl.call.on_playback_pause(state); } - if (displayBiography) { - biography.on_playback_pause(state); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playback_pause'); + bio.call.on_playback_pause(state); } } /** * Called when adding new playlist tracks in queue. - * @param {number} origin The parameter has following settings: + * @global + * @param {number} origin - The parameter has following settings: * - 0 - User added. * - 1 - User removed. * - 2 - Playback advance. */ function on_playback_queue_changed(origin) { - trace_call && console.log('Playlist => on_playback_queue_changed'); - playlist.on_playback_queue_changed(origin); - library.on_playback_queue_changed(); + grm.ui.traceCall && console.log('Playlist => on_playback_queue_changed'); + pl.call.on_playback_queue_changed(origin); + lib.call.on_playback_queue_changed(); } /** * Called when playback time is being seeked, float value in seconds. + * @global */ function on_playback_seek() { - if (pref.seekbar === 'progressbar') { - progressBar.progressMoved = true; - } else if (pref.seekbar === 'peakmeterbar') { - peakmeterBar.progressMoved = true; + if (grSet.seekbar === 'progressbar') { + grm.progBar.progressMoved = true; + } else if (grSet.seekbar === 'peakmeterbar') { + grm.peakBar.progressMoved = true; } - if (pref.displayLyrics) { - lyrics.seek(); + if (grm.ui.displayLyrics) { + grm.lyrics.seek(); } - else if (displayBiography) { - biography.on_playback_seek(); + else if (grm.ui.displayBiography) { + bio.call.on_playback_seek(); } on_playback_time(); - refreshSeekbar(); + grm.ui.refreshSeekbar(); } /** * Called when playback process is being initialized, on_playback_new_track should be called soon after this when first file is successfully opened for decoding. - * @param {number} cmd The command has following settings: + * @global + * @param {number} cmd - The command has following settings: * - 0 - Default. * - 1 - Play. * - 2 - Plays the next track from the current playlist according to the current playback order. @@ -1300,19 +1350,20 @@ function on_playback_seek() { * - 4 - settrack (internal fb2k value). * - 5 - Plays a random track from the current playlist. * - 6 - resume (internal fb2k value). - * @param {boolean} is_paused Whether the playback is paused. + * @param {boolean} is_paused - Whether the playback is paused. */ function on_playback_starting(cmd, is_paused) { - if (settings.hideCursor) { + if (grCfg.settings.hideCursor) { window.SetCursor(-1); // Hide cursor } - btnPlayPause(); + grm.button.lowerPlayPause(); } /** * Called when playback is stopped. - * @param {number} reason The playback stop has following settings: + * @global + * @param {number} reason - The playback stop has following settings: * - 0 - Invoked by user. * - 1 - End of file. * - 2 - Starting another track. @@ -1321,127 +1372,144 @@ function on_playback_starting(cmd, is_paused) { function on_playback_stop(reason) { if (reason !== 2) { // Clear all variables and repaint - str = clearUIVariables(); + grm.ui.clearUIVariables(); DebugLog('Repainting on_playback_stop:', reason); RepaintWindow(); - isPlayingCD = false; - isStreaming = false; - lastAlbumFolder = ''; - btns.playbackTime = ''; - lastAlbumDiscNumber = '0'; - recordLabels = []; - recordLabelsInverted = []; - btnPlayPause(); + grm.ui.isPlayingCD = false; + grm.ui.isStreaming = false; + grm.ui.lastAlbumFolder = ''; + grm.ui.btn.playbackTime = ''; + grm.ui.lastAlbumDiscNumber = '0'; + grm.ui.recordLabels = []; + grm.ui.recordLabelsInverted = []; + grm.button.lowerPlayPause(); // * Keep Reborn/Random colors when they are not too bright or too dark otherwise reset colors to default - if (['reborn', 'random'].includes(pref.theme) && ((colBrightness < 20 || imgBrightness < 20) || (colBrightness > 240 || imgBrightness > 240)) || - !['reborn', 'random'].includes(pref.theme) || pref.styleNighttime) { - setThemeColors(); - initTheme(); + if (['reborn', 'random'].includes(grSet.theme) && ((grCol.colBrightness < 20 || grCol.imgBrightness < 20) || (grCol.colBrightness > 240 || grCol.imgBrightness > 240)) || + !['reborn', 'random'].includes(grSet.theme) || grSet.styleNighttime) { + grm.color.setThemeColors(); + grm.ui.initTheme(); DebugLog('\n>>> initTheme -> on_playback_stop <<<\n'); } } - waveformBar.on_playback_stop(reason); + grm.waveBar.on_playback_stop(reason); - clearInterval(discArtRotationTimer); - clearInterval(progressBarTimer); - clearTimeout(albumArtTimeout); + clearInterval(grm.ui.discArtRotationTimer); + clearInterval(grm.ui.progressBarTimer); + clearTimeout(grm.ui.albumArtTimeout); - if (albumArt && ((pref.cycleArt && albumArtIndex !== 0) || lastAlbumFolder === '')) { + if (grm.ui.albumArt && ((grSet.cycleArt && grm.ui.albumArtIndex !== 0) || grm.ui.lastAlbumFolder === '')) { DebugLog('disposing artwork'); - albumArt = null; - albumArtScaled = null; + grm.ui.albumArt = null; + grm.ui.albumArtScaled = null; } - bandLogo = null; - invertedBandLogo = null; + grm.ui.bandLogo = null; + grm.ui.bandLogoInverted = null; - if (pref.displayLyrics && lyrics) { - lyrics.on_playback_stop(reason); + if (grm.ui.displayLyrics && grm.lyrics) { + grm.lyrics.on_playback_stop(reason); } - flagImgs = []; - discArtRotation = null; - discArtRotationCover = null; - albumArtTimeout = 0; + grm.ui.flagImgs = []; + grm.ui.discArtRotation = null; + grm.ui.discArtRotationCover = null; + grm.ui.albumArtTimeout = 0; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); } if (reason === 0 || reason === 1) { // Stop or end of playlist - discArt = disposeDiscArt(discArt); - discArtCover = disposeDiscArt(discArtCover); - discArtArray = []; // Clear Images - discArtArrayCover = []; // Clear Images + grm.ui.discArt = grm.ui.disposeDiscArt(grm.ui.discArt); + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArray = []; // Clear Images + grm.ui.discArtArrayCover = []; // Clear Images window.Repaint(); } - if (displayPlaylist || displayPlaylistArtwork) { - playlist.on_playback_stop(reason); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playback_stop'); + pl.call.on_playback_stop(reason); } - else if (displayLibrary) { - library.on_playback_stop(reason); + else if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_playback_stop'); + lib.call.on_playback_stop(reason); } - if (displayBiography) { - biography.on_playback_stop(reason); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playback_stop'); + bio.call.on_playback_stop(reason); } } +/** + * Refresh playback time plus playback time remaining every second. + * @global + */ +function on_playback_time() { + grStr.time = grSet.switchPlaybackTime ? $('-%playback_time_remaining%') : $('%playback_time%'); + grm.waveBar.on_playback_time(fb.PlaybackTime); + bio.call.on_playback_time(); +} + + /** * Called when clicking on playlist items that are visible in the playlist panel. - * @param {number} playlistIndex The index of the playlist. - * @param {number} playlistItemIndex The index of the playlist item. + * @global + * @param {number} playlistIndex - The index of the playlist. + * @param {number} playlistItemIndex - The index of the playlist item. */ function on_playlist_item_ensure_visible(playlistIndex, playlistItemIndex) { - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlist_item_ensure_visible'); - playlist.on_playlist_item_ensure_visible(playlistIndex, playlistItemIndex); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlist_item_ensure_visible'); + pl.call.on_playlist_item_ensure_visible(playlistIndex, playlistItemIndex); } } /** * Called when adding tracks to the playlist. - * @param {number} playlistIndex The index of the playlist. + * @global + * @param {number} playlistIndex - The index of the playlist. */ function on_playlist_items_added(playlistIndex) { - if (playlistHistory) { - playlistHistory.playlistAltered(PlaylistMutation.Added); + if (pl.history) { + pl.history.playlistAltered(PlaylistMutation.Added); } - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlist_items_added'); - playlist.on_playlist_items_added(playlistIndex); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlist_items_added'); + pl.call.on_playlist_items_added(playlistIndex); } - if (displayLibrary || displayLibrarySplit()) { - trace_call && console.log('Library => on_playlist_items_added'); - library.on_playlist_items_added(playlistIndex); + if (grm.ui.displayLibrary || grm.ui.displayLibrarySplit()) { + grm.ui.traceCall && console.log('Library => on_playlist_items_added'); + lib.call.on_playlist_items_added(playlistIndex); } - if (displayBiography) { - trace_call && console.log('Biography => on_playlist_items_added'); - biography.on_playlist_items_added(playlistIndex); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playlist_items_added'); + bio.call.on_playlist_items_added(playlistIndex); } } /** * Called when removing tracks from the playlist. - * @param {number} playlistIndex The index of the playlist. + * @global + * @param {number} playlistIndex - The index of the playlist. */ function on_playlist_items_removed(playlistIndex) { - if (playlistHistory) { - playlistHistory.playlistAltered(PlaylistMutation.Removed); + if (pl.history) { + pl.history.playlistAltered(PlaylistMutation.Removed); } - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlist_items_removed'); - playlist.on_playlist_items_removed(playlistIndex); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlist_items_removed'); + pl.call.on_playlist_items_removed(playlistIndex); } - if (displayLibrary) { - trace_call && console.log('Library => on_playlist_items_removed'); - library.on_playlist_items_removed(playlistIndex); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_playlist_items_removed'); + lib.call.on_playlist_items_removed(playlistIndex); } - if (displayBiography) { - trace_call && console.log('Biography => on_playlist_items_removed'); - biography.on_playlist_items_removed(playlistIndex); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playlist_items_removed'); + bio.call.on_playlist_items_removed(playlistIndex); } } @@ -1449,102 +1517,110 @@ function on_playlist_items_removed(playlistIndex) { /** * Called when reordering tracks in the playlist, i.e by dragging them up or down. * Changes selection too. Doesn't actually change the set of items that are selected or item having focus, just changes their order. - * @param {number} playlistIndex The index of the playlist. + * @global + * @param {number} playlistIndex - The index of the playlist. */ function on_playlist_items_reordered(playlistIndex) { - if (playlistHistory) { - playlistHistory.playlistAltered(PlaylistMutation.Reordered); + if (pl.history) { + pl.history.playlistAltered(PlaylistMutation.Reordered); } - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlist_items_reordered'); - playlist.on_playlist_items_reordered(playlistIndex); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlist_items_reordered'); + pl.call.on_playlist_items_reordered(playlistIndex); } - if (displayLibrary) { - trace_call && console.log('Library => on_playlist_items_reordered'); - library.on_playlist_items_reordered(playlistIndex); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_playlist_items_reordered'); + lib.call.on_playlist_items_reordered(playlistIndex); } } /** * Called as a workaround for some 3rd party playlist viewers not working with on_selection_changed. + * @global */ function on_playlist_items_selection_change() { - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlist_items_selection_change'); - playlist.on_playlist_items_selection_change(); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlist_items_selection_change'); + pl.call.on_playlist_items_selection_change(); } } /** * Called when switching the current active playlist to another. + * @global */ function on_playlist_switch() { - if (playlistHistory) { - playlistHistory.playlistAltered(PlaylistMutation.Switch); + if (pl.history) { + pl.history.playlistAltered(PlaylistMutation.Switch); } - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlist_switch'); - playlist.on_playlist_switch(); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlist_switch'); + pl.call.on_playlist_switch(); } - if (displayLibrary || ppt.libSource === 0) { - trace_call && console.log('Library => on_playlist_switch'); - library.on_playlist_switch(); + if (grm.ui.displayLibrary || libSet.libSource === 0) { + grm.ui.traceCall && console.log('Library => on_playlist_switch'); + lib.call.on_playlist_switch(); } - if (displayBiography) { - trace_call && console.log('Biography => on_playlist_switch'); - biography.on_playlist_switch(); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playlist_switch'); + bio.call.on_playlist_switch(); } } /** * Called when playlists are added/removed/reordered/renamed or a playlist's lock status changes. + * @global */ function on_playlists_changed() { - if (playlistHistory) { - playlistHistory.reset(); // When playlists are changed, indexes no longer apply, and so we have to wipe history + if (pl.history) { + pl.history.reset(); // When playlists are changed, indexes no longer apply, and so we have to wipe history } - if (displayPlaylist || displayPlaylistArtwork) { - trace_call && console.log('Playlist => on_playlists_changed'); - playlist.on_playlists_changed(); + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.ui.traceCall && console.log('Playlist => on_playlists_changed'); + pl.call.on_playlists_changed(); } - if (displayLibrary) { - trace_call && console.log('Library => on_playlists_changed'); - library.on_playlists_changed(); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_playlists_changed'); + lib.call.on_playlists_changed(); } - if (displayBiography) { - trace_call && console.log('Biography => on_playlists_changed'); - biography.on_playlists_changed(); + if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_playlists_changed'); + bio.call.on_playlists_changed(); } } /** * Called when script is reloaded via context menu > Reload or script is changed via panel menu > Configure or fb2k is exiting normally. + * @global */ function on_script_unload() { console.log('Unloading Script'); - waveformBar.on_script_unload(); + grm.waveBar.on_script_unload(); // It appears we don't need to dispose the images which we loaded using gdi.Image in their declaration for some reason. Attempting to dispose them causes a script error. - if (displayLibrary) { - library.on_script_unload(); + if (grm.ui.displayLibrary) { + grm.ui.traceCall && console.log('Library => on_script_unload'); + lib.call.on_script_unload(); } - else if (displayBiography) { - biography.on_script_unload(); + else if (grm.ui.displayBiography) { + grm.ui.traceCall && console.log('Biography => on_script_unload'); + bio.call.on_script_unload(); } } /** * Called when volume changes, i.e the volume bar in the volume button. - * @param {float} val The volume level in dB. Minimum is -100. Maximum is 0. + * @global + * @param {number} val - The volume level in dB. Minimum is -100. Maximum is 0. */ function on_volume_change(val) { - trace_call && console.log('Volume bar => on_volume_change'); - volumeBtn.on_volume_change(val); + grm.ui.traceCall && console.log('Volume bar => on_volume_change'); + grm.volBtn.on_volume_change(val); } @@ -1553,13 +1629,14 @@ function on_volume_change(val) { ////////////////////////// /** * Checks if the mouse is within the boundaries of the top menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInTopMenu(x, y) { - if (x < ww - SCALE(100) && y < geo.topMenuHeight) { - trace_call && trace_on_move && console.log('mouseInTopMenu'); + if (x < grm.ui.ww - SCALE(100) && y < grm.ui.topMenuHeight) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('mouseInTopMenu'); return true; } @@ -1569,27 +1646,28 @@ function mouseInTopMenu(x, y) { /** * Checks if the mouse is within the boundaries of the album art. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInAlbumArt(x, y) { const displayAlbumArt = - pref.layout === 'default' && !displayBiography && (displayPlaylist && !displayLibrary && pref.playlistLayout !== 'full' // Playlist + grSet.layout === 'default' && !grm.ui.displayBiography && (grm.ui.displayPlaylist && !grm.ui.displayLibrary && grSet.playlistLayout !== 'full' // Playlist || - displayLibrary && pref.libraryLayout === 'normal' && pref.libraryDesign !== 'flowMode' // Library + grm.ui.displayLibrary && grSet.libraryLayout === 'normal' && grSet.libraryDesign !== 'flowMode' // Library || - !displayPlaylist && !displayLibrary || pref.displayLyrics) // Details, Lyrics + !grm.ui.displayPlaylist && !grm.ui.displayLibrary || grm.ui.displayLyrics) // Details, Lyrics || - pref.layout === 'artwork' && !displayBiography && (displayPlaylist || !displayPlaylistArtwork && !displayLibrary); // Cover, Details, Lyrics + grSet.layout === 'artwork' && !grm.ui.displayBiography && (grm.ui.displayPlaylist || !grm.ui.displayPlaylistArtwork && !grm.ui.displayLibrary); // Cover, Details, Lyrics const albumArtBounds = - state.mouse_x > 0 && state.mouse_x <= ((isStreaming || !albumArt && noArtwork || albumArt) && (displayPlaylist || displayLibrary) && - pref.layout === 'default' ? pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : ww * 0.5 : !displayPlaylist && !displayLibrary ? ww : albumArtSize.w) && - state.mouse_y > albumArtSize.y && state.mouse_y <= albumArtSize.y + albumArtSize.h; + grm.ui.state.mouse_x > 0 && grm.ui.state.mouse_x <= ((grm.ui.isStreaming || !grm.ui.albumArt && grm.ui.noArtwork || grm.ui.albumArt) && (grm.ui.displayPlaylist || grm.ui.displayLibrary) && + grSet.layout === 'default' ? grSet.panelWidthAuto ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grm.ui.ww * 0.5 : !grm.ui.displayPlaylist && !grm.ui.displayLibrary ? grm.ui.ww : grm.ui.albumArtSize.w) && + grm.ui.state.mouse_y > grm.ui.albumArtSize.y && grm.ui.state.mouse_y <= grm.ui.albumArtSize.y + grm.ui.albumArtSize.h; if (displayAlbumArt && albumArtBounds) { - trace_call && trace_on_move && console.log('mouseInAlbumArt'); + grm.ui.traceCall && grm.ui.traceOnMove && console.log('mouseInAlbumArt'); return true; } @@ -1599,31 +1677,32 @@ function mouseInAlbumArt(x, y) { /** * Checks if the mouse can pause when clicking on the album art or noAlbumArtStub. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInPause(x, y) { // * Do not pause when Playlist/Library layout is in full width or when using Library's flow mode - const panelPlaylist = displayPlaylist && !displayLibrary && !displayBiography && pref.playlistLayout !== 'full'; - const panelDetails = !displayPlaylist && !displayLibrary && !displayBiography; - const panelLibrary = displayLibrary && pref.libraryLayout === 'normal' && pref.libraryDesign !== 'flowMode'; - const artworkLayout = pref.layout === 'artwork' && !displayPlaylistArtwork && !displayLibrary && !displayBiography; - - const albumArtBounds = albumArtSize.x <= x && albumArtSize.y <= y && albumArtSize.x + albumArtSize.w >= x && albumArtSize.y + albumArtSize.h >= y; - const discArtBounds = discArtSize.x <= x && discArtSize.y <= y && discArtSize.x + discArtSize.w >= x && discArtSize.y + discArtSize.h >= y; - const noAlbumArtBounds = state.mouse_x > 0 && state.mouse_x <= (displayPlaylist || displayLibrary ? albumArtSize.x + albumArtSize.w : !displayPlaylist || !displayLibrary ? ww : ww * 0.5) && - state.mouse_y > albumArtSize.y && state.mouse_y <= albumArtSize.h + geo.topMenuHeight; + const panelPlaylist = grm.ui.displayPlaylist && !grm.ui.displayLibrary && !grm.ui.displayBiography && grSet.playlistLayout !== 'full'; + const panelDetails = grm.ui.displayDetails; + const panelLibrary = grm.ui.displayLibrary && grSet.libraryLayout === 'normal' && grSet.libraryDesign !== 'flowMode'; + const artworkLayout = grSet.layout === 'artwork' && !grm.ui.displayPlaylistArtwork && !grm.ui.displayLibrary && !grm.ui.displayBiography; + + const albumArtBounds = grm.ui.albumArtSize.x <= x && grm.ui.albumArtSize.y <= y && grm.ui.albumArtSize.x + grm.ui.albumArtSize.w >= x && grm.ui.albumArtSize.y + grm.ui.albumArtSize.h >= y; + const discArtBounds = grm.ui.discArtSize.x <= x && grm.ui.discArtSize.y <= y && grm.ui.discArtSize.x + grm.ui.discArtSize.w >= x && grm.ui.discArtSize.y + grm.ui.discArtSize.h >= y; + const noAlbumArtBounds = grm.ui.state.mouse_x > 0 && grm.ui.state.mouse_x <= (grm.ui.displayPlaylist || grm.ui.displayLibrary ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : !grm.ui.displayPlaylist || !grm.ui.displayLibrary ? grm.ui.ww : grm.ui.ww * 0.5) && + grm.ui.state.mouse_y > grm.ui.albumArtSize.y && grm.ui.state.mouse_y <= grm.ui.albumArtSize.h + grm.ui.topMenuHeight; const pauseOnAlbumArt = - (pref.layout === 'default' && albumArt && (panelPlaylist || panelDetails || panelLibrary) || artworkLayout) && - !displayCustomThemeMenu && !displayMetadataGridMenu && albumArtBounds || discArt && !albumArt && discArtBounds; + (grSet.layout === 'default' && grm.ui.albumArt && (panelPlaylist || panelDetails || panelLibrary) || artworkLayout) && + !grm.ui.displayCustomThemeMenu && !grm.ui.displayMetadataGridMenu && albumArtBounds || grm.ui.discArt && !grm.ui.albumArt && discArtBounds; const pauseOnNoAlbumArt = - (pref.layout === 'default' && !albumArt && (panelPlaylist || panelDetails || panelLibrary) || artworkLayout) && - !displayCustomThemeMenu && !displayMetadataGridMenu && noAlbumArtBounds; + (grSet.layout === 'default' && !grm.ui.albumArt && (panelPlaylist || panelDetails || panelLibrary) || artworkLayout) && + !grm.ui.displayCustomThemeMenu && !grm.ui.displayMetadataGridMenu && noAlbumArtBounds; if (pauseOnAlbumArt || pauseOnNoAlbumArt) { - trace_call && trace_on_move && console.log('mouseInPause'); + grm.ui.traceCall && grm.ui.traceOnMove && console.log('mouseInPause'); return true; } @@ -1633,13 +1712,14 @@ function mouseInPause(x, y) { /** * Checks if the mouse is within the boundaries of the lower bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInLowerBar(x, y) { - if (y > wh - SCALE(120)) { - trace_call && trace_on_move && console.log('mouseInLowerBar'); + if (y > grm.ui.wh - SCALE(120)) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('mouseInLowerBar'); return true; } @@ -1649,19 +1729,20 @@ function mouseInLowerBar(x, y) { /** * Checks if the mouse is within the boundaries of the seekbar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInSeekbar(x, y) { - const seekX = x || state.mouse_x; - const seekY = y || state.mouse_y; - const pBar = pref.seekbar === 'progressbar'; - - if (seekX >= SCALE(40) && seekX < ww - SCALE(40) && - seekY >= wh - (pref.layout !== 'default' ? 0.6 : 0.5) * geo.lowerBarHeight - 0.5 * geo.progBarHeight && - seekY <= wh - (pref.layout !== 'default' ? SCALE(pBar ? 60 : 55) : SCALE(pBar ? 35 : 20))) { - trace_call && trace_on_move && console.log('mouseInSeekbar'); + const seekX = x || grm.ui.state.mouse_x; + const seekY = y || grm.ui.state.mouse_y; + const pBar = grSet.seekbar === 'progressbar'; + + if (seekX >= SCALE(40) && seekX < grm.ui.ww - SCALE(40) && + seekY >= grm.ui.wh - (grSet.layout !== 'default' ? 0.6 : 0.5) * grm.ui.lowerBarHeight - 0.5 * grm.ui.progressBarH && + seekY <= grm.ui.wh - (grSet.layout !== 'default' ? SCALE(pBar ? 60 : 55) : SCALE(pBar ? 35 : 20))) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('mouseInSeekbar'); return true; } @@ -1671,13 +1752,14 @@ function mouseInSeekbar(x, y) { /** * Checks if the mouse is within the boundaries of the panel. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInPanel(x, y) { - if (y < geo.topMenuHeight && y > wh - geo.topMenuHeight - geo.lowerBarHeight) { - trace_call && trace_on_move && console.log('mouseInPanel'); + if (y < grm.ui.topMenuHeight && y > grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('mouseInPanel'); return true; } @@ -1687,53 +1769,66 @@ function mouseInPanel(x, y) { /** * Checks if the mouse is within the boundaries of the Playlist. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInPlaylist(x, y) { - const plistX = x || state.mouse_x; - const plistY = y || state.mouse_y; + const plistX = x || grm.ui.state.mouse_x; + const plistY = y || grm.ui.state.mouse_y; - if (plistX >= playlist.x && plistX < playlist.x + playlist.w && - plistY >= (pref.layout !== 'default' ? playlist.y - SCALE(g_properties.row_h) : playlist.y) && plistY < playlist.y + playlist.h) { - trace_call && trace_on_move && console.log('Playlist => mouseInPlaylist'); + if (plistX >= pl.playlist.x && plistX < pl.playlist.x + pl.playlist.w && + plistY >= pl.playlist.y - SCALE(plSet.row_h) && plistY < pl.playlist.y + pl.playlist.h) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Playlist => mouseInPlaylist'); return true; } + if (pl.playlist.scrollbar.b_is_dragging) { + pl.playlist.scrollbar.b_is_dragging = false; + pl.playlist.scrollbar.desiredScrollPosition = undefined; + } + return false; } /** * Checks if the mouse is within the boundaries of the Library. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInLibrary(x, y) { - const libX = x || state.mouse_x; - const libY = y || state.mouse_y; + const libX = x || grm.ui.state.mouse_x; + const libY = y || grm.ui.state.mouse_y; - if (libX >= ui.x && libX < ui.x + ui.w && libY >= ui.y && libY < ui.y + ui.h) { - trace_call && trace_on_move && console.log('Library => mouseInLibrary'); + if (libX >= lib.ui.x && libX < lib.ui.x + lib.ui.w && libY >= lib.ui.y && libY < lib.ui.y + lib.ui.h) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Library => mouseInLibrary'); return true; } + if (lib.sbar.bar.isDragging) { + lib.sbar.bar.isDragging = false; + lib.but.Dn = false; + } + return false; } /** * Checks if the mouse is within the boundaries of the Library search. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInLibrarySearch(x, y) { - if (!but.Dn && x > ui.x + but.q.h + but.margin && x < panel.search.x + panel.search.w && - y > ui.y && y < ui.y + panel.search.h && ppt.searchShow) { - trace_call && trace_on_move && console.log('Library => mouseInLibrarySearch'); + if (!lib.but.Dn && x > lib.ui.x + lib.but.q.h + lib.but.margin && x < lib.panel.search.x + lib.panel.search.w && + y > lib.ui.y && y < lib.ui.y + lib.panel.search.h && libSet.searchShow) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Library => mouseInLibrarySearch'); return true; } @@ -1743,18 +1838,27 @@ function mouseInLibrarySearch(x, y) { /** * Checks if the mouse is within the boundaries of the Biography. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ function mouseInBiography(x, y) { - const bioX = x || state.mouse_x; - const bioY = y || state.mouse_y; + const bioX = x || grm.ui.state.mouse_x; + const bioY = y || grm.ui.state.mouse_y; - if (bioX >= uiBio.x && bioX < uiBio.x + uiBio.w && bioY >= uiBio.y && bioY < uiBio.y + uiBio.h) { - trace_call && trace_on_move && console.log('Biography => mouseInBiography'); + if (bioX >= bio.ui.x && bioX < bio.ui.x + bio.ui.w && bioY >= bio.ui.y && bioY < bio.ui.y + bio.ui.h) { + grm.ui.traceCall && grm.ui.traceOnMove && console.log('Biography => mouseInBiography'); return true; } + if (bio.alb_scrollbar.bar.isDragging || bio.art_scrollbar.bar.isDragging || bio.art_scroller.bar.isDragging || bio.cov_scroller.bar.isDragging) { + bio.alb_scrollbar.bar.isDragging = false; + bio.art_scrollbar.bar.isDragging = false; + bio.art_scroller.bar.isDragging = false; + bio.cov_scroller.bar.isDragging = false; + bio.but.Dn = false; + } + return false; } diff --git a/profile/georgia-reborn/scripts/Base/gr-color.js b/profile/georgia-reborn/scripts/Base/gr-color.js index 0b199560..55ead5ab 100644 --- a/profile/georgia-reborn/scripts/Base/gr-color.js +++ b/profile/georgia-reborn/scripts/Base/gr-color.js @@ -1,13 +1,13 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Color Definition * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Color Definition * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; @@ -17,96 +17,150 @@ // * COLOR DEFINITION * // ////////////////////////// /** - * Represents a color and provides methods for parsing and manipulating it. + * A class that handles a color and provides methods for parsing and manipulating it. * The value of the color can be in various formats such as RGB, HEX, HSL, HSV, or INT. */ class Color { /** + * Creates the `Color` instance. * The constructor parses the initial value and sets up the color object. - * @param {string|number} value The initial color value in one of the supported formats: + * @param {string|number} value - The initial color value in one of the supported formats: * - RGB: A string like "rgb(255, 0, 0)" or "rgba(255, 0, 0, 1)" * - HEX: A string like "#ff0000" or "ff0000" * - HSL: A string like "hsl(0, 100%, 50%)" or "hsla(0, 100%, 50%, 1)" * - HSV: A string like "hsv(0, 100%, 100%)" - * - INT: A number representing the color in integer format (0 - 16777215) + * - INT: A number representing the color in integer format (0 - 16777215). */ constructor(value) { - // * Events - this._decimal = 0; // 0 - 16777215 - this._hex = '#000000'; // #000000 - #FFFFFF - this._red = 0; // 0 - 255 - this._green = 0; // 0 - 255 - this._blue = 0; // 0 - 255 - this._hue = 0; // 0 - 360 - this._saturation = 0; // 0 - 100 - this._lightness = 0; // 0 - 100 - this._brightness = 0; // 0 - 100 - this._alpha = 255; // 0 - 255 + // * EVENTS * // + // #region EVENTS + /** @private @type {number} 0 - 16777215. */ + this._decimal = 0; + /** @private @type {string} #000000 - #FFFFFF5. */ + this._hex = '#000000'; + /** @private @type {number} 0 - 255. */ + this._red = 0; + /** @private @type {number} 0 - 255. */ + this._green = 0; + /** @private @type {number} 0 - 255. */ + this._blue = 0; + /** @private @type {number} 0 - 360. */ + this._hue = 0; + /** @private @type {number} 0 - 100. */ + this._saturation = 0; + /** @private @type {number} 0 - 100. */ + this._lightness = 0; + /** @private @type {number} 0 - 100. */ + this._brightness = 0; + /** @private @type {number} 0 - 255. */ + this._alpha = 255; + /** @private @type {object} */ this._listeners = {}; + // #endregion - // * Event subscription - this.subscribe(Color.Events.RGB_UPDATED, this._RGBUpdated); - this.subscribe(Color.Events.HEX_UPDATED, this._HEXUpdated); - this.subscribe(Color.Events.HSL_UPDATED, this._HSLUpdated); - this.subscribe(Color.Events.HSV_UPDATED, this._HSVUpdated); - this.subscribe(Color.Events.INT_UPDATED, this._INTUpdated); - - // * Patterns + // * PATTERNS * // + // #region PATTERNS + /** + * Regular expression to test for a valid hex color. + * @type {RegExp} + * @private + */ this.isHex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i; + + /** + * Regular expression to test for a valid HSL(A) color. + * @type {RegExp} + * @private + */ this.isHSL = /^hsla?\((\d{1,3}?),\s*(\d{1,3}%),\s*(\d{1,3}%)(,\s*[01]?\.?\d*)?\)$/; + + /** + * Regular expression to test for a valid RGB(A) color. + * @type {RegExp} + * @private + */ this.isRGB = /^rgba?\((\d{1,3}%?),\s*(\d{1,3}%?),\s*(\d{1,3}%?)(,\s*[01]?\.?\d*)?\)$/; + + /** + * Regular expression to test for a value with a percentage. + * @type {RegExp} + * @private + */ this.isPercent = /^\d+(\.\d+)*%$/; + + /** + * Regular expression to match individual hex digits. + * @type {RegExp} + * @private + */ this.hexBit = /([0-9a-f])/gi; + + /** + * Regular expression to match the leading hash symbol in hex colors. + * @type {RegExp} + * @private + */ this.leadHex = /^#/; + + /** + * Regular expression to test and capture groups in HSL(A) strings. + * @type {RegExp} + * @private + */ this.matchHSL = /^hsla?\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%(,\s*([01]?\.?\d*))?\)$/; + + /** + * Regular expression to test and capture groups in RGB(A) strings. + * @type {RegExp} + * @private + */ this.matchRGB = /^rgba?\((\d{1,3}%?),\s*(\d{1,3}%?),\s*(\d{1,3}%?)(,\s*([01]?\.?\d*))?\)$/; + // #endregion - // * Helpers + // * HELPERS * // + // #region HELPERS /** * Rounds a number to the nearest integer using the "round half up" method. - * @param {number} number The number that will be round to the nearest whole number (integer) using the absolute value method. + * @param {number} number - The number that will be round to the nearest whole number (integer) using the absolute value method. + * @returns {number} The number rounded to the nearest integer. + * @private */ this.AbsRound = (number) => (0.5 + number) << 0; /** * Takes a number as input and returns a string representation of that number with leading zeroes added if necessary. - * @param {number} num The number that we want to pad with zeroes. + * @param {number} num - The number that we want to pad with zeroes. + * @returns {string} The number padded with leading zeroes, resulting in a string of at least 3 characters width. + * @private */ this.PadZeroes = (num) => (` ${num}`).substr(-3, 3); - /** - * Converts a hue value to its corresponding RGB value. - * http://www.w3.org/TR/css3-color/#hsl-color - * @param {number} a The starting value of the color component (e.g., red, green, or blue) in the RGB color model. - * @param {number} b The middle value in the range of colors. It is used to calculate the RGB value based on the given hue value. - * @param {number} c The hue value in the HSL color model. It is a value between 0 and 1, where 0 represents red, 1/3 represents green, and 2/3 represents blue. - * @returns {number} The specific value that is returned depends on the value of `c` and follows a series of conditional statements. - */ - this.HUEtoRGB = (a, b, c) => { - if (c < 0) c++; - if (c > 1) c--; - if (c < 1 / 6) return a + (b - a) * 6 * c; - if (c < 1 / 2) return b; - if (c < 2 / 3) return a + (b - a) * (2 / 3 - c) * 6; - return a; - }; - /** * Converts a percentage value to a value between 0 and 255. - * @param {number} p The percentage number. + * @param {number} p - The percentage number. + * @returns {number} The corresponding value between 0 and 255 if the input is a percentage, otherwise the parsed integer value of the input. + * @private */ this.PerToVal = (p) => this.isPercent.test(p) ? this.AbsRound(parseInt(p) * 2.55) : parseInt(p); + // #endregion - // * Parse the initial value - this.parse(value); + // * INITIALIZATION * // + // #region INITIALIZATION + this.subscribe(Color.Events.RGB_UPDATED, this._RGBUpdated); + this.subscribe(Color.Events.HEX_UPDATED, this._HEXUpdated); + this.subscribe(Color.Events.HSL_UPDATED, this._HSLUpdated); + this.subscribe(Color.Events.HSV_UPDATED, this._HSVUpdated); + this.subscribe(Color.Events.INT_UPDATED, this._INTUpdated); + this.parse(value); // Parse the initial value + // #endregion } - // * STATICS * // - + // * STATIC METHODS * // + // #region STATIC METHODS /** * A static property that provides an enumeration of event names used by the Color class. * This allows for consistent naming of events throughout the application. - * @returns {Object} An object containing event name constants. + * @returns {object} An object containing event name constants. * @example * Assuming Color is a class that emits events and has an 'on' method for event listening. * colorInstance.on(Color.Events.RGB_UPDATED, (newColor) => { @@ -134,9 +188,10 @@ class Color { static random() { return new Color(this.AbsRound(Math.random() * 16777215)); } + // #endregion - // * GETTERS * // - + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS /** * Gets the red component value of the color. * @returns {number} The red component value, between 0 and 255. @@ -202,29 +257,29 @@ class Color { } /** - * Checks if the color is greyscale (all RGB values are identical). - * @returns {boolean} `true` if the color is greyscale, otherwise `false`. + * Checks if the color is grayscale (all RGB values are identical). + * @returns {boolean} `true` if the color is grayscale, otherwise `false`. */ - get isGreyscale() { + get isGrayscale() { return this._red === this._green && this._red === this._blue; } /** - * Checks if a color is almost greyscale by determining if each RGB component is within a + * Checks if a color is almost grayscale by determining if each RGB component is within a * specific threshold from the average RGB value. - * @returns {boolean} `true` if the color is close to greyscale, otherwise `false`. + * @returns {boolean} `true` if the color is close to grayscale, otherwise `false`. * @example * Assuming Color is a class that takes three arguments: red, green, and blue. * const color = new Color(100, 102, 104); - * console.log(color.isCloseToGreyscale); // => true + * console.log(color.isCloseToGrayscale); // => true * * const color2 = new Color(100, 100, 120); - * console.log(color2.isCloseToGreyscale); // => false + * console.log(color2.isCloseToGrayscale); // => false */ - get isCloseToGreyscale() { + get isCloseToGrayscale() { const threshold = 6; const avg = Math.round((this._red + this._green + this._blue) / 3); - return this.isGreyscale || + return this.isGrayscale || (Math.abs(this._red - avg) < threshold && Math.abs(this._green - avg) < threshold && Math.abs(this._blue - avg) < threshold); @@ -247,11 +302,9 @@ class Color { return this._decimal; } - // * SETTERS * // - /** * Sets the hue component value of the color, updates all other components based on the new hue, and dispatches an event to notify about the update. - * @param {number} value The hue component value to set, in the range of 0 to 360. + * @param {number} value - The hue component value to set, in the range of 0 to 360. * @example * const color = new Color(); * color.hue = 280; // sets the hue to 280 degrees @@ -262,7 +315,7 @@ class Color { /** * Sets the saturation component value of the color, updates all other components based on the new saturation, and dispatches an event to notify about the update. - * @param {number} value The saturation component value to set, in the range of 0 to 100. + * @param {number} value - The saturation component value to set, in the range of 0 to 100. * @example * const color = new Color(); * color.saturation = 50; // sets the saturation to 50% @@ -273,7 +326,7 @@ class Color { /** * Sets the lightness component value of the color, updates all other components based on the new lightness, and dispatches an event to notify about the update. - * @param {number} value The lightness component value to set, in the range of 0 to 100. + * @param {number} value - The lightness component value to set, in the range of 0 to 100. * @example * const color = new Color(); * color.lightness = 80; // sets the lightness to 80% @@ -281,9 +334,10 @@ class Color { set lightness(value) { this._handle('_lightness', value, Color.Events.HSL_UPDATED); } + // #endregion // * PRIVATE METHODS * // - + // #region PRIVATE METHODS /** * Converts RGB color values to HSL (Hue, Saturation, Lightness) and updates the instance properties. * @@ -298,16 +352,14 @@ class Color { * * The conversion accounts for cases where the RGB values are equal (achromatic colors), in which case saturation is set to 0, * and hue is set arbitrarily to 0 as it is undefined for achromatic colors. - * + * @property {number} _red - The internal red value used for conversion. + * @property {number} _green - The internal green value used for conversion. + * @property {number} _blue - The internal blue value used for conversion. + * @property {number} _hue - The internal property set to the calculated hue value. + * @property {number} _saturation - The internal property set to the calculated saturation value. + * @property {number} _lightness - The internal property set to the calculated lightness value. + * @property {number} _brightness - The internal property set to the calculated brightness value. * @private - * @method _RGB2HSL - * @property {_red} Internal red value used for conversion. - * @property {_green} Internal green value used for conversion. - * @property {_blue} Internal blue value used for conversion. - * @property {_hue} Internal property set to the calculated hue value. - * @property {_saturation} Internal property set to the calculated saturation value. - * @property {_lightness} Internal property set to the calculated lightness value. - * @property {_brightness} Internal property set to the calculated brightness value. * @see AbsRound */ _RGB2HSL() { @@ -348,19 +400,18 @@ class Color { * * Assumes instance properties `_hue`, `_saturation`, and `_lightness` are defined: * - `_hue`: Range of 0 to 360 degrees - * - `_saturation` and `_lightness`: Percentage from 0 to 100 + * - `_saturation` and `_lightness`: Percentage from 0 to 100. * * The RGB values are in the range of 0 to 255. * No value is returned. - * + * @property {number} _hue - The internal hue value used for conversion. + * @property {number} _saturation - The internal saturation value used for conversion. + * @property {number} _lightness - The internal lightness value used for conversion. + * @property {number} _red - The internal property set after conversion to the red color component. + * @property {number} _green - The internal property set after conversion to the green color component. + * @property {number} _blue - The internal property set after conversion to the blue color component. * @private - * @property {_hue} Internal hue value used for conversion. - * @property {_saturation} Internal saturation value used for conversion. - * @property {_lightness} Internal lightness value used for conversion. - * @property {_red} Internal property set after conversion. - * @property {_green} Internal property set after conversion. - * @property {_blue} Internal property set after conversion. - * @see HUEtoRGB + * @see _HUEtoRGB * @see AbsRound */ _HSL2RGB() { @@ -369,9 +420,9 @@ class Color { const l = this._lightness / 100; const q = l < 0.5 ? l * (1 + s) : (l + s - l * s); const p = 2 * l - q; - this._red = this.AbsRound(this.HUEtoRGB(p, q, h + 1 / 3) * 255); - this._green = this.AbsRound(this.HUEtoRGB(p, q, h) * 255); - this._blue = this.AbsRound(this.HUEtoRGB(p, q, h - 1 / 3) * 255); + this._red = this.AbsRound(this._HUEtoRGB(p, q, h + 1 / 3) * 255); + this._green = this.AbsRound(this._HUEtoRGB(p, q, h) * 255); + this._blue = this.AbsRound(this._HUEtoRGB(p, q, h - 1 / 3) * 255); } /** @@ -380,20 +431,19 @@ class Color { * * Assumes instance properties `_hue`, `_saturation`, and `_brightness` are defined: * - `_hue`: Range of 0 to 360 degrees - * - `_saturation` and `_brightness`: Percentage from 0 to 100 + * - `_saturation` and `_brightness`: Percentage from 0 to 100. * * The RGB values are in the range of 0 to 255. * No value is returned. * * The conversion algorithm is sector-based, typical for HSV to RGB conversion. - * + * @property {number} _hue - Internal hue value used for conversion. + * @property {number} _saturation - Internal saturation value used for conversion. + * @property {number} _brightness - Internal brightness value used for conversion. + * @property {number} _red - Internal property set after conversion to the red color component. + * @property {number} _green - Internal property set after conversion to the green color component. + * @property {number} _blue - Internal property set after conversion to the blue color component. * @private - * @property {_hue} Internal hue value used for conversion. - * @property {_saturation} Internal saturation value used for conversion. - * @property {_brightness} Internal brightness value used for conversion. - * @property {_red} Internal property set after conversion. - * @property {_green} Internal property set after conversion. - * @property {_blue} Internal property set after conversion. * @see AbsRound * @see {@link http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript|Conversion algorithm} */ @@ -422,6 +472,24 @@ class Color { this._blue = this.AbsRound(b * 255); } + /** + * Converts a hue value to its corresponding RGB value. + * Http://www.w3.org/TR/css3-color/#hsl-color. + * @param {number} a - The starting value of the color component (e.g., red, green, or blue) in the RGB color model. + * @param {number} b - The middle value in the range of colors. It is used to calculate the RGB value based on the given hue value. + * @param {number} c - The hue value in the HSL color model. It is a value between 0 and 1, where 0 represents red, 1/3 represents green, and 2/3 represents blue. + * @returns {number} The specific value that is returned depends on the value of `c` and follows a series of conditional statements. + * @private + */ + _HUEtoRGB(a, b, c) { + if (c < 0) c++; + if (c > 1) c--; + if (c < 1 / 6) return a + (b - a) * 6 * c; + if (c < 1 / 2) return b; + if (c < 2 / 3) return a + (b - a) * (2 / 3 - c) * 6; + return a; + } + /** * Converts the internal integer (decimal) representation of the color, stored in `_decimal`, to a HEX string. * Updates the `_hex` property with the resulting HEX string prefixed with '#'. @@ -527,9 +595,9 @@ class Color { * Handles the property update by setting the new value if it differs from the current one. * Triggers a specific event broadcast if the event name is provided and the value has changed. * Always broadcasts an 'updated' event regardless of the change in value. - * @param {string} prop The name of the property to handle. - * @param {*} value The new value to set for the property. - * @param {string} [event] The name of the event to broadcast on change. + * @param {string} prop - The name of the property to handle. + * @param {*} value - The new value to set for the property. + * @param {string} [event] - The name of the event to broadcast on change. * @returns {*} The updated value of the property if it was changed, otherwise the original value. * @private */ @@ -548,16 +616,17 @@ class Color { /** * Checks if the object has subscribers for a given event type. - * @param {string} type The event type to check for subscribers. + * @param {string} type - The event type to check for subscribers. * @returns {boolean} True if there are subscribers for the event type, false otherwise. * @private */ _isSubscribed(type) { return this._listeners[type] != null; } + // #endregion - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Returns a CSS-formatted hex string (e.g., "#FF9900") from the Color's component values. * @returns {string} Hexadecimal color string representing the Color object's current state. @@ -568,8 +637,8 @@ class Color { /** * Returns a CSS-formatted RGB or RGBA string from the Color's component values. - * @param {boolean} [showPrefix=true] If true, includes 'rgb' or 'rgba' as prefix in the string. - * @param {boolean} [threeDigitColors=false] If true, pads the color component values with zeroes to have three digits. + * @param {boolean} [showPrefix] - If true, includes 'rgb' or 'rgba' as prefix in the string. + * @param {boolean} [threeDigitColors] - If true, pads the color component values with zeroes to have three digits. * @returns {string} The CSS-formatted RGB or RGBA color string. * @example * Assuming the color instance has _red=255, _green=153, _blue=0, _alpha=0.5 @@ -629,7 +698,7 @@ class Color { /** * Sets the hex value of the color, updates all other components, and dispatches Event.HEX_UPDATED. - * @param {string} value The hex value to be set. + * @param {string} value - The hex value to be set. * @returns {Color} This Color instance for method chaining. * @fires Color#event:HEX_UPDATED When the hex value is updated. * @example @@ -643,7 +712,7 @@ class Color { /** * Sets the red component value of the color, updates all other components, and dispatches Event.RGB_UPDATED. - * @param {number} value The red component value to set (0 - 255). + * @param {number} value - The red component value to set (0 - 255). * @returns {Color} This Color instance for method chaining. * @fires Color#event:RGB_UPDATED When the red component is updated. * @example @@ -656,7 +725,7 @@ class Color { /** * Sets the green component value of the color, updates all other components, and dispatches Event.RGB_UPDATED. - * @param {number} value The green component value to set (0 - 255). + * @param {number} value - The green component value to set (0 - 255). * @returns {Color} This Color instance for method chaining. * @fires Color#event:RGB_UPDATED When the green component is updated. * @example @@ -669,7 +738,7 @@ class Color { /** * Sets the blue component value of the color, updates all other components, and dispatches Event.RGB_UPDATED. - * @param {number} value The blue component value to set (0 - 255). + * @param {number} value - The blue component value to set (0 - 255). * @returns {Color} This Color instance for method chaining. * @fires Color#event:RGB_UPDATED When the blue component is updated. * @example @@ -682,7 +751,7 @@ class Color { /** * Sets the brightness component value of the color, updates all other components, and dispatches Event.HSV_UPDATED. - * @param {number} value The brightness component value to set (0 - 100). + * @param {number} value - The brightness component value to set (0 - 100). * @returns {Color} This Color instance for method chaining. * @fires Color#event:HSV_UPDATED When the brightness component is updated. * @example @@ -695,7 +764,7 @@ class Color { /** * Sets the opacity value of the color, updates all other components, and dispatches Event.UPDATED. - * @param {number} [value=1] The opacity component value to set (0 - 1). Defaults to 1 if not specified. + * @param {number} [value] - The opacity component value to set (0 - 1). Defaults to 1 if not specified. * @returns {Color} This Color instance for method chaining. * @fires Color#event:UPDATED When the opacity is updated. * @example @@ -708,7 +777,7 @@ class Color { /** * Parses a mixed variable and adopts its properties into the current Color instance. The value can be any CSS color value, a hash of properties, another Color instance, a numeric value, or a named CSS color. - * @param {*} value A CSS color string, object with color properties, another Color instance, or a numeric color value. If undefined, the method will return the current instance without parsing. + * @param {*} value - A CSS color string, object with color properties, another Color instance, or a numeric color value. If undefined, the method will return the current instance without parsing. * @returns {Color} This Color instance for method chaining. * @fires Color#event:PARSED When the color is successfully parsed. The PARSED event is emitted with the new color value. * @example @@ -793,7 +862,7 @@ class Color { /** * Copies the color properties from another Color object to this one. - * @param {Color} color The Color object to copy properties from. + * @param {Color} color - The Color object to copy properties from. * @returns {Color} The current Color object after copying the color and alpha values. */ copy(color) { @@ -805,8 +874,8 @@ class Color { * If a single numeric value is provided, it sets the decimal color value. * If an object is provided, each key-value pair is applied to the corresponding color component. * If a key and value are provided, it sets the specified component to the given value. - * @param {string|object|number} key The name of the color component to set, a hash of key-value pairs for multiple components, or a single numeric value representing the color. - * @param {?string|number} [value] The value of the color component to be set. This parameter is ignored if key is an object or number. + * @param {string|object|number} key - The name of the color component to set, a hash of key-value pairs for multiple components, or a single numeric value representing the color. + * @param {?string|number} [value] - The value of the color component to be set. This parameter is ignored if key is an object or number. * @returns {Color} The Color instance for method chaining. * @example * const color = new Color(); @@ -833,8 +902,8 @@ class Color { /** * Modifies the invoking Color instance by interpolating its component values between the original color and the destination color based on the provided factor. - * @param {Color} destination The Color instance toward which the interpolation occurs. - * @param {number} factor A float between 0 and 1, where 0 is the original Color, 1 is the destination Color, and 0.5 represents the midpoint. This method blends the colors accordingly. + * @param {Color} destination - The Color instance toward which the interpolation occurs. + * @param {number} factor - A float between 0 and 1, where 0 is the original Color, 1 is the destination Color, and 0.5 represents the midpoint. This method blends the colors accordingly. * @returns {Color} The Color instance after interpolation, for method chaining. * @example * const orange = new Color('#FF9900'); @@ -878,8 +947,8 @@ class Color { * - `v` for brightness component (in HSV) * - `a` for alpha component (transparency) * - `x` for hexadecimal color representation - * - `d` for decimal color representation - * @param {string} string The string with tokens to be replaced by color values. + * - `d` for decimal color representation. + * @param {string} string - The string with tokens to be replaced by color values. * @returns {string} The formatted string with all tokens replaced by their corresponding values. */ format(string) { @@ -932,8 +1001,8 @@ class Color { /** * Broadcasts an event to all subscribed listeners with optional parameters. - * @param {string} type The event type to broadcast. - * @param {Array} [params] An array of parameters to pass to each of the event listeners. + * @param {string} type - The event type to broadcast. + * @param {Array} [params] - An array of parameters to pass to each of the event listeners. * @example * Suppose some listeners have been added to 'update' event * color.broadcast('update', [newColorValue]); @@ -951,8 +1020,8 @@ class Color { /** * Subscribes a new listener to a specific event type. - * @param {string} type The event type for which to register the listener. - * @param {function} callback The callback function that will be invoked when the event is broadcasted. + * @param {string} type - The event type for which to register the listener. + * @param {Function} callback - The callback function that will be invoked when the event is broadcasted. * @example * To listen for a color update event * color.subscribe('update', function(updatedColor) { @@ -969,8 +1038,9 @@ class Color { /** * Unsubscribes a previously registered listener from a specific event type. * If the callback is not found, no action is taken. - * @param {string} type The event type to unsubscribe from. - * @param {function} callback The callback function to unregister from the event. + * @param {string} type - The event type to unsubscribe from. + * @param {Function} callback - The callback function to unregister from the event. + * @returns {void|this} If the callback is found and removed, the method returns the instance for chaining. * @example * To remove a previously added listener from the 'update' event * color.unsubscribe('update', updateListener); @@ -987,4 +1057,5 @@ class Color { } } } + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-common.js b/profile/georgia-reborn/scripts/Base/gr-common.js index 8cdddb8a..25a21a1f 100644 --- a/profile/georgia-reborn/scripts/Base/gr-common.js +++ b/profile/georgia-reborn/scripts/Base/gr-common.js @@ -1,40 +1,139 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Common * // -// * Author: TT * // -// * Org. Author: TheQwertiest * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Common * // +// * Author: TT * // +// * Org. Author: TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; +///////////////////////// +// * ACTIVEX OBJECTS * // +///////////////////////// +/** @global @type {ActiveXObject} */ +const app = new ActiveXObject('Shell.Application'); +/** @global @type {ActiveXObject} */ +const esl = new ActiveXObject('eslyric'); +/** @global @type {ActiveXObject} */ +const doc = new ActiveXObject('htmlfile'); +/** @global @type {ActiveXObject} */ +const fso = new ActiveXObject('Scripting.FileSystemObject'); +/** @global @type {ActiveXObject} */ +const UIHacks = new ActiveXObject('UIHacks'); +/** @global @type {ActiveXObject} */ +const vb = new ActiveXObject('ScriptControl'); +/** @global @type {ActiveXObject} */ +const WshShell = new ActiveXObject('WScript.Shell'); + + +//////////////////// +// * COMPONENTS * // +//////////////////// +/** + * A set of boolean flags indicating the presence of specific components. + * Each flag is set based on the result of a check performed at the time of initialization. + * @global + * @enum {boolean} + */ +const Component = { + /** Indicates if the foo_chronflow component or its mod version is installed. */ + ChronFlow: utils.CheckComponent('foo_chronflow') || utils.CheckComponent('foo_chronflow_mod'), + /** Indicates if the foo_enhanced_playcount component is installed. */ + EnhancedPlaycount: utils.CheckComponent('foo_enhanced_playcount'), + /** Indicates if the foo_uie_eslyric component is installed. */ + ESLyric: utils.CheckComponent('foo_uie_eslyric'), + /** Indicates if the foo_vis_vumeter component is installed. */ + VUMeter: utils.CheckComponent('foo_vis_vumeter') +}; + + +/////////////////////// +// * COMPATIBILITY * // +/////////////////////// +/** + * A set of boolean flags indicating various environment conditions. + * @global + * @enum {boolean} + */ +const Detect = { + /** Indicates if user has Internet Explorer installed, needed to render HTML popups. */ + IE: DetectIE(), + /** Indicates if the user's system is running on Windows 64 bit. */ + Win64: DetectWin64(), + /** Indicates if the user's system is running Wine on Linux or macOS. */ + Wine: DetectWine() +}; + + +/////////////////////// +// * CONFIGURATION * // +/////////////////////// +/** + * A set of configuration type settings. + * @global + * @enum {string} + */ +const ConfigurationObjectType = { + Array: 'array', + Object: 'object', + Value: 'value' // Not currently handled +}; + + +///////////////// +// * DISPLAY * // +///////////////// +/** + * A set of display resolution state settings, initially set to false. + * These states can be modified at runtime to reflect the current display settings. + * @global + * @enum {boolean} + */ +const RES = { + /** 4K resolution state. */ + _4K: false, + + /** Quad HD resolution state. */ + _QHD: false, + + /** High Definition resolution state. */ + _HD: false +}; + + /////////////////// // * RENDERING * // /////////////////// /** - * A set of several different quality type settings for text rendering. - * Used in SetTextRenderingHint(). - * For more information, see: http://msdn.microsoft.com/en-us/library/ms534404(VS.85).aspx + * A set of several different quality type settings for interpolation of image processing when resizing or transforming. + * Used in SetInterpolationMode(). + * For more information, see: http://msdn.microsoft.com/en-us/library/ms534141(VS.85).aspx. + * @global * @enum {number} */ -const TextRenderingHint = { - SystemDefault: 0, - SingleBitPerPixelGridFit: 1, - SingleBitPerPixel: 2, - AntiAliasGridFit: 3, - AntiAlias: 4, - ClearTypeGridFit: 5 +const InterpolationMode = { + Invalid: -1, + Default: 0, + LowQuality: 1, + HighQuality: 2, + Bilinear: 3, + Bicubic: 4, + NearestNeighbor: 5, + HighQualityBilinear: 6, + HighQualityBicubic: 7 // Highest quality }; /** * A set of several different quality type settings for anti-aliasing that is applied to the edges of lines and curves. * Used in SetSmoothingMode(). - * For more information, see: http://msdn.microsoft.com/en-us/library/ms534173(VS.85).aspx + * For more information, see: http://msdn.microsoft.com/en-us/library/ms534173(VS.85).aspx. + * @global * @enum {number} */ const SmoothingMode = { @@ -47,29 +146,41 @@ const SmoothingMode = { }; /** - * A set of several different quality type settings for interpolation of image processing when resizing or transforming. - * Used in SetInterpolationMode(). - * For more information, see: http://msdn.microsoft.com/en-us/library/ms534141(VS.85).aspx + * A set of several different quality type settings for text rendering. + * Used in SetTextRenderingHint(). + * For more information, see: http://msdn.microsoft.com/en-us/library/ms534404(VS.85).aspx. + * @global * @enum {number} */ -const InterpolationMode = { - Invalid: -1, - Default: 0, - LowQuality: 1, - HighQuality: 2, - Bilinear: 3, - Bicubic: 4, - NearestNeighbor: 5, - HighQualityBilinear: 6, - HighQualityBicubic: 7 // Highest quality +const TextRenderingHint = { + SystemDefault: 0, + SingleBitPerPixelGridFit: 1, + SingleBitPerPixel: 2, + AntiAliasGridFit: 3, + AntiAlias: 4, + ClearTypeGridFit: 5 }; ////////////////// // * GRAPHICS * // ////////////////// +/** + * A set of foobar's album art ID settings used to identify which album art image to load. + * @global + * @enum {number} + */ +const AlbumArtId = { + front: 0, + back: 1, + disc: 2, + icon: 3, + artist: 4 +}; + /** * A set of different text alignment and formatting settings. + * @global * @enum {number} */ const DrawText = { @@ -86,9 +197,10 @@ const DrawText = { /** * A set of text formatting settings used in gr.DrawString() or gr.GdiDrawText(). + * @global * @enum {number} */ -const g_string_format = { +const Stringformat = { h_align_near: 0x00000000, h_align_center: 0x10000000, h_align_far: 0x20000000, @@ -119,9 +231,10 @@ const g_string_format = { /** * A set of font style settings used when creating font objects. + * @global * @enum {number} */ -const g_font_style = { +const FontStyle = { regular: 0, bold: 1, italic: 2, @@ -132,9 +245,10 @@ const g_font_style = { /** * A set of font mapping settings for the 'Guifx v2 Transports' font used for button symbols. + * @global * @enum {string|number} */ -const g_guifx = { +const Guifx = { name: 'Guifx v2 Transports', play: 1, pause: 2, @@ -196,24 +310,37 @@ const g_guifx = { police: 'p' }; + +/////////////////////// +// * FILE SETTINGS * // +/////////////////////// /** - * A set of foobar's album art ID settings used to identify which album art image to load. + * A set of file attribute settings that specifies the attributes of a file, used with utils.Glob(). + * For more information, see: http://msdn.microsoft.com/en-us/library/ee332330%28VS.85%29.aspx. + * @global * @enum {number} */ -const g_album_art_id = { - front: 0, - back: 1, - disc: 2, - icon: 3, - artist: 4 +const FileAttributes = { + ReadOnly: 0x00000001, + Hidden: 0x00000002, + System: 0x00000004, + Directory: 0x00000010, + Archive: 0x00000020, + // Device: 0x00000040, // ! Do Not Use + Normal: 0x00000080, + Temporary: 0x00000100, + SparseFile: 0x00000200, + ReparsePoint: 0x00000400, + Compressed: 0x00000800, + Offline: 0x00001000, + NotContentIndexed: 0x00002000, + Encrypted: 0x00004000 + // Virtual: 0x00010000; // ! Do not use }; - -/////////////////////// -// * FILE SETTINGS * // -/////////////////////// /** * A set of file mode settings that specifies how the operating system should open a file. + * @global * @enum {number} */ const FileMode = { @@ -224,6 +351,7 @@ const FileMode = { /** * A set of file type settings that specifies the file format. + * @global * @enum {number} */ const FileType = { @@ -232,35 +360,35 @@ const FileType = { Ascii: 0 }; + +//////////////// +// * STATES * // +//////////////// /** - * A set of file attribute settings that specifies the attributes of a file, used with utils.Glob(). - * For more information, see: http://msdn.microsoft.com/en-us/library/ee332330%28VS.85%29.aspx + * A set of all button state settings. + * @global * @enum {number} */ -const FileAttributes = { - ReadOnly: 0x00000001, - Hidden: 0x00000002, - System: 0x00000004, - Directory: 0x00000010, - Archive: 0x00000020, - // Device: 0x00000040, // ! Do Not Use - Normal: 0x00000080, - Temporary: 0x00000100, - SparseFile: 0x00000200, - ReparsePoint: 0x00000400, - Compressed: 0x00000800, - Offline: 0x00001000, - NotContentIndexed: 0x00002000, - Encrypted: 0x00004000 - // Virtual: 0x00010000; // ! Do not use +const ButtonState = { + Default: 0, + Hovered: 1, + Down: 2, + Enabled: 3 }; +/** + * A set of all hyperlink state settings. + * @global + * @enum {number} + */ +const HyperlinkStates = { + Normal: 0, + Hovered: 1 +}; -//////////////// -// * STATES * // -//////////////// /** * A set of all available foobar playback order state settings. + * @global * @enum {number} */ const PlaybackOrder = { @@ -279,6 +407,7 @@ const PlaybackOrder = { ///////////////// /** * A set of UIHacks main menu state settings. + * @global * @enum {number} */ const MainMenuState = { @@ -287,8 +416,20 @@ const MainMenuState = { Auto: 2 }; +/** + * A set of UIHacks window state settings. + * @global + * @enum {number} + */ +const WindowState = { + Normal: 0, + Minimized: 1, + Maximized: 2 +}; + /** * A set of UIHacks frame style settings, see foobar's Preferences > Display > Main Window > Frame style. + * @global * @enum {number} */ const FrameStyle = { @@ -300,6 +441,7 @@ const FrameStyle = { /** * A set of UIHacks move style settings, see foobar's Preferences > Display > Main Window > Move with. + * @global * @enum {number} */ const MoveStyle = { @@ -315,9 +457,10 @@ const MoveStyle = { /////////////////////// /** * A set of country codes that maps two digit country codes to full names, mostly used for displaying flag images via tags. + * @global * @enum {string} */ -const countryCodes = { +const CountryCodes = { US: 'United States', GB: 'United Kingdom', AU: 'Australia', @@ -570,24 +713,28 @@ const countryCodes = { /** * The menu item is disabled. * @type {number} + * @global */ const MF_DISABLED = 0x00000002; /** * The menu item is grayed out. * @type {number} + * @global */ const MF_GRAYED = 0x00000001; /** * The menu item is a popup menu item. * @type {number} + * @global */ const MF_POPUP = 0x00000010; /** * The menu item is a string. * @type {number} + * @global */ const MF_STRING = 0x00000000; @@ -598,42 +745,49 @@ const MF_STRING = 0x00000000; /** * The CTRL key is down. * @type {number} + * @global */ const MK_CONTROL = 0x0008; /** * The left mouse button is down. * @type {number} + * @global */ const MK_LBUTTON = 0x0001; /** * The middle mouse button is down. * @type {number} + * @global */ const MK_MBUTTON = 0x0010; /** * The right mouse button is down. * @type {number} + * @global */ const MK_RBUTTON = 0x0002; /** * The SHIFT key is down. * @type {number} + * @global */ const MK_SHIFT = 0x0004; /** * The first X button is down. * @type {number} + * @global */ const MK_XBUTTON1 = 0x0020; /** * The second X button is down. * @type {number} + * @global */ const MK_XBUTTON2 = 0x0040; @@ -644,96 +798,112 @@ const MK_XBUTTON2 = 0x0040; /** * The standard arrow cursor with small hourglass to indicate application is starting. * @type {number} + * @global */ const IDC_APPSTARTING = 32650; /** * The standard arrow cursor. * @type {number} + * @global */ const IDC_ARROW = 32512; /** * The crosshair cursor. * @type {number} + * @global */ const IDC_CROSS = 32515; /** * The pointing hand cursor. * @type {number} + * @global */ const IDC_HAND = 32649; /** * The arrow cursor with a question mark to indicate help. * @type {number} + * @global */ const IDC_HELP = 32651; /** * The text insertion cursor (I-beam). * @type {number} + * @global */ const IDC_IBEAM = 32513; /** * The application icon cursor. * @type {number} + * @global */ const IDC_ICON = 32641; /** * The slashed circle cursor indicating "no". * @type {number} + * @global */ const IDC_NO = 32648; /** * The double-headed horizontal arrow cursor. * @type {number} + * @global */ const IDC_SIZE = 32640; /** * The four-headed arrow cursor (North/South/East/West). * @type {number} + * @global */ const IDC_SIZEALL = 32646; /** * The double-headed horizontal arrow cursor (West/East). * @type {number} + * @global */ const IDC_SIZEWE = 32644; /** * The double-headed diagonal arrow cursor (Northeast/Southwest). * @type {number} + * @global */ const IDC_SIZENESW = 32643; /** * The double-headed diagonal arrow cursor (Northwest/Southeast). * @type {number} + * @global */ const IDC_SIZENWSE = 32642; /** * The double-headed vertical arrow cursor (North/South). * @type {number} + * @global */ const IDC_SIZENS = 32645; /** * The up arrow cursor. * @type {number} + * @global */ const IDC_UPARROW = 32516; /** * The wait/busy cursor. * @type {number} + * @global */ const IDC_WAIT = 32514; @@ -748,6 +918,11 @@ const VK_MBUTTON = 0x04; // Middle mouse button (three-button mouse) const VK_XBUTTON1 = 0x05; // X1 mouse button const VK_XBUTTON2 = 0x06; // X2 mouse button +const VK_COPY = 0x03; // Copy command +const VK_CUT = 0x18; // Cut command +const VK_PASTE = 0x16; // Paste command +const VK_SELECT_ALL = 0x01; // Select All command + const VK_BACK = 0x08; // BACKSPACE key const VK_TAB = 0x09; // TAB key const VK_CLEAR = 0x0C; // CLEAR key @@ -930,6 +1105,7 @@ const VK_NONAME = 0xFC; // Reserved const VK_PA1 = 0xFD; // PA1 key const VK_OEM_CLEAR = 0xFE; // Clear key +/************************/ // 0x0A-0B // Reserved // 0x0E-0F // Undefined // 0x3A-40 // Undefined @@ -946,457 +1122,267 @@ const VK_OEM_CLEAR = 0xFE; // Clear key // 0xE6 // OEM specific // 0xE8 // Unassigned // 0xE9-F5 // OEM specific +/************************/ -/////////////////////// -// * ERROR HANDLER * // -/////////////////////// +/////////////// +// * ERROR * // +/////////////// /** - * A custom error class that handles theme errors with detailed messages. + * A class that handles theme errors with detailed messages. + * @augments {Error} */ class ThemeError extends Error { /** - * Creates an instance of ThemeError. - * @param {string} msg The error message. - * @extends {Error} + * Creates the `ThemeError` instance. + * @param {string} msg - The error message. */ constructor(msg) { super(msg); + /** @private @type {string} */ this.name = 'ThemeError'; + /** @private @type {string} */ this.message = `\n${msg}\n`; } } /** - * A custom error class that handles logic errors with detailed messages. + * A class that handles logic errors with detailed messages. + * @augments {Error} */ class LogicError extends Error { /** - * Creates an instance of LogicError. - * @param {string} msg The error message. - * @extends {Error} + * Creates the `LogicError` instance. + * @param {string} msg - The error message. */ constructor(msg) { super(msg); + /** @private @type {string} */ this.name = 'LogicError'; + /** @private @type {string} */ this.message = `\n${msg}\n`; } } /** - * A custom error class that handles invalid type errors with detailed messages. + * A class that handles invalid type errors with detailed messages. + * @augments {Error} */ class InvalidTypeError extends Error { /** - * Creates an instance of InvalidTypeError. - * @param {string} arg_name The name of the argument that caused the error. - * @param {string} arg_type The actual type of the argument that was passed. - * @param {string} valid_type The expected type of the argument. - * @param {string=} additional_msg An optional message to provide more information about the error. - * @extends {Error} + * Creates the `InvalidTypeError` instance. + * @param {string} arg_name - The name of the argument that caused the error. + * @param {string} arg_type - The actual type of the argument that was passed. + * @param {string} valid_type - The expected type of the argument. + * @param {string} [additional_msg] - An optional message to provide more information about the error. */ constructor(arg_name, arg_type, valid_type, additional_msg = '') { super(''); + /** @private @type {string} */ this.name = 'InvalidTypeError'; + /** @private @type {string} */ this.message = `\n'${arg_name}' is not a ${valid_type}, it's a ${arg_type}${additional_msg ? `\n${additional_msg}` : ''}\n`; } } /** - * A custom error class that handles argument errors with detailed messages. + * A class that handles argument errors with detailed messages. + * @augments {Error} */ class ArgumentError extends Error { /** - * Creates an instance of ArgumentError. - * @param {string} arg_name The name of the argument that has an invalid value. - * @param {*} arg_value The value of the argument that is considered invalid. - * @param {string=} additional_msg An optional message to provide more information about the error. - * @extends {Error} + * Creates the `ArgumentError` instance. + * @param {string} arg_name - The name of the argument that has an invalid value. + * @param {*} arg_value - The value of the argument that is considered invalid. + * @param {string} [additional_msg] - An optional message to provide more information about the error. */ constructor(arg_name, arg_value, additional_msg = '') { super(''); + /** @private @type {string} */ this.name = 'ArgumentError'; + /** @private @type {string} */ this.message = `\n'${arg_name}' has invalid value: ${arg_value}${additional_msg ? `\n${additional_msg}` : ''}\n`; } } -///////////////////////// -// * UTILITY HANDLER * // -///////////////////////// +/////////////////// +// * UTILITIES * // +/////////////////// /** - * Contains several utility methods. - * @type {Object} + * A class that provides a collection of utilities for various operations. + * @type {object} */ -const qwr_utils = { +class Utilities { /** - * Enables window sizing via UIHacks. - * @param {number} m The mouse mask. + * Creates the `Utilities` instance. + * Initializes default values for saved coordinates and modifier keys. */ - EnableSizing(m) { + constructor() { + /** @private @type {number} */ + this.savedX = 0; + /** @private @type {number} */ + this.savedY = 0; + /** @private @type {number} */ + this.savedM = 0; + /** @private @type {number} */ + this.savedKey = 0; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Disables window resizing if certain conditions are met via UIHacks. + * @param {number} m - The mouse mask. + */ + disableSizing(m) { try { - if (UIHacks && UIHacks.FrameStyle === 3 && UIHacks.DisableSizing) { - UIHacks.DisableSizing = false; + if (m && UIHacks && UIHacks.FrameStyle === 3 && !UIHacks.DisableSizing) { + UIHacks.DisableSizing = true; } } catch (e) { console.log(e); } - }, + } /** - * Disables window sizing via UIHacks. - * @param {number} m The mouse mask. + * Enables window resizing if certain conditions are met via UIHacks. + * @param {number} m - The mouse mask. */ - DisableSizing(m) { + enableSizing(m) { try { - if (m && UIHacks && UIHacks.FrameStyle === 3 && !UIHacks.DisableSizing) { - UIHacks.DisableSizing = true; + if (UIHacks && UIHacks.FrameStyle === 3 && UIHacks.DisableSizing) { + UIHacks.DisableSizing = false; } } catch (e) { console.log(e); } - }, + } /** - * Takes a site name and a metadata object, extracts artist, album, and title information from the metadata, - * and generates a search URL for the specified site using the extracted information. - * @param {string} site The url of the website. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @returns the URL of a specific website based on the provided site parameter and the metadata of a music file. + * Gets the major and minor version of Windows operating system from the registry. + * Falls back to a default version if registry read is unsuccessful. + * @returns {string} The Windows version in 'major.minor' format or a default if not obtainable. + */ + getWindowsVersion() { + return Once(() => { + let version = ''; + let ret = Attempt(() => { + version = (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString(); + version += '.'; + version += (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString(); + }); + + if (!IsError(ret)) { + return version; + } + + ret = Attempt(() => { + version = WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion'); + }); + + if (!IsError(ret)) { + return version; + } + + return '6.1'; + }); + } + + /** + * Constructs a search URL for a specified site using available track metadata. + * @param {string} site - The name of the site to generate a search URL for. + * @param {FbMetadbHandle} metadb - The metadata handle of the track. */ link(site, metadb) { - if (!metadb) { - return; - } + if (!metadb) return; - const meta_info = metadb.GetFileInfo(); - const artist = meta_info.MetaValue(meta_info.MetaFind('artist'), 0).replace(/\s+/g, '+').replace(/&/g, '%26'); - const album = meta_info.MetaValue(meta_info.MetaFind('album'), 0).replace(/\s+/g, '+'); - const title = meta_info.MetaValue(meta_info.MetaFind('title'), 0).replace(/\s+/g, '+'); + const metaInfo = metadb.GetFileInfo(); + const artist = metaInfo.MetaValue(metaInfo.MetaFind('artist'), 0).replace(/\s+/g, '+').replace(/&/g, '%26'); + const album = metaInfo.MetaValue(metaInfo.MetaFind('album'), 0).replace(/\s+/g, '+'); + const title = metaInfo.MetaValue(metaInfo.MetaFind('title'), 0).replace(/\s+/g, '+'); - const search_term = artist || title; + const searchQuerry = artist || title; switch (site.toLowerCase()) { case 'google': - site = (search_term ? `http://images.google.com/search?q=${search_term}&ie=utf-8` : null); + site = (searchQuerry ? `http://images.google.com/search?q=${searchQuerry}&ie=utf-8` : null); break; case 'googleimages': - site = (search_term ? `http://images.google.com/images?hl=en&q=${search_term}&ie=utf-8` : null); + site = (searchQuerry ? `http://images.google.com/images?hl=en&q=${searchQuerry}&ie=utf-8` : null); break; case 'wikipedia': site = (artist ? `http://en.wikipedia.org/wiki/${artist.replace(/\+/g, '_')}` : null); break; case 'youtube': - site = (search_term ? `http://www.youtube.com/results?search_type=&search_query=${search_term}&ie=utf-8` : null); + site = (searchQuerry ? `http://www.youtube.com/results?search_type=&search_query=${searchQuerry}&ie=utf-8` : null); break; case 'lastfm': - site = (search_term ? `http://www.last.fm/music/${search_term.replace('/', '%252F')}` : null); + site = (searchQuerry ? `http://www.last.fm/music/${searchQuerry.replace('/', '%252F')}` : null); break; case 'discogs': - site = (search_term || album ? `http://www.discogs.com/search?q=${search_term}+${album}&ie=utf-8` : null); + site = (searchQuerry || album ? `http://www.discogs.com/search?q=${searchQuerry}+${album}&ie=utf-8` : null); + break; + case 'musicbrainz': + site = (searchQuerry || album ? `https://musicbrainz.org/taglookup/index?tag-lookup.artist=${searchQuerry}&tag-lookup.release=${album}&ie=utf-8` : null); break; default: site = ''; } - if (!site) { - return; - } + if (!site) return; RunCmd(site); - }, - - /** - * Checks if a mouse movement event should be suppressed or not. - */ - MouseMoveSuppress() { - let saved_x; - let saved_y; - let saved_m; - - return { - /** - * Checks if the mouse movement is suppressed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - is_supressed: (x, y, m) => { - if (saved_x === x && saved_y === y && saved_m === m) { - return true; - } - - saved_x = x; - saved_y = y; - saved_m = m; - - return false; - } - }; - }, - - /** - * Prepares an HTML file by replacing the CSS file reference with a new CSS file based on the Windows version. - * @param {string} path The file path of the HTML file that needs to be prepared. - * @returns {string} The modified HTML code with the updated CSS path. - */ - prepare_html_file(path) { - const html_code = utils.ReadTextFile(path); - - const new_css = (qwr_utils.get_windows_version() === '6.1') ? 'styles7.css' : 'styles10.css'; - const css_path = `${fb.FoobarPath}${g_pl_colors.script_folder}html\\${new_css}`; - - return html_code.replace(/href="styles10.css"/i, `href="${css_path}"`); - }, - - /** - * Suppresses certain key modifiers (SHIFT, CONTROL, and MENU) in order - * to prevent them from being triggered multiple times in quick succession. - */ - KeyModifiersSuppress() { - let saved_key; - - return { - /** - * Checks if the key modifiers are suppressed. - * @param {string|number} key The keyboard key. - * @returns {boolean} True or false. - */ - is_supressed: (key) => { - if ((VK_SHIFT === key || VK_CONTROL === key || VK_MENU === key) && saved_key === key) { - return true; - } - - saved_key = key; - - return false; - } - }; - }, - - /** - * Gets the Windows version of the operating system. - * @returns {string} - */ - get_windows_version: Once(() => { - let version = ''; - let ret = Attempt(() => { - version = (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString(); - version += '.'; - version += (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString(); - }); - - if (!IsError(ret)) { - return version; - } - - ret = Attempt(() => { - version = WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion'); - }); - - if (!IsError(ret)) { - return version; - } - - return '6.1'; - }) -}; - -/** - * The key down suppress utility handler. - * @type {Object} - */ -const qwr_supress_key_down = qwr_utils.KeyModifiersSuppress(); - -/** - * The mouse move suppress utility handler. - * @type {Object} - */ -const qwr_supress_mouse_move = qwr_utils.MouseMoveSuppress(); - - -///////////////////////// -// * KEYBOARD EVENTS * // -///////////////////////// -/** - * Handles key action events to determine whether a key is pressed. - * Provides a way to manage key action events, allowing you to register - * and invoke actions based on key inputs. It maintains an internal registry - * of key actions, which can be triggered by invoking the registered keys. - */ -class KeyActionHandler { - /** - * The actions registry is an object that maps keys to their respective action callbacks. - */ - constructor() { - /** @type {Object.} */ - this.actions = {}; - } - - /** - * Registers a key action. - * @param {string|number} key The key to register. - * @param {function} action_callback The callback to run when the key is pressed. - * @throws {ArgumentError} If the action callback is not a function. - * @throws {ArgumentError} If the key is already used. - */ - register_key_action(key, action_callback) { - if (!action_callback) { - throw new ArgumentError('action_callback', action_callback); - } - - if (this.actions[key]) { - throw new ArgumentError('key', key.toString(), 'This key is already used'); - } - - this.actions[key] = action_callback; - } - - /** - * Invokes a key action. - * @param {string} key The key to invoke. - * @param {Object} [key_modifiers={}] The modifiers for the key passed to key action callback. - * @param {boolean=} [key_modifiers.ctrl=false] The option to disable the CTRL key. - * @param {boolean=} [key_modifiers.alt=false] The option to disable the ALT key. - * @param {boolean=} [key_modifiers.shift=false] The option to disable the SHIFT key. - * @returns {boolean} True or false. - */ - invoke_key_action(key, key_modifiers) { - const key_action = this.actions[key]; - if (!this.actions[key]) { - return false; - } - - key_action(key_modifiers || {}); - - return true; } -} - -////////////////////////// -// * PANEL PROPERTIES * // -////////////////////////// -/** - * Creates an object with a name and a value, and provides methods to get and set the value while also storing. - */ -class PanelProperty { /** - * @param {string} name The name of the property, used as a key to store and retrieve the property value. - * @param {*} defaultValue The initial value that will be used if there is no existing value stored for the property. - */ - constructor(name, defaultValue) { - /** @const {string} */ - this.name = name; - /** @type {*} */ - this.value = window.GetProperty(this.name, defaultValue); - } - - /** - * Gets the panel property value (backward compatibility method). - * @returns {*} - */ - get() { - return this.value; - } - - /** - * Sets the panel properties of the SMP. - * @param {*} new_value + * Prepares an HTML file by replacing the CSS file reference with a new CSS file based on the Windows version. + * @param {string} path - The file path of the HTML file that needs to be prepared. + * @returns {string} The modified HTML content with the updated CSS file reference. */ - set(new_value) { - if (this.value !== new_value) { - window.SetProperty(this.name, new_value); - this.value = new_value; - } - } -} - + prepareHTML(path) { + const htmlCode = utils.ReadTextFile(path); + const newCss = grm.utils.getWindowsVersion() === '6.1' ? 'styles7.css' : 'styles10.css'; + const cssPath = `${fb.FoobarPath}georgia-reborn\\scripts\\playlist\\assets\\html\\${newCss}`; -/** - * Allows to add and manage SMP properties with their names and default values. - */ -class PanelProperties { - constructor() { - /** - * Used for collision checks only and shared between objects. - * @type {Object} - */ - this.properties_name_list = {}; + return htmlCode.replace(/href="styles10.css"/i, `href="${cssPath}"`); } /** - * @param {Object} properties Each item in array is an object of the following type { string, [string, any] }. + * Suppresses key events for SHIFT, CONTROL, and MENU keys if they are triggered in quick succession. + * @param {number} key - The keycode of the key to potentially suppress. + * @returns {boolean} Whether the key event should be suppressed. */ - add_properties(properties) { - for (const key of Object.keys(properties)) { - this.validate_property_item(properties[key], key); - this.add_property_item(properties[key], key); - } - } - - /** - * Checks if a given value is a string. - * @param {*} str A value that we want to check if it is a string. - * @returns {boolean} True or false. - */ - isString(str) { - if (str != null && typeof str.valueOf() === 'string') { + suppressKey(key) { + if ((VK_SHIFT === key || VK_CONTROL === key || VK_MENU === key) && this.savedKey === key) { return true; } + + this.savedKey = key; return false; } /** - * Validates a property item and throws appropriate errors if any validation fails. - * @param {Array} item An array that contains the name and default value. - * @param {string} item_id A unique identifier for the property item. + * Suppresses mouse movement events if the current position and modifier keys are the same as the last. + * @param {number} x - The current x-coordinate of the mouse. + * @param {number} y - The current y-coordinate of the mouse. + * @param {number} m - The current mouse mask. + * @returns {boolean} Whether the mouse move event should be suppressed. */ - validate_property_item(item, item_id) { - if (!Array.isArray(item) || item.length !== 2 || !this.isString(item[0])) { - throw new InvalidTypeError('property', typeof item, '{ string, [string, any] }', 'Usage: add_properties({\n property_id: [property_name, property_default_value]\n})'); - } - if (item_id === 'add_properties') { - throw new ArgumentError('property_id', item_id, 'This id is reserved'); - } - if (this[item_id] || this[`${item_id}_internal`]) { - throw new ArgumentError('property_id', item_id, 'This id is already occupied'); - } - if (this.properties_name_list[item[0]]) { - throw new ArgumentError('property_name', item[0], 'This name is already occupied'); + suppressMouseMove(x, y, m) { + if (this.savedX === x && this.savedY === y && this.savedM === m) { + return true; } - } - /** - * Adds a new property to an object and creates a getter and setter for that property. - * @param {Array} item An array that contains the name and default value. - * @param {string} item_id A unique identifier for the property item. - * @returns {*} Defining a new property on an object and setting its getter and setter methods. - */ - add_property_item(item, item_id) { - this.properties_name_list[item[0]] = 1; - - this[`${item_id}_internal`] = new PanelProperty(item[0], item[1]); - - Object.defineProperty(this, item_id, { - get() { - return this[`${item_id}_internal`].get(); - }, - set(new_value) { - this[`${item_id}_internal`].set(new_value); - } - }); + this.savedX = x; + this.savedY = y; + this.savedM = m; + return false; } + // #endregion } - -/** @type {*} The Main UI properties object. */ -const pref = new PanelProperties(); - -/** @type {*} The Playlist panel properties object. */ -const g_properties = new PanelProperties(); diff --git a/profile/georgia-reborn/scripts/Base/gr-config-defaults.js b/profile/georgia-reborn/scripts/Base/gr-config-defaults.js new file mode 100644 index 00000000..e5fb6ac2 --- /dev/null +++ b/profile/georgia-reborn/scripts/Base/gr-config-defaults.js @@ -0,0 +1,2537 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Config Defaults * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +//////////////////////////////////////// +// ! GEORGIA-REBORN-CONFIG DEFAULTS ! // +//////////////////////////////////////// +// * This file contains the various definitions, default values, and schemas for the objects that will be written to the configuration file. +// * Any value defined here will be written to the config ( although some of these objects will be modified in gr-settings.js with values that are not saved ). +// ! DO NOT EDIT: Editing these values will likely not provide you with the results you expect as they will probably not be stored in the configs. +// ! NOTE: If you wish to make changes to this, edit it in your georgia-reborn-config.jsonc and georgia-reborn-custom.jsonc file and NOT here. + + +/** + * A class that reads and stores all theme default config settings. + */ +class ConfigDefaults { + /** + * Creates the `ConfigDefaults` instance. + * Instantiates all config default settings. + */ + constructor() { + // * TITLE FORMAT STRINGS * // + // #region TITLE FORMAT STRINGS + /** @public @type {object} Assigning the title formatting strings. */ + this.titleFormatDefaults = Object.assign({}, grTF); + + /** @public @type {object} Title formatting config name description. */ + this.titleFormatComments = { + artist_country: 'Only used for displaying artist flags.', + date: 'The full date stored for the track', + lyrics: 'gr-lyrics.js will check these fields in order if no local lyrics file is found.', + releaseCountry: 'Releases tagged from Musicbrainz with a release country of AF (Afghanistan) are almost always whole world releases that have each country listed individually, so replace with \'XW\' (Worldwide) tag.', + title: 'Track title shown above the progress bar', + vinyl_side: 'Used for determining what side a song appears on for vinyl releases - i.e. song A1 has a %vinyl side% of "A"', + vinyl_tracknum: 'Used for determining the track number on vinyl releases - i.e. song A1 has %vinyl tracknumber% set to "1"', + year: 'Just the year portion of any stored date.' + }; + + /** @public @type {object} Title formatting config header description. */ + this.titleFormatSchema = new ConfigurationObjectSchema('title_format_strings', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* TITLE FORMATTING STRINGS: ' + + '* Used throughout the display. Do NOT change the key names or add new ones. ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * ARTWORK IMAGE PATHS * // + // #region ARTWORK IMAGE PATHS + /** @public @type {array} Artwork image paths load order - add, change or re-order entries as needed. */ + this.imgPathDefaults = [ + // * File names with formats + '$replace(%path%,%filename_ext%,)folder*', + '$replace(%path%,%filename_ext%,)cover*', + '$replace(%path%,%filename_ext%,)front*', + '$replace(%path%,%filename_ext%,)*.*', + + // * All folder images in parent directory + '$replace(%path%,%directoryname%\\%filename_ext%,)folder*', + '$replace(%path%,%directoryname%\\%filename_ext%,)cover*', + '$replace(%path%,%directoryname%\\%filename_ext%,)front*', + '$replace(%path%,%directoryname%\\%filename_ext%,)*.*', + + // * Artwork, Images, Scans in root directory ( 1 Disc ) + '$replace(%path%\\..\\Artwork\\,%filename_ext%,)folder*', + '$replace(%path%\\..\\Artwork\\,%filename_ext%,)cover*', + '$replace(%path%\\..\\Artwork\\,%filename_ext%,)front*', + '$replace(%path%\\..\\Artwork\\,%filename_ext%,)*.*', + '$replace(%path%\\..\\Images\\,%filename_ext%,)folder*', + '$replace(%path%\\..\\Images\\,%filename_ext%,)cover*', + '$replace(%path%\\..\\Images\\,%filename_ext%,)front*', + '$replace(%path%\\..\\Images\\,%filename_ext%,)*.*', + '$replace(%path%\\..\\Scans\\,%filename_ext%,)folder*', + '$replace(%path%\\..\\Scans\\,%filename_ext%,)cover*', + '$replace(%path%\\..\\Scans\\,%filename_ext%,)front*', + '$replace(%path%\\..\\Scans\\,%filename_ext%,)*.*', + + // * Artwork, Images, Scans in other subfolders ( Multi Discs ) + '$replace(%path%\\Artwork\\,%filename_ext%,)folder*', + '$replace(%path%\\Artwork\\,%filename_ext%,)cover*', + '$replace(%path%\\Artwork\\,%filename_ext%,)front*', + '$replace(%path%\\Artwork\\,%filename_ext%,)*.*', + '$replace(%path%\\Images\\,%filename_ext%,)folder*', + '$replace(%path%\\Images\\,%filename_ext%,)cover*', + '$replace(%path%\\Images\\,%filename_ext%,)front*', + '$replace(%path%\\Images\\,%filename_ext%,)*.*', + '$replace(%path%\\Scans\\,%filename_ext%,)folder*', + '$replace(%path%\\Scans\\,%filename_ext%,)cover*', + '$replace(%path%\\Scans\\,%filename_ext%,)front*', + '$replace(%path%\\Scans\\,%filename_ext%,)*.*' + ]; + + /** @public @type {object} Artwork image paths config header description. */ + this.imgPathSchema = new ConfigurationObjectSchema('imgPaths', ConfigurationObjectType.Array, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* ARTWORK IMAGE PATHS: ' + + '* The title formatting defined paths for artwork to be displayed. The first image matched will be shown first. ' + + '* Re-arrange, add, or remove as needed. Folder delimiters must be double-slashes. ' + + '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * THEME * // + // #region THEME + /** @public @type {object} Options > Theme settings with default value. */ + this.themeDefaults = { + theme: 'reborn', + theme_day: 'white', + theme_night: 'black' + }; + + /** @public @type {object} Options > Theme settings config name description. */ + this.themeComments = { + theme: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold", "custom01-custom10" - Options > Theme', + theme_day: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold", "custom01-custom10" - Options > Theme - daytime theme', + theme_night: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold", "custom01-custom10" - Options > Theme - nighttime theme' + }; + + /** @public @type {object} Options > Theme settings config header description. */ + this.themeSchema = new ConfigurationObjectSchema('theme', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* THEME: ' + + '* Top menu Options > Theme ' + + '* You can set and select between the 12 available themes that will be used. ' + + '* If you choose to enable harmonic mode, your selected theme will be overriden once harmonic mode is deactivated again. ' + + '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * STYLE * // + // #region STYLE + /** @public @type {object} Options > Style settings with default values */ + this.themeStyleDefaults = { + default: true, + nighttime: false, + bevel: false, + blend: false, + blend2: false, + gradient: false, + gradient2: false, + alternative: false, + alternative2: false, + blackAndWhite: false, + blackAndWhite2: false, + blackAndWhiteReborn: false, + blackReborn: false, + rebornWhite: false, + rebornBlack: false, + rebornFusion: false, + rebornFusion2: false, + rebornFusionAccent: false, + randomPastel: false, + randomDark: false, + randomAutoColor: 'off', + topMenuButtons: 'default', + transportButtons: 'default', + progressBarDesign: 'default', + progressBar: 'default', + progressBarFill: 'default', + volumeBarDesign: 'default', + volumeBar: 'default', + volumeBarFill: 'default', + nighttime_day: false, + bevel_day: false, + blend_day: false, + blend2_day: false, + gradient_day: false, + gradient2_day: false, + alternative_day: false, + alternative2_day: false, + blackAndWhite_day: false, + blackAndWhite2_day: false, + blackAndWhiteReborn_day: false, + blackReborn_day: false, + rebornWhite_day: false, + rebornBlack_day: false, + rebornFusion_day: false, + rebornFusion2_day: false, + rebornFusionAccent_day: false, + randomPastel_day: false, + randomDark_day: false, + randomAutoColor_day: 'off', + topMenuButtons_day: 'default', + transportButtons_day: 'default', + progressBarDesign_day: 'default', + progressBar_day: 'default', + progressBarFill_day: 'default', + volumeBarDesign_day: 'default', + volumeBar_day: 'default', + volumeBarFill_day: 'default', + nighttime_night: false, + bevel_night: false, + blend_night: false, + blend2_night: false, + gradient_night: false, + gradient2_night: false, + alternative_night: false, + alternative2_night: false, + blackAndWhite_night: false, + blackAndWhite2_night: false, + blackAndWhiteReborn_night: false, + blackReborn_night: false, + rebornWhite_night: false, + rebornBlack_night: false, + rebornFusion_night: false, + rebornFusion2_night: false, + rebornFusionAccent_night: false, + randomPastel_night: false, + randomDark_night: false, + randomAutoColor_night: 'off', + topMenuButtons_night: 'default', + transportButtons_night: 'default', + progressBarDesign_night: 'default', + progressBar_night: 'default', + progressBarFill_night: 'default', + volumeBarDesign_night: 'default', + volumeBar_night: 'default', + volumeBarFill_night: 'default' + }; + + /** @public @type {object} Options > Style settings config name description. */ + this.themeStyleComments = { + default: 'Values: true, false - can be used in all themes', + nighttime: 'Values: true, false - special style can only be used with reborn, random, custom theme', + bevel: 'Values: true, false - can be used in all themes', + blend: 'Values: true, false - can be used in all themes', + blend2: 'Values: true, false - can be used in all themes', + gradient: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', + gradient2: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', + alternative: 'Values: true, false - can be used in all themes but not with special styles', + alternative2: 'Values: true, false - can be used in all themes but not with special styles', + blackAndWhite: 'Values: true, false - special white style can only be used with white theme', + blackAndWhite2: 'Values: true, false - special white style can only be used with white theme', + blackAndWhiteReborn: 'Values: true, false - special white style can only be used with white theme', + blackReborn: 'Values: true, false - special black style can only be used with black theme', + rebornWhite: 'Values: true, false - special reborn style can only be used with reborn theme', + rebornBlack: 'Values: true, false - special reborn style can only be used with reborn theme', + rebornFusion: 'Values: true, false - special reborn style can only be used with reborn theme', + rebornFusion2: 'Values: true, false - special reborn style can only be used with reborn theme', + rebornFusionAccent: 'Values: true, false - special reborn style can only be used with reborn theme', + randomPastel: 'Values: true, false - special random style can only be used with random theme', + randomDark: 'Values: true, false - special random style can only be used with random theme', + randomAutoColor: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track" - can only be used with random theme', + topMenuButtons: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal"', + transportButtons: 'Values: "default", "bevel", "inner", "emboss", "minimal"', + progressBarDesign: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin"', + progressBar: 'Values: "default", "bevel", "inner"', + progressBarFill: 'Values: "default", "bevel", "inner", "blend"', + volumeBarDesign: 'Values: "default", "rounded"', + volumeBar: 'Values: "default", "bevel", "inner"', + volumeBarFill: 'Values: "default", "bevel", "inner"', + nighttime_day: 'Values: true, false - daytime theme - special style can only be used with reborn, random, custom theme', + bevel_day: 'Values: true, false - daytime theme - can be used in all themes', + blend_day: 'Values: true, false - daytime theme - can be used in all themes', + blend2_day: 'Values: true, false - daytime theme - can be used in all themes', + gradient_day: 'Values: true, false - daytime theme - can only be used with reborn, random, blue, darkblue, red theme', + gradient2_day: 'Values: true, false - daytime theme - can only be used with reborn, random, blue, darkblue, red theme', + alternative_day: 'Values: true, false - daytime theme - can be used in all themes but not with special styles', + alternative2_day: 'Values: true, false - daytime theme - can be used in all themes but not with special styles', + blackAndWhite_day: 'Values: true, false - daytime theme - special white style can only be used with white theme', + blackAndWhite2_day: 'Values: true, false - daytime theme - special white style can only be used with white theme', + blackAndWhiteReborn_day: 'Values: true, false - daytime theme - special white style can only be used with white theme', + blackReborn_day: 'Values: true, false - daytime theme - special black style can only be used with black theme', + rebornWhite_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', + rebornBlack_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', + rebornFusion_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', + rebornFusion2_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', + rebornFusionAccent_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', + randomPastel_day: 'Values: true, false - daytime theme - special random style can only be used with random theme', + randomDark_day: 'Values: true, false - daytime theme - special random style can only be used with random theme', + randomAutoColor_day: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track" - daytime theme - can only be used with random theme', + topMenuButtons_day: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal" - daytime theme', + transportButtons_day: 'Values: "default", "bevel", "inner", "emboss", "minimal" - daytime theme', + progressBarDesign_day: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin" - daytime theme', + progressBar_day: 'Values: "default", "bevel", "inner" - daytime theme', + progressBarFill_day: 'Values: "default", "bevel", "inner", "blend" - daytime theme', + volumeBarDesign_day: 'Values: "default", "rounded" - daytime theme', + volumeBar_day: 'Values: "default", "bevel", "inner" - daytime theme', + volumeBarFill_day: 'Values: "default", "bevel", "inner" - daytime theme', + nighttime_night: 'Values: true, false - nighttime theme - special style can only be used with reborn, random, custom theme', + bevel_night: 'Values: true, false - nighttime theme - can be used in all themes', + blend_night: 'Values: true, false - nighttime theme - can be used in all themes', + blend2_night: 'Values: true, false - nighttime theme - can be used in all themes', + gradient_night: 'Values: true, false - nighttime theme - can only be used with reborn, random, blue, darkblue, red theme', + gradient2_night: 'Values: true, false - nighttime theme - can only be used with reborn, random, blue, darkblue, red theme', + alternative_night: 'Values: true, false - nighttime theme - can be used in all themes but not with special styles', + alternative2_night: 'Values: true, false - nighttime theme - can be used in all themes but not with special styles', + blackAndWhite_night: 'Values: true, false - nighttime theme - special white style can only be used with white theme', + blackAndWhite2_night: 'Values: true, false - nighttime theme - special white style can only be used with white theme', + blackAndWhiteReborn_night: 'Values: true, false - nighttime theme - special white style can only be used with white theme', + blackReborn_night: 'Values: true, false - nighttime theme - special black style can only be used with black theme', + rebornWhite_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', + rebornBlack_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', + rebornFusion_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', + rebornFusion2_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', + rebornFusionAccent_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', + randomPastel_night: 'Values: true, false - nighttime theme - special random style can only be used with random theme', + randomDark_night: 'Values: true, false - nighttime theme - special random style can only be used with random theme', + randomAutoColor_night: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track" - nighttime theme - can only be used with random theme', + topMenuButtons_night: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal" - nighttime theme', + transportButtons_night: 'Values: "default", "bevel", "inner", "emboss", "minimal" - nighttime theme', + progressBarDesign_night: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin" - nighttime theme', + progressBar_night: 'Values: "default", "bevel", "inner" - nighttime theme', + progressBarFill_night: 'Values: "default", "bevel", "inner", "blend" - nighttime theme', + volumeBarDesign_night: 'Values: "default", "rounded" - nighttime theme', + volumeBar_night: 'Values: "default", "bevel", "inner" - nighttime theme', + volumeBarFill_night: 'Values: "default", "bevel", "inner" - nighttime theme' + }; + + /** @public @type {object} Options > Style settings config header description. */ + this.themeStyleSchema = new ConfigurationObjectSchema('style', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* STYLE: ' + + '* Top menu Options > Style ' + + '* If you set a style, you need to set default: false, ' + + '* Basic style "gradient" and "gradient2" is only supported and can be used with reborn, random, blue, darkblue, red theme. ' + + '* Basic style "alternative" and "alternative2" can not be used with special styles except "nighttime". ' + + '* Special style "nighttime" can only be used with "reborn", "random", "custom" theme. ' + + '* Special style "blackAndWhite", "blackAndWhite2" and "blackAndWhiteReborn" can only be used with "white" theme. ' + + '* Special style "blackReborn" can only be used with "black" theme. ' + + '* Special style "rebornWhite", "rebornBlack", "rebornFusion", "rebornFusion2" and "rebornFusionAccent" can only be used with "reborn" theme. ' + + '* Special style "randomPastel", "randomDark" and "randomAutoColor" can only be used with "random" theme. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * PRESET * // + // #region PRESET + /** @public @type {object} Options > Preset settings with default values. */ + this.themePresetDefaults = { + selectMode: 'default', + selectWhitePresets: true, + selectBlackPresets: true, + selectRebornPresets: true, + selectRandomPresets: true, + selectBluePresets: true, + selectDarkbluePresets: true, + selectRedPresets: true, + selectCreamPresets: true, + selectNbluePresets: true, + selectNgreenPresets: true, + selectNredPresets: true, + selectNgoldPresets: true, + selectCustomPresets: true, + autoRandomMode: 'dblclick', + indicator: true + }; + + /** @public @type {object} Options > Preset settings config name description. */ + this.themePresetComments = { + selectMode: 'Values: "default", "harmonic", "theme" - Options > Preset > Select mode', + selectWhitePresets: 'Values: true, false - Options > Preset > Select presets > White', + selectBlackPresets: 'Values: true, false - Options > Preset > Select presets > Black', + selectRebornPresets: 'Values: true, false - Options > Preset > Select presets > Reborn', + selectRandomPresets: 'Values: true, false - Options > Preset > Select presets > Random', + selectBluePresets: 'Values: true, false - Options > Preset > Select presets > Blue', + selectDarkbluePresets: 'Values: true, false - Options > Preset > Select presets > Dark blue', + selectRedPresets: 'Values: true, false - Options > Preset > Select presets > Red', + selectCreamPresets: 'Values: true, false - Options > Preset > Select presets > Cream', + selectNbluePresets: 'Values: true, false - Options > Preset > Select presets > Neon blue', + selectNgreenPresets: 'Values: true, false - Options > Preset > Select presets > Neon green', + selectNredPresets: 'Values: true, false - Options > Preset > Select presets > Neon red', + selectNgoldPresets: 'Values: true, false - Options > Preset > Select presets > Neon gold', + selectCustomPresets: 'Values: true, false - Options > Preset > Select presets > Custom theme', + autoRandomMode: 'Values: "off", 5000, 10000, 15000, 30000, 60000, 300000, 600000, 900000, 1800000, 3600000, "track", "album", "dblclick" - Options > Preset > Auto random', + indicator: 'Values: true, false - Options > Preset > Indicator' + }; + + /** @public @type {object} Options > Preset settings config header description. */ + this.themePresetSchema = new ConfigurationObjectSchema('preset', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* PRESET: ' + + '* Top menu Options > Preset ' + + '* You can set the preset select mode and preset auto-random mode and deactivate theme presets to be excluded in the preset selection when preset select mode or preset auto-random mode is being used. ' + + '* If you choose to change the preset select mode, your active theme will be overriden once preset select mode is not set to "default". ' + + '* This behavior also applies to the preset auto-random mode when "dblclick" has not been set. ' + + '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * PLAYER SIZE * // + // #region PLAYER SIZE + /** @public @type {object} Options > Player size settings with default value. */ + this.themePlayerSizeDefaults = { + playerSize: 'small', + savedWidth_default: 1140, + savedHeight_default: 730, + savedWidth_artwork: 526, + savedHeight_artwork: 686, + savedWidth_compact: 484, + savedHeight_compact: 730 + }; + + /** @public @type {object} Options > Player size settings config name description. */ + this.themePlayerSizeComments = { + playerSize: 'Values: "small", "normal", "large" - Options > Player size', + savedWidth_default: 'The saved player width for the Default layout - Options > Layout > Default', + savedHeight_default: 'The saved player height for the Default layout - Options > Layout > Default', + savedWidth_artwork: 'The saved player width for the Artwork layout - Options > Layout > Artwork', + savedHeight_artwork: 'The saved player height for the Artwork layout - Options > Layout > Artwork', + savedWidth_compact: 'The saved player width for the Compact layout - Options > Layout > Compact', + savedHeight_compact: 'The saved player height for the Compact layout - Options > Layout > Compact' + }; + + /** @public @type {object} Options > Player size settings config header description. */ + this.themePlayerSizeSchema = new ConfigurationObjectSchema('themePlayerSize', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* PLAYER SIZE: ' + + '* Top menu Options > Player size ' + + '* You can set and select between the 3 available player sizes that will be used. ' + + '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * LAYOUT * // + // #region LAYOUT + /** @public @type {object} Options > Layout settings with default value. */ + this.themeLayoutDefaults = { + layout: 'default' + }; + + /** @public @type {object} Options > Layout settings config name description. */ + this.themeLayoutComments = { + layout: 'Values: "default", "artwork", "compact" - Options > Layout' + }; + + /** @public @type {object} Options > Layout settings config header description. */ + this.themeLayoutSchema = new ConfigurationObjectSchema('themeLayout', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* LAYOUT: ' + + '* Top menu Options > Layout ' + + '* You can set and select between the 3 available layouts that will be used. ' + + '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * DISPLAY * // + // #region DISPLAY + /** @public @type {object} Options > Display settings with default value. */ + this.themeDisplayDefaults = { + resolution: 'HD' + }; + + /** @public @type {object} Options > Display settings config name description. */ + this.themeDisplayComments = { + resolution: 'Values: "4K", "QHD", "HD" - Options > Display' + }; + + /** @public @type {object} Options > Display settings config header description. */ + this.themeDisplaySchema = new ConfigurationObjectSchema('themeDisplay', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* DISPLAY: ' + + '* Top menu Options > Display ' + + '* You can set and select between the 3 available resolution that will be used. ' + + '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * BRIGHTNESS * // + // #region BRIGHTNESS + /** @public @type {object} Options > Brightness settings with default value. */ + this.themeBrightnessDefaults = { + themeBrightness: 'default', + themeBrightness_day: 'default', + themeBrightness_night: 'default' + }; + + /** @public @type {object} Options > Brightness settings config name description. */ + this.themeBrightnessComments = { + themeBrightness: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50 - Options > Brightness', + themeBrightness_day: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50 - Options > Brightness - daytime theme', + themeBrightness_night: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50 - Options > Brightness - nighttime theme' + }; + + /** @public @type {object} Options > Brightness settings config header description. */ + this.themeBrightnessSchema = new ConfigurationObjectSchema('themeBrightness', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* THEME BRIGHTNESS: ' + + '* Top menu Options > Brightness ' + + '* You can set and select between the 11 available brightness settings that will be used. ' + + '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * FONT SIZE * // + // #region FONT SIZE + /** @public @type {object} Options > Font size settings with default values. */ + this.themeFontSizesDefaults = { + menuFontSize_default: RES._QHD ? 14 : 12, + menuFontSize_artwork: RES._QHD ? 14 : 12, + menuFontSize_compact: RES._QHD ? 14 : 12, + lowerBarFontSize_default: RES._QHD ? 20 : 18, + lowerBarFontSize_artwork: RES._QHD ? 18 : 16, + lowerBarFontSize_compact: RES._QHD ? 18 : 16, + notificationFontSize_default: RES._QHD ? 20 : 18, + notificationFontSize_artwork: RES._QHD ? 18 : 16, + notificationFontSize_compact: RES._QHD ? 18 : 16, + popupFontSize_default: RES._QHD ? 18 : 16, + popupFontSize_artwork: RES._QHD ? 16 : 14, + popupFontSize_compact: RES._QHD ? 16 : 14, + tooltipFontSize_default: RES._QHD ? 18 : 16, + tooltipFontSize_artwork: RES._QHD ? 16 : 14, + tooltipFontSize_compact: RES._QHD ? 16 : 14, + gridArtistFontSize_default: RES._QHD ? 20 : 18, + gridArtistFontSize_artwork: RES._QHD ? 20 : 18, + gridTrackNumFontSize_default: RES._QHD ? 20 : 18, + gridTrackNumFontSize_artwork: RES._QHD ? 20 : 18, + gridTitleFontSize_default: RES._QHD ? 20 : 18, + gridTitleFontSize_artwork: RES._QHD ? 20 : 18, + gridAlbumFontSize_default: RES._QHD ? 20 : 18, + gridAlbumFontSize_artwork: RES._QHD ? 20 : 18, + gridKeyFontSize_default: RES._QHD ? 19 : 17, + gridKeyFontSize_artwork: RES._QHD ? 19 : 17, + gridValueFontSize_default: RES._QHD ? 19 : 17, + gridValueFontSize_artwork: RES._QHD ? 19 : 17, + playlistHeaderFontSize_default: RES._QHD ? 17 : 15, + playlistHeaderFontSize_artwork: RES._QHD ? 17 : 15, + playlistHeaderFontSize_compact: RES._QHD ? 17 : 15, + playlistFontSize_default: RES._QHD ? 14 : 12, + playlistFontSize_artwork: RES._QHD ? 14 : 12, + playlistFontSize_compact: RES._QHD ? 14 : 12, + libraryFontSize_default: RES._4K ? 24 : RES._QHD ? 14 : 12, + libraryFontSize_artwork: RES._4K ? 24 : RES._QHD ? 14 : 12, + biographyFontSize_default: RES._4K ? 24 : RES._QHD ? 14 : 12, + biographyFontSize_artwork: RES._4K ? 24 : RES._QHD ? 14 : 12, + lyricsFontSize_default: RES._QHD ? 22 : 20, + lyricsFontSize_artwork: RES._QHD ? 22 : 20 + }; + + /** @public @type {object} Options > Font size settings config name description. */ + this.themeFontSizesComments = { + menuFontSize_default: 'Values: 8, 10, 11, 12, 13, 14, 16 - Options > Font size > Main > Top menu - when Default layout is active', + menuFontSize_artwork: 'Values: 8, 10, 11, 12, 13, 14, 16 - Options > Font size > Main > Top menu - when Artwork layout is active', + menuFontSize_compact: 'Values: 8, 10, 11, 12, 13, 14, 16 - Options > Font size > Main > Top menu - when Compact layout is active', + lowerBarFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26 - Options > Font size > Main > Lower bar - when Default layout is active', + lowerBarFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26 - Options > Font size > Main > Lower bar - when Artwork layout is active', + lowerBarFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26 - Options > Font size > Main > Lower bar - when Compact layout is active', + notificationFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Notification - when Default layout is active', + notificationFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Notification - when Artwork layout is active', + notificationFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Notification - when Compact layout is active', + popupFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Popup - when Default layout is active', + popupFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Popup - when Artwork layout is active', + popupFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Popup - when Compact layout is active', + tooltipFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Tooltip - when Default layout is active', + tooltipFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Tooltip - when Artwork layout is active', + tooltipFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Tooltip - when Compact layout is active', + gridArtistFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Artist - when Default layout is active', + gridArtistFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Artist - when Artwork layout is active', + gridTrackNumFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - not in Options, track number before song title in grid - when Default layout is active', + gridTrackNumFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - not in Options, track number before song title in grid - when Artwork layout is active', + gridTitleFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Title - when Default layout is active', + gridTitleFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Title - when Artwork layout is active', + gridAlbumFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Album - when Default layout is active', + gridAlbumFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Album - when Artwork layout is active', + gridKeyFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag name - when Default layout is active', + gridKeyFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag name - when Artwork layout is active', + gridValueFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag value - when Default layout is active', + gridValueFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag value - when Artwork layout is active', + playlistHeaderFontSize_default: 'Values: 10, 12, 13, 14, 15, 16, 17, 18, 20, 22 - Options > Font size > Playlist - when Default layout is active', + playlistHeaderFontSize_artwork: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Artwork layout is active', + playlistHeaderFontSize_compact: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Compact layout is active', + playlistFontSize_default: 'Values: 10, 12, 13, 14, 15, 16, 17, 18, 20, 22 - Options > Font size > Playlist - when Default layout is active', + playlistFontSize_artwork: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Artwork layout is active', + playlistFontSize_compact: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Compact layout is active', + libraryFontSize_default: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Library - when Default layout is active', + libraryFontSize_artwork: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Library - when Artwork layout is active', + biographyFontSize_default: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Biography - when Default layout is active', + biographyFontSize_artwork: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Biography - when Artwork layout is active', + lyricsFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 - Options > Font size > Lyrics - when Default layout is active', + lyricsFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 - Options > Font size > Lyrics - when Artwork layout is active' + }; + + /** @public @type {object} Options > Font size settings config header description. */ + this.themeFontSizesSchema = new ConfigurationObjectSchema('themeFontSize', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* FONT SIZES: ' + + '* Top menu Options > Font size ' + + '* Default, Artwork and Compact font sizes can be independently customized and will be used when changing between layouts. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * PLAYER CONTROLS * // + // #region PLAYER CONTROLS + /** @public @type {object} Options > Player controls settings with default values. */ + this.themePlayerControlsDefaults = { + showPanelDetails_default: true, + showPanelDetails_artwork: true, + showPanelLibrary_default: true, + showPanelLibrary_artwork: true, + showPanelBiography_default: true, + showPanelBiography_artwork: true, + showPanelLyrics_default: true, + showPanelLyrics_artwork: true, + showPanelRating_default: true, + showPanelRating_artwork: true, + topMenuAlignment: 'center', + topMenuCompact: true, + albumArtAlign: 'right', + albumArtBg: 'left', + albumArtScale: 'cropped', + albumArtAspectRatioLimit: 1.5, + cycleArt: false, + cycleArtMWheel: true, + loadEmbeddedAlbumArtFirst: false, + showHiResAudioBadge: false, + hiResAudioBadgeRound: false, + hiResAudioBadgeSize: 'normal', + hiResAudioBadgePos: 'bottomright', + showPause: true, + jumpSearchIncludeLibrary: true, + jumpSearchIncludePlaylist: true, + jumpSearchComposerOnly: false, + playlistWheelScrollSteps: 3, + playlistWheelScrollDuration: 300, + playlistAutoScrollNowPlaying: false, + playlistAutoHideScrollbar: true, + playlistSmoothScrolling: true, + scrollStepLib: 3, + durationScrollLib: 500, + libraryAutoScrollNowPlaying: false, + libraryAutoHideScrollbar: true, + smoothLib: true, + scrollStepBio: 3, + durationScrollBio: 500, + biographyAutoHideScrollbar: true, + smoothBio: true, + showTooltipTruncated: true, + showTooltipTimeline: true, + showTooltipVolume: false, + showTooltipVolumeInPercent: false, + showTooltipMain: false, + showTooltipLibrary: false, + showTooltipBiography: false, + showStyledTooltips: true, + panelWidthAuto: false, + showPanelOnStartup: 'playlist', + showPreloaderLogo: true, + returnToHomeOnPlaybackStop: true, + addTracksPlaylistSwitch: false, + hideMiddlePanelShadow: false, + lockPlayerSize: false, + maximizeToFullscreen: true, + switchPlaybackTime: false, + transportButtonSize_default: 32, + transportButtonSize_artwork: 32, + transportButtonSize_compact: 32, + transportButtonSpacing_default: 5, + transportButtonSpacing_artwork: 5, + transportButtonSpacing_compact: 5, + showTransportControls_default: true, + showTransportControls_artwork: true, + showTransportControls_compact: true, + showPlaybackOrderBtn_default: true, + showPlaybackOrderBtn_artwork: true, + showPlaybackOrderBtn_compact: true, + showReloadBtn_default: false, + showReloadBtn_artwork: false, + showReloadBtn_compact: false, + showAddTracksBtn_default: false, + showAddTracksBtn_artwork: false, + showAddTracksBtn_compact: false, + showVolumeBtn_default: true, + showVolumeBtn_artwork: true, + showVolumeBtn_compact: true, + autoHideVolumeBar: true, + showPlaybackTime_default: true, + showPlaybackTime_artwork: true, + showPlaybackTime_compact: true, + showLowerBarArtist_default: true, + showLowerBarArtist_artwork: true, + showLowerBarArtist_compact: true, + showLowerBarTrackNum_default: true, + showLowerBarTrackNum_artwork: true, + showLowerBarTrackNum_compact: true, + showLowerBarTitle_default: true, + showLowerBarTitle_artwork: true, + showLowerBarTitle_compact: true, + showLowerBarComposer_default: false, + showLowerBarComposer_artwork: false, + showLowerBarComposer_compact: false, + showLowerBarArtistFlags_default: true, + showLowerBarArtistFlags_artwork: true, + showLowerBarArtistFlags_compact: true, + showLowerBarVersion_default: true, + showLowerBarVersion_artwork: true, + showLowerBarVersion_compact: true, + showProgressBar_default: true, + showProgressBar_artwork: true, + showProgressBar_compact: true, + showPeakmeterBar_default: true, + showPeakmeterBar_artwork: true, + showPeakmeterBar_compact: true, + showWaveformBar_default: true, + showWaveformBar_artwork: true, + showWaveformBar_compact: true, + addTracksPlaylist: 'Favorites', + seekbar: 'progressbar', + progressBarWheelSeekSpeed: 5, + progressBarRefreshRate: 'variable', + peakmeterBarDesign: 'horizontal', + peakmeterBarVertSize: 20, + peakmeterBarVertDbRange: 220, + peakmeterBarOverBars: true, + peakmeterBarOuterBars: true, + peakmeterBarOuterPeaks: true, + peakmeterBarMainBars: true, + peakmeterBarMainPeaks: true, + peakmeterBarMiddleBars: true, + peakmeterBarProgBar: true, + peakmeterBarGaps: false, + peakmeterBarGrid: false, + peakmeterBarInfo: false, + peakmeterBarVertPeaks: true, + peakmeterBarVertBaseline: true, + peakmeterBarRefreshRate: 80, + waveformBarMode: 'audiowaveform', + waveformBarAnalysis: 'rms_level', + waveformBarDesign: 'halfbars', + waveformBarSizeWave: 3, + waveformBarSizeBars: 1, + waveformBarSizeDots: 2, + waveformBarSizeHalf: 4, + waveformBarSizeNormalize: false, + waveformBarPaint: 'partial', + waveformBarPrepaint: true, + waveformBarPrepaintFront: 'Infinity', + waveformBarAnimate: true, + waveformBarBPM: true, + waveformBarInvertHalfbars: true, + waveformBarIndicator: false, + waveformBarRefreshRate: 200, + waveformBarRefreshRateVar: false, + waveformBarAutoDelete: false, + playbackOrder: 'default' + }; + + /** @public @type {object} Options > Player controls settings config name description. */ + this.themePlayerControlsComments = { + showPanelDetails_default: 'Values: true, false - Options > Player controls > Top menu > Default > Details', + showPanelDetails_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Details', + showPanelLibrary_default: 'Values: true, false - Options > Player controls > Top menu > Default > Library', + showPanelLibrary_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Library', + showPanelBiography_default: 'Values: true, false - Options > Player controls > Top menu > Default > Biography', + showPanelBiography_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Biography', + showPanelLyrics_default: 'Values: true, false - Options > Player controls > Top menu > Default > Lyrics', + showPanelLyrics_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Lyrics', + showPanelRating_default: 'Values: true, false - Options > Player controls > Top menu > Default > Rating', + showPanelRating_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Rating', + topMenuAlignment: 'Values: left, center - Options > Player controls > Top menu', + topMenuCompact: 'Values: true, false - Options > Player controls > Top menu > Compact top menu', + albumArtAlign: 'Values: "left", "leftMargin", "center", "right" - Options > Player controls > Album art > When player size is not proportional', + albumArtBg: 'Values: "left", "full", "none" - Options > Player controls > Album art > When player size is not proportional', + albumArtScale: 'Values: "cropped", "stretched", "proportional" - Options > Player controls > Album art > When player size is maximized/fullscreen', + albumArtAspectRatioLimit: 'Values: 1, 1.25, 1.5, 1.75, 2 - Options > Player controls > Album art > When player size is maximized/fullscreen > Keep wide and tall artworks proportional', + cycleArt: 'Values: min: 5, max: 120 in seconds - Options > Player controls > Album art > Cycle album artwork', + cycleArtMWheel: 'Values: true, false - Options > Player controls > Album art > Cycle album artwork with mouse wheel', + loadEmbeddedAlbumArtFirst: 'Values: true, false - Options > Player controls > Album art > Load embedded album art first', + showHiResAudioBadge: 'Values: true, false - Options > Player controls > Album art > Show hi-res audio badge on album cover > Enabled', + hiResAudioBadgeRound: 'Values: true, false - Options > Player controls > Album art > Show hi-res audio badge on album cover > Round', + hiResAudioBadgeSize: 'Values: "small", "normal", "large" - Options > Player controls > Album art > Show hi-res audio badge on album cover', + hiResAudioBadgePos: 'Values: "topleft", "topright", "bottomleft", "bottomright" - Options > Player controls > Album art > Show hi-res audio badge on album cover', + showPause: 'Values: true, false - Options > Player controls > Album art > Show pause on album cover', + jumpSearchIncludeLibrary: 'Values: true, false - Options > Player controls > Jump search > Include library in playlist search query', + jumpSearchIncludePlaylist: 'Values: true, false - Options > Player controls > Jump search > Include playlist in library search query', + jumpSearchComposerOnly: 'Values: true, false - Options > Player controls > Jump search > Composer only in jump search query', + playlistWheelScrollSteps: 'Values: 0.5, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Scrollbar > Playlist > Mouse wheel scroll steps', + playlistWheelScrollDuration: 'Values: 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 - Options > Player controls > Scrollbar > Playlist > Mouse wheel scroll smooth duration', + playlistAutoScrollNowPlaying: 'Values: true, false - Options > Player controls > Scrollbar > Playlist > Auto-scroll to current playing song', + playlistAutoHideScrollbar: 'Values: true, false - Options > Player controls > Scrollbar > Playlist > Auto-hide', + playlistSmoothScrolling: 'Values: true, false - Options > Player controls > Scrollbar > Playlist > Smooth scroll', + scrollStepLib: 'Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Scrollbar > Library > Mouse wheel scroll steps', + durationScrollLib: 'Values: 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 - Options > Player controls > Scrollbar > Library > Mouse wheel scroll smooth duration', + libraryAutoScrollNowPlaying: 'Values: true, false - Options > Player controls > Scrollbar > Library > Auto-scroll to current playing song', + libraryAutoHideScrollbar: 'Values: true, false - Options > Player controls > Scrollbar > Library > Auto-hide', + smoothLib: 'Values: true, false - Options > Player controls > Scrollbar > Library > Smooth scroll', + scrollStepBio: 'Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Scrollbar > Biography > Mouse wheel scroll steps', + durationScrollBio: 'Values: 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 - Options > Player controls > Scrollbar > Biography > Mouse wheel scroll smooth duration', + biographyAutoHideScrollbar: 'Values: true, false - Options > Player controls > Scrollbar > Biography > Auto-hide', + smoothBio: 'Values: true, false - Options > Player controls > Scrollbar > Biography > Smooth scroll', + showTooltipTruncated: 'Values: true, false - Options > Player controls > Tooltip > Show tooltips only on truncated text', + showTooltipTimeline: 'Values: true, false - Options > Player controls > Tooltip > Show timeline tooltips', + showTooltipVolume: 'Values: true, false - Options > Player controls > Tooltip > Show volume tooltips', + showTooltipVolumeInPercent: 'Values: true, false - Options > Player controls > Tooltip > Show volume tooltips in percent', + showTooltipMain: 'Values: true, false - Options > Player controls > Tooltip > Show main tooltips', + showTooltipLibrary: 'Values: true, false - Options > Player controls > Tooltip > Show library tooltips', + showTooltipBiography: 'Values: true, false - Options > Player controls > Tooltip > Show biography tooltips', + showStyledTooltips: 'Values: true, false - Options > Player controls > Tooltip > Show styled tooltips', + panelWidthAuto: 'Values: true, false - Options > Player controls > Panel > Width > Use auto panel width', + showPanelOnStartup: 'Values: "cover", "playlist", "details", "library", "biography", "lyrics" - Options > Player controls > Panel > Show panel on startup', + showPreloaderLogo: 'Values: true, false - Options > Player controls > Panel > Show logo on preloader', + returnToHomeOnPlaybackStop: 'Values: true, false - Options > Player controls > Panel > Return to home on playback stop', + addTracksPlaylistSwitch: 'Values: true, false - Options > Player controls > Panel > Switch to playlist when adding songs', + hideMiddlePanelShadow: 'Values: true, false - Options > Player controls > Panel > Hide middle panel shadow', + lockPlayerSize: 'Values: true, false - Options > Player controls > Panel > Lock player size', + maximizeToFullscreen: 'Values: true, false - not in Options - enable or disable the maximize to fullscreen function', + switchPlaybackTime: 'Values: true, false - not in Options - switch to playback time remaining when clicking on the playback time in the lower bar', + transportButtonSize_default: 'Values: 28, 30, 32, 34, 36, 38, 40, 42 - Options > Player controls > Transport button size > Default', + transportButtonSize_artwork: 'Values: 28, 30, 32, 34, 36 - Options > Player controls > Lower bar > Transport button size > Artwork', + transportButtonSize_compact: 'Values: 28, 30, 32, 34, 36 - Options > Player controls > Lower bar > Transport button size > Compact', + transportButtonSpacing_default: 'Values: 3, 5, 7, 10, 15 - Options > Player controls > Lower bar > Transport button spacing > Default', + transportButtonSpacing_artwork: 'Values: 3, 5, 7, 10, 15 - Options > Player controls > Lower bar > Transport button spacing > Artwork', + transportButtonSpacing_compact: 'Values: 3, 5, 7, 10, 15 - Options > Player controls > Lower bar > Transport button spacing > Compact', + showTransportControls_default: 'Values: true, false - Options > Player controls > Lower bar > Show transport controls > Default', + showTransportControls_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show transport controls > Artwork', + showTransportControls_compact: 'Values: true, false - Options > Player controls > Lower bar > Show transport controls > Compact', + showPlaybackOrderBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show playback order button > Default', + showPlaybackOrderBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show playback order button > Artwork', + showPlaybackOrderBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show playback order button > Compact', + showReloadBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show reload button > Default', + showReloadBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show reload button > Artwork', + showReloadBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show reload button > Compact', + showAddTracksBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show add tracks button > Default', + showAddTracksBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show add tracks button > Artwork', + showAddTracksBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show add tracks button > Compact', + showVolumeBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Default', + showVolumeBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Artwork', + showVolumeBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Compact', + autoHideVolumeBar: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Auto-hide bar', + showPlaybackTime_default: 'Values: true, false - Options > Player controls > Lower bar > Show playback time > Default', + showPlaybackTime_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show playback time > Artwork', + showPlaybackTime_compact: 'Values: true, false - Options > Player controls > Lower bar > Show playback time > Compact', + showLowerBarArtist_default: 'Values: true, false - Options > Player controls > Lower bar > Show artist > Default', + showLowerBarArtist_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show artist > Artwork', + showLowerBarArtist_compact: 'Values: true, false - Options > Player controls > Lower bar > Show artist > Compact', + showLowerBarTrackNum_default: 'Values: true, false - Options > Player controls > Lower bar > Show track number > Default', + showLowerBarTrackNum_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show track number > Artwork', + showLowerBarTrackNum_compact: 'Values: true, false - Options > Player controls > Lower bar > Show track number > Compact', + showLowerBarTitle_default: 'Values: true, false - Options > Player controls > Lower bar > Show song title > Default', + showLowerBarTitle_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show song title > Artwork', + showLowerBarTitle_compact: 'Values: true, false - Options > Player controls > Lower bar > Show song title > Compact', + showLowerBarComposer_default: 'Values: true, false - Options > Player controls > Lower bar > Show composer > Default', + showLowerBarComposer_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show composer > Artwork', + showLowerBarComposer_compact: 'Values: true, false - Options > Player controls > Lower bar > Show composer > Compact', + showLowerBarArtistFlags_default: 'Values: true, false - Options > Player controls > Lower bar > Show artist country flags > Default', + showLowerBarArtistFlags_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show artist country flags > Artwork', + showLowerBarArtistFlags_compact: 'Values: true, false - Options > Player controls > Lower bar > Show artist country flags > Compact', + showLowerBarVersion_default: 'Values: true, false - Options > Player controls > Lower bar > Show software version > Default', + showLowerBarVersion_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show software version > Artwork', + showLowerBarVersion_compact: 'Values: true, false - Options > Player controls > Lower bar > Show software version > Compact', + showProgressBar_default: 'Values: true, false - Options > Player controls > Lower bar > Show progress bar > Default', + showProgressBar_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show progress bar > Artwork', + showProgressBar_compact: 'Values: true, false - Options > Player controls > Lower bar > Show progress bar > Compact', + showPeakmeterBar_default: 'Values: true, false - Options > Player controls > Lower bar > Show peakmeter bar > Default', + showPeakmeterBar_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show peakmeter bar > Artwork', + showPeakmeterBar_compact: 'Values: true, false - Options > Player controls > Lower bar > Show peakmeter bar > Compact', + showWaveformBar_default: 'Values: true, false - Options > Player controls > Lower bar > Show waveform bar > Default', + showWaveformBar_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show waveform bar > Artwork', + showWaveformBar_compact: 'Values: true, false - Options > Player controls > Lower bar > Show waveform bar > Compact', + addTracksPlaylist: 'Values: "Favorites", "OrAnyOtherName", - Options > Player controls > Lower bar > Add tracks playlist', + seekbar: 'Values: "progressbar", "peakmeterBar", "waveformbar", - Options > Player controls > Seekbar > Type', + progressBarWheelSeekSpeed: 'Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Seekbar > Progress bar > Mouse wheel seek speed', + progressBarRefreshRate: 'Values: 1000, 500, 333, "variable", 100, 60, 30 - Options > Player controls > Seekbar > Progress bar > Refresh rate', + peakmeterBarDesign: 'Values: "horizontal", "horizontal_center", "vertical" - Options > Player controls > Seekbar > Peakmeter bar > Style', + peakmeterBarVertSize: 'Values: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, "min" - Options > Player controls > Seekbar > Peakmeter bar > Size', + peakmeterBarVertDbRange: 'Values: 220, 215, 210, 320, 315, 310, 520, 515, 510 - Options > Player controls > Seekbar > Peakmeter bar > Decibel range', + peakmeterBarOverBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show over bars', + peakmeterBarOuterBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show outer bars', + peakmeterBarOuterPeaks: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show outer peaks', + peakmeterBarMainBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show main bars', + peakmeterBarMainPeaks: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show main peaks', + peakmeterBarMiddleBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show middle bars', + peakmeterBarProgBar: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show progress bar', + peakmeterBarGaps: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show gaps', + peakmeterBarGrid: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show grid', + peakmeterBarInfo: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show info', + peakmeterBarVertPeaks: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show peaks', + peakmeterBarVertBaseline: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show baseline', + peakmeterBarRefreshRate: 'Values: 200, 150, 120, 100, 80, 60, 30 - Options > Player controls > Seekbar > Peakmeter bar > Refresh rate', + waveformBarMode: 'Values: "ffprobe", "audiowaveform", "visualizer" - Options > Player controls > Seekbar > Waveform bar > Mode', + waveformBarAnalysis: 'Values: "rms_level", "peak_level", "rms_peak" - Options > Player controls > Seekbar > Waveform bar > Analysis', + waveformBarDesign: 'Values: "waveform", "bars", "dots", "halfbars" - Options > Player controls > Seekbar > Waveform bar > Shape', + waveformBarSizeWave: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Waveform', + waveformBarSizeBars: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Bars', + waveformBarSizeDots: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Dots', + waveformBarSizeHalf: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Halfbars', + waveformBarSizeNormalize: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Size > Normalize width', + waveformBarPaint: 'Values: "full", "partial" - Options > Player controls > Seekbar > Waveform bar > Display', + waveformBarPrepaint: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Prepaint', + waveformBarPrepaintFront: 'Values: "Infinity", 2, 5, 10, - Options > Player controls > Seekbar > Waveform bar > Display > Prepaint front', + waveformBarAnimate: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Animate', + waveformBarBPM: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Use BPM', + waveformBarInvertHalfbars: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Invert halfbars', + waveformBarIndicator: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Show indicator', + waveformBarRefreshRate: 'Values: 1000, 500, 200, 100, 80, 60, 30, - Options > Player controls > Seekbar > Waveform bar > Refresh rate', + waveformBarRefreshRateVar: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Refresh rate > Variable', + playbackOrder: 'Values: "default", "repeatPlaylist", "repeatTrack", "shuffle" - not in Options - playback order state button' + }; + + /** @public @type {object} Options > Player controls settings config header description. */ + this.themePlayerControlsSchema = new ConfigurationObjectSchema('themeControls', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* PLAYER CONTROLS: ' + + '* Top menu Options > Player controls ' + + '* You can set and select between various player control settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * PLAYLIST * // + // #region PLAYLIST + /** @public @type {object} Options > Playlist settings with default values. */ + this.themePlaylistDefaults = { + playlistLayout: 'normal', + showPlaylistManager_default: true, + showPlaylistManager_artwork: false, + showPlaylistManager_compact: false, + showPlaylistHistory: true, + autoHidePlman: true, + show_album_art: true, + auto_album_art: false, + show_header: true, + show_rating_header: true, + show_PLR_header: false, + use_compact_header: false, + auto_collapse: false, + hyperlinksCtrlClick: false, + show_disc_header: true, + show_group_info: true, + showWeblinks: true, + showPlaylistFullDate: false, + show_row_stripes: false, + show_playcount: true, + show_queue_position: true, + show_rating: true, + use_rating_from_tags: false, + showPlaylistRatingGrid: false, + show_PLR: false, + showPlaylistTrackNumbers: true, + showPlaylistIndexNumbers: false, + showDifferentArtist: false, + showArtistPlaylistRows: false, + showAlbumPlaylistRows: false, + playlistTimeRemaining: false, + showVinylNums: true, + lastFmScrobblesFallback: true, + playlistRowHover: true, + playlistSortOrderAuto: false, + playlistSortOrder: '', + playlistSortOrderDirection: '_asc', + playlist_stats_include_artist: true, + playlist_stats_include_album : true, + playlist_stats_include_track: true, + playlist_stats_include_year: false, + playlist_stats_include_genre: false, + playlist_stats_include_label: false, + playlist_stats_include_country: false, + playlist_stats_include_stats: true, + playlist_stats_sort_by: '', + playlist_stats_sort_direction: '_dsc' + }; + + /** @public @type {object} Options > Playlist settings config name description. */ + this.themePlaylistComments = { + playlistLayout: 'Values: "normal", "full" - Options > Playlist > Layout', + showPlaylistManager_default: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist manager > Default', + showPlaylistManager_artwork: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist manager > Artwork', + showPlaylistManager_compact: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist manager > Compact', + showPlaylistHistory: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist history', + autoHidePlman: 'Values: true, false - Options > Playlist > Playlist manager > Auto-hide', + show_album_art: 'Values: true, false - Options > Playlist > Album header > Album art > Show', + auto_album_art: 'Values: true, false - Options > Playlist > Album header > Album art > Auto-hide when no cover', + show_header: 'Values: true, false - Options > Playlist > Album header > Album header', + show_rating_header: 'Values: true, false - Options > Playlist > Album Header > Show rating', + show_PLR_header: 'Values: true, false - Options > Playlist > Album Header > Show PLR value', + use_compact_header: 'Values: true, false - Options > Playlist > Album header > Compact header', + auto_collapse: 'Values: true, false - Options > Playlist > Album header > Auto collapse and expand', + hyperlinksCtrlClick: 'Values: true, false - Options > Playlist > Album header > Ctrl+click to follow hyperlinks', + show_disc_header: 'Values: true, false - Options > Playlist > Album header > Show disc sub-header', + show_group_info: 'Values: true, false - Options > Playlist > Album header > Show group info', + showWeblinks: 'Values: true, false - Options > Playlist > Album header > Show weblinks in context menu', + showPlaylistFullDate: 'Values: true, false - Options > Playlist > Album header > Show long release date (YYYY-MM-DD)', + show_row_stripes: 'Values: true, false - Options > Playlist > Track row > Show row stripes', + show_playcount: 'Values: true, false - Options > Playlist > Track row > Show play count', + show_queue_position: 'Values: true, false - Options > Playlist > Track row > Show queue position', + show_rating: 'Values: true, false - Options > Playlist > Track row > Show rating', + use_rating_from_tags: 'Values: true, false - Options > Playlist > Track row > Show rating from tags', + showPlaylistRatingGrid: 'Values: true, false - Options > Playlist > Track row > Show rating grid', + show_PLR: 'Values: true, false - Options > Playlist > Track row > Show PLR value', + showPlaylistTrackNumbers: 'Values: true, false - Options > Playlist > Track row > Show track numbers', + showPlaylistIndexNumbers: 'Values: true, false - Options > Playlist > Track row > Show index numbers', + showDifferentArtist: 'Values: true, false - Options > Playlist > Track row > Show artist name on difference', + showArtistPlaylistRows: 'Values: true, false - Options > Playlist > Track row > Show artist name in all rows', + showAlbumPlaylistRows: 'Values: true, false - Options > Playlist > Track row > Show album title in all rows', + playlistTimeRemaining: 'Values: true, false - Options > Playlist > Track row > Show time remaining on playing track', + showVinylNums: 'Values: true, false - Options > Playlist > Track row > Show vinyl style numbering if available', + lastFmScrobblesFallback: 'Values: true, false - Options > Playlist > Track row > Show last.fm scrobbles on no local plays', + playlistRowHover: 'Values: true, false - Options > Playlist > Track row > Row mouse hover', + playlistSortOrderAuto: 'Values: true, false - Options > Playlist > Sort order > Always auto-sort', + playlistSortOrder: 'Values: "", "default", "artistDate_asc", "artistDate_dsc", "album", "title", "trackNumber", "year_asc", "year_dsc", "filePath", "custom" - Options > Playlist > Sort order', + playlistSortOrderDirection: 'Values: "_asc", "_dsc" - Options > Playlist > Sort order', + playlist_stats_include_artist: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include artist', + playlist_stats_include_album: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include album', + playlist_stats_include_track: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include track', + playlist_stats_include_year: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include year', + playlist_stats_include_genre: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include genre', + playlist_stats_include_label: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include label', + playlist_stats_include_country: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include country', + playlist_stats_include_stats: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include stats', + playlist_stats_sort_by: 'Values: "", "artist", "albumTitle", "year", "genre" - playlist context menu - Write playlist statistics to text', + playlist_stats_sort_direction: 'Values: "_asc", "_dsc" - playlist context menu - Write playlist statistics to text' + }; + + /** @public @type {object} Options > Playlist settings config header description. */ + this.themePlaylistSchema = new ConfigurationObjectSchema('themePlaylist', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* PLAYLIST: ' + + '* Top menu Options > Playlist ' + + '* You can set and select between various playlist settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {PlaylistGroupingPresets[]} Playlist grouping presets with default entries and values. */ + this.themePlaylistGroupingPresets = [ + { + name: 'artist', + description: 'by artist', + group_query: '%album artist%', + title_query: '[%album artist%]', + sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", + show_date: false, + show_disc: false + }, + { + name: 'artist_album', + description: 'by artist / album', + group_query: '%album artist%%album%', + title_query: '[%album artist%]', + sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", + show_date: true, + show_disc: false + }, + { + name: 'artist_album_disc', + description: 'by artist / album / disc number', + group_query: '%album artist%%album%%discnumber%', + title_query: '[%album artist%]', + sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", + show_date: true, + show_disc: true + }, + { + name: 'artist_album_disc_edition', + description: 'by artist / album / disc number / edition / codec', + group_query: '%album artist%%album%%discnumber%%edition%%codec%', + title_query: '[%album artist%]', + sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", + show_date: true, + show_disc: true + }, + { + name: 'path', + description: 'by path', + group_query: '$directory_path(%path%)', + title_query: '[%album artist%]', + sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", + show_date: true, + show_disc: false + }, + { + name: 'date', + description: 'by date', + group_query: '%date%', + title_query: '[%album artist%]', + sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", + show_date: true, + show_disc: false + } + ]; + + /** @public @type {object} Playlist grouping presets config header description. */ + this.themePlaylistGroupingPresetsSchema = new ConfigurationObjectSchema('themePlaylistGroupingPresets', ConfigurationObjectType.Array, [ + { name: 'name' }, + { name: 'description' }, + { name: 'group_query' }, + { name: 'title_query' }, + { name: 'sub_title_query' }, + { name: 'show_date' }, + { name: 'show_disc' }], + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* PLAYLIST GROUPING PRESETS: ' + + '* You can add new entries with grouping patterns or reorder entries that will be displayed in the Playlist grouping manager. ' + + '* The playlist grouping manager can be accessed via right clicking in the Playlist > Grouping > Manage presets. ' + + '* Changes will be saved to the config file after reload when creating new grouping presets or modifications in the Playlist grouping manager. ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * DETAILS * // + // #region DETAILS + /** @public @type {object} Options > Details settings with default values. */ + this.themeDetailsDefaults = { + showDiscArtStub: true, + noDiscArtStub: false, + discArtStub: 'cdAlbumCover', + displayDiscArt: true, + discArtOnTop: false, + filterDiscJpgsFromAlbumArt: true, + spinDiscArt: false, + spinDiscArtImageCount: 72, + spinDiscArtRedrawInterval: 75, + rotateDiscArt: true, + rotationAmt: 3, + artRotateDelay: 30, + discArtDisplayAmount: 0.5, + detailsAlbumArtOpacity: 255, + detailsAlbumArtDiscAreaOpacity: 255, + showGridArtist_default: false, + showGridArtist_artwork: false, + showGridTitle_default: false, + showGridTrackNum_default: false, + showGridTrackNum_artwork: false, + showGridTitle_artwork: false, + showGridPlayingPlaylist: false, + showGridTimeline_default: true, + showGridTimeline_artwork: true, + showGridArtistFlags_default: true, + showGridArtistFlags_artwork: true, + showGridReleaseFlags_default: 'logo', + showGridReleaseFlags_artwork: 'logo', + showGridCodecLogo_default: 'logo', + showGridCodecLogo_artwork: 'logo', + showGridChannelLogo_default: 'logo', + showGridChannelLogo_artwork: 'logo', + autoHideGridMetadata: true, + noDiscArtBg: true, + labelArtOnBg: false + }; + + /** @public @type {object} Options > Details settings config name description. */ + this.themeDetailsComments = { + showDiscArtStub: 'Values: true, false - Options > Details > Disc art > Disc art placeholder > Show placeholder if no disc art found', + noDiscArtStub: 'Values: true, false - Options > Details > Disc art > Disc art placeholder > No placeholder', + discArtStub: 'Values: "cdAlbumCover", "cdWhite", "cdBlack", "cdBlank", "cdTrans", "vinylAlbumCover", "vinylWhite", "vinylVoid", "vinylColdFusion", "vinylRingOfFire", "vinylMaple", "vinylBlack", "vinylBlackHole", "vinylEbony", "vinylTrans" - Options > Details > Disc art > Disc art placeholder', + displayDiscArt: 'Values: true, false - Options > Details > Disc art > Display disc art if found', + discArtOnTop: 'Values: true, false - Options > Details > Disc art > Display disc art above cover', + filterDiscJpgsFromAlbumArt: 'Values: true, false - Options > Details > Disc art > Filter cd/disc/vinyl .jpgs from artwork', + spinDiscArt: 'Values: true, false - Options > Details > Disc art > Spin disc art while songs play (increases memory and CPU)', + spinDiscArtImageCount: 'Values: 36, 45, 60, 72, 90, 120, 180 - Options > Details > Disc art > # Rotation images (memory usage/rotational speed)', + spinDiscArtRedrawInterval: 'Values: 250, 200, 150, 125, 100, 75, 50, 40, 30, 20, 10 - Options > Details > Disc art > Spinning disc art redraw speed', + rotateDiscArt: 'Values: true, false - Options > Details > Disc art > Rotate disc art as tracks change', + rotationAmt: 'Values: 2, 3, 4, 5 - Options > Details > Disc art > Disc art rotation amount', + artRotateDelay: 'Values: free to choose - not in Options - seconds to display each art', + discArtDisplayAmount: 'Values: 1, 0.5, 0.455, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1 - Options > Details > Disc art > Disc art display amount', + detailsAlbumArtOpacity: 'Values: 255, 230, 204, 178, 153, 128, 102, 76, 51, 25 - from 100% - 10% - Options > Details > Album art > Full artwork opacity', + detailsAlbumArtDiscAreaOpacity: 'Values: 255, 230, 204, 178, 153, 128, 102, 76, 51, 25 - from 100% - 10% - Options > Details > Album art > Disc area opacity', + showGridArtist_default: 'Values: true, false - Options > Details > Metadata grid > Show artist - when Default layout is active', + showGridArtist_artwork: 'Values: true, false - Options > Details > Metadata grid > Show artist - when Artwork layout is active', + showGridTrackNum_default: 'Values: true, false - Options > Details > Metadata grid > Show track number - when Default layout is active', + showGridTrackNum_artwork: 'Values: true, false - Options > Details > Metadata grid > Show track number - when Artwork layout is active', + showGridTitle_default: 'Values: true, false - Options > Details > Metadata grid > Show song title - when Default layout is active', + showGridTitle_artwork: 'Values: true, false - Options > Details > Metadata grid > Show song title - when Artwork layout is active', + showGridPlayingPlaylist: 'Values: true, false - Options > Details > Metadata grid > Show playing playlist', + showGridTimeline_default: 'Values: true, false - Options > Details > Metadata grid > Show timeline - when Default layout is active', + showGridTimeline_artwork: 'Values: true, false - Options > Details > Metadata grid > Show timeline - when Artwork layout is active', + showGridArtistFlags_default: 'Values: true, false - Options > Details > Metadata grid > Show artist country flags - when Default layout is active', + showGridArtistFlags_artwork: 'Values: true, false - Options > Details > Metadata grid > Show artist country flags - when Artwork layout is active', + showGridReleaseFlags_default: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show release country flags - when Default layout is active', + showGridReleaseFlags_artwork: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show release country flags - when Artwork layout is active', + showGridCodecLogo_default: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show codec logo - when Default layout is active', + showGridCodecLogo_artwork: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show codec logo - when Artwork layout is active', + showGridChannelLogo_default: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show channel logo - when Default layout is active', + showGridChannelLogo_artwork: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show channel logo - when Artwork layout is active', + autoHideGridMetadata: 'Values: true, false - Options > Details > Metadata grid > Auto-hide full metadata on small player', + noDiscArtBg: 'Values: true, false - Options > Details > Background > Show full background when no disc art', + labelArtOnBg: 'Values: true, false - Options > Details > Background > Show label art on background' + }; + + /** @public @type {object} Options > Details settings config header description. */ + this.themeDetailsSchema = new ConfigurationObjectSchema('themeDetails', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* DETAILS: ' + + '* Top menu Options > Details ' + + '* You can set and select between various details settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** + * @typedef {object} MetadataGridEntry + * @property {string} label Text that shows in the left column of the metadata grid. + * @property {string} val Evaluated text in the right column. If this evaluates to an empty string, the entry is not shown. + * @property {boolean} [age] If True, appends the "(1y 10, 23d)" style text to the evaluated val. Only valid for date strings. + * @property {string} [comment] Optional comment for the .jsonc file. + */ + /** @public @type {MetadataGridEntry[]} Metadata grid default entries and values. */ + this.metadataGridDefaults = [ + { label: 'Disc', val: `$if(${grTF.disc_subtitle},[Disc %discnumber% \u2013 ]${grTF.disc_subtitle})` }, + { label: 'Rel. Type', val: '$if($stricmp(%releasetype%,Album),,[%releasetype%])' }, + { label: 'Year', val: `$puts(d,${grTF.date})$if($strcmp($year($get(d)),$get(d)),$get(d),)`, comment: '\'Year\' is shown if the date format is YYYY' }, + { label: 'Rel. Date', val: `$puts(d,${grTF.date})$if($strcmp($year($get(d)),$get(d)),,$get(d))`, age: true, comment: '\'Release Date\' is shown if the date format is YYYY-MM-DD' }, + { label: 'Edition', val: grTF.edition }, + { label: 'Label', val: '[$if($meta(label),$meta_sep(label, \u00B7 ),$if3(%publisher%,%discogs_label%,))]', comment: 'The label(s) or publisher(s) that released the album.' }, + { label: 'Catalog', val: `$puts(cn,$if3(%catalognumber%,%discogs_catalog%,))[$if($get(cn),$get(cn)[ / ${grTF.releaseCountry}],)]` }, + { label: 'Rel. Country', val: `$puts(cn,$if3(%catalognumber%,%discogs_catalog%,))[$if($get(cn),,$replace(${grTF.releaseCountry},XW,))]`, comment: 'Only shown if %catalognumber% or %discogs_catalog% is not present. If release country is entire world (\'XW\') value is hidden.' }, + { label: 'Track', val: '$if(%tracknumber%,$num(%tracknumber%,1)$if(%totaltracks%,/$num(%totaltracks%,1))$ifgreater(%totaldiscs%,1, CD %discnumber%/$num(%totaldiscs%,1),)' }, + { label: 'Genre', val: '[$meta_sep(genre, \u00B7 )]' }, + { label: 'Style', val: '[$meta_sep(style, \u00B7 )]' }, + { label: 'Release', val: '[%release%]' }, + { label: 'Codec', val: '[%codec%]' }, + { label: 'Channels', val: '[%channels%]' }, + { label: 'Source', val: '[%codec_profile%$if(%__bitspersample%, \u00B7 )]$if($strcmp(%__encoding%,lossless),%__bitspersample% bit)$ifgreater(%samplerate%,44100,$if($if2(%codec_profile%,%__bitspersample%), \u00B7 )$div(%samplerate%,1000)$replace($insert($right($div(%samplerate%,100),1),.,0),.0,) kHz,)[ \u00B7 $if3(%media%,%mediatype%,%media type%)]' }, + { label: 'Data', val: '%__bitrate% kbps \u00B7 $div(%filesize%,1048576).$num($div($mul($mod(%filesize%,1048576),10),1048576),0) MB' }, + { label: 'Added', val: '[$if2(%added_enhanced%,%added%)]', age: true }, + { label: 'Last Played', val: `[${grTF.last_played}]`, age: true }, + { label: 'Hotness', val: "$puts(X,5)$puts(Y,$div(%_dynamic_rating%,400))$repeat($repeat(I,$get(X)) ,$div($get(Y),$get(X)))$repeat(I,$mod($get(Y),$get(X)))$ifgreater(%_dynamic_rating%,0, $replace($div(%_dynamic_rating%,1000)'.'$mod($div(%_dynamic_rating%,100),10),0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9),)" }, + { label: 'View Count', val: '[%fy_view_count%]' }, + { label: 'Likes', val: '[$if(%fy_like_count%,%fy_like_count% \u25B2 / %fy_dislike_count% \u25BC,)]' }, + { label: 'Play Count', val: '$if($or(%play_count%,%lastfm_play_count%),$puts(X,5)$puts(Y,$max(%play_count%,%lastfm_play_count%))$ifgreater($get(Y),30,,$repeat($repeat(I,$get(X)) ,$div($get(Y),$get(X)))$repeat(I,$mod($get(Y),$get(X))) )$get(Y))' }, + { label: 'Rating', val: '$if(%rating%,$repeat(\u2605 ,%rating%))' }, + { label: 'Mood', val: '$if(%mood%,$puts(X,5)$puts(Y,$mul(5,%mood%))$repeat($repeat(I,$get(X)) ,$div($get(Y),$get(X)))$repeat(I,$mod($get(Y),$get(X)))$replace(%mood%,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9))' }, + { label: 'Playing List', val: grTF.playing_playlist }, + { label: 'Blank 01', val: '' }, + { label: 'Blank 02', val: '' }, + { label: 'Blank 03', val: '' }, + { label: 'Blank 04', val: '' }, + { label: 'Blank 05', val: '' }, + { label: 'Blank 06', val: '' }, + { label: 'Blank 07', val: '' }, + { label: 'Blank 08', val: '' } + ]; + + /** @public @type {object} Metadata grid config header description. */ + this.metadataGridSchema = new ConfigurationObjectSchema('metadataGrid', ConfigurationObjectType.Array, [ + { name: 'label' }, + { name: 'val' }, + { name: 'age', optional: true }], + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* DETAILS METADATA GRID: ' + + '* You can add new tags or reorder entries that will be displayed in the metadata grid in top menu Details. ' + + '* If there are too many entries and no space available in Details, tags will be hidden. You can change to a larger player size. ' + + '* Entries that evaluate to an empty string will not be shown in the grid. ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * LIBRARY * // + // #region LIBRARY + /** @public @type {object} Options > Library settings with default values. */ + this.themeLibraryDefaults = { + libraryLayout: 'normal', + libraryLayoutFullPreset: true, + libraryLayoutSplitPreset: true, + libraryLayoutSplitPreset2: false, + libraryLayoutSplitPreset3: false, + libraryLayoutSplitPreset4: false, + libraryDesign: 'reborn', + libraryTheme: 0, + libraryThumbnailSize: 'auto', + libraryThumbnailBorder: 'border', + albumArtShow: false, + itemOverlayType: 0, + albumArtLetter: true, + albumArtLetterNo: 1, + artId: 0, + albumArtGrpLevel: 0, + imgStyleFront: 1, + imgStyleBack: 1, + imgStyleDisc: 1, + imgStyleIcon: 1, + imgStyleArtist: 1, + albumArtLabelType: 1, + albumArtFlipLabels: false, + actionMode: 0, + clickAction: 0, + dblClickAction: 1, + mbtnClickAction: 1, + altClickAction: 1, + autoPlay: true, + keyAction: 0, + rememberTree: false, + artTreeSameView: false, + presetLoadCurView: true, + rootNode: 3, + nodeCounts: 1, + countsRight: true, + autoCollapse: false, + itemShowStatistics: 0, + highLightNowplaying: true, + showTracks: true, + rowStripes: false, + fullLineSelection: true, + libraryRowHover: true, + filterBy: 0, + sortOrder: 'default', + yearBeforeAlbum: true, + albumArtViewBy: 0, + treeViewBy: 0, + librarySource: 1, + librarySourceFixedPlaylist: false, + librarySourceFixedPlaylistName: '' + }; + + /** @public @type {object} Options > Library settings config name dscription. */ + this.themeLibraryComments = { + libraryLayout: 'Values: "normal", "full", "split" - Options > Library > Layout', + libraryLayoutFullPreset: 'Values: true, false - Options > Library > Layout > Use full preset', + libraryLayoutSplitPreset: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (collapse)', + libraryLayoutSplitPreset2: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (text)', + libraryLayoutSplitPreset3: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (art grid)', + libraryLayoutSplitPreset4: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (art header)', + libraryDesign: 'Values: "reborn", "traditional", "modern", "ultraModern", "clean", "facet", "coversLabelsRight", "coversLabelsBottom", "coversLabelsBlend", "flowMode" - Options > Library > Design', + libraryTheme: 'Values: 0, 1, 2, 3, 4, 5 - Options > Library > Theme', + libraryThumbnailSize: 'Values: "auto", "playlist", 0, 1, 2, 3, 4, 5, 6, 7 - Options > Library > Album art > Thumbnail size', + libraryThumbnailBorder: 'Values: "none", "border", "shadow" - Options > Library > Album art > Thumbnail border', + albumArtShow: 'Values: true, false - Options > Library > Album art > Activate option or change design to album art', + itemOverlayType: 'Values: 0, 1, 2 - Options > Library > Album art > Overlay', + albumArtLetter: 'Values: true, false - Options > Library > Album art > Index > Show on scrollbar drag', + albumArtLetterNo: 'Values: 0, 1 - Options > Library > Album art > Index', + artId: 'Values: 0, 1, 2, 3, 4 - Options > Library > Album art > View', + albumArtGrpLevel: 'Values: 0, 1, 2 - Options > Library > Album art > View', + imgStyleFront: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Front', + imgStyleBack: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Back', + imgStyleDisc: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Disc', + imgStyleIcon: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Icon', + imgStyleArtist: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Artist', + albumArtLabelType: 'Values: 1, 2, 3, 4, 0 - Options > Library > Album art > Labels', + albumArtFlipLabels: 'Values: true, false - Options > Library > Album art > Labels', + actionMode: 'Values: 0, 1, 2 - Options > Library > Controls > Action mode', + clickAction: 'Values: 0, 1, 2, 3 - Options > Library > Controls > Single-click action', + dblClickAction: 'Values: 0, 1, 2, 3 - Options > Library > Controls > Double-click action', + mbtnClickAction: 'Values: 0, 1, 2 - Options > Library > Controls > Middle-mouse click action', + altClickAction: 'Values: 0, 1, 2 - Options > Library > Controls > Alt + mouse click action', + autoPlay: 'Values: true, false - Options > Library > Controls > Keystroke action > Play on Enter or send from menu', + keyAction: 'Values: 0, 1 - Options > Library > Controls > Keystroke action', + rememberTree: 'Values: true, false - Options > Library > Controls > Always remember library state', + artTreeSameView: 'Values: true, false - Options > Library > Controls > Always load View by same as tree', + presetLoadCurView: 'Values: true, false - Options > Library > Controls > Always load preset with current view pattern', + rootNode: 'Values: 0, 1, 2, 3 - Options > Library > Track row > Node root type', + nodeCounts: 'Values: 0, 1, 2 - Options > Library > Track row > Node item counts', + countsRight: 'Values: true, false - Options > Library > Track row > Node item counts position', + autoCollapse: 'Values: true, false - Options > Library > Track row > Node auto collapse', + itemShowStatistics: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 - Options > Library > Track row > Statistics', + highLightNowplaying: 'Values: true, false - Options > Library > Track row > Show now playing', + showTracks: 'Values: true, false - Options > Library > Track row > Show tracks when expanding nodes', + rowStripes: 'Values: true, false - Options > Library > Track row > Show row stripes', + fullLineSelection: 'Values: true, false - Options > Library > Track row > Row fully clickable', + libraryRowHover: 'Values: true, false - Options > Library > Track row > Row mouse hover', + filterBy: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - Options > Library > Filter order', + sortOrder: 'Values: 0, 1, 2, 3, 4 - Options > Library > Sort order', + yearBeforeAlbum: 'Values: true, false - Options > Library > Sort order', + albumArtViewBy: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Library > View order', + treeViewBy: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Library > View order', + librarySource: 'Values: 0, 1 - Options > Library > Source', + librarySourceFixedPlaylist: 'Values: true, false - not in Options, managed by Library source menu', + librarySourceFixedPlaylistName: 'Values: "selected playlist name" - not in Options, managed by Library source menu' + }; + + /** @public @type {object} Options > Library settings config header dscription. */ + this.themeLibrarySchema = new ConfigurationObjectSchema('themeLibrary', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* LIBRARY: ' + + '* Top menu Options > Library ' + + '* You can set and select between various library settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * BIOGRAPHY * // + // #region BIOGRAPHY + /** @public @type {object} Options > Biography settings with default values. */ + this.themeBiographyDefaults = { + biographyLayout: 'normal', + biographyLayoutFullPreset: true, + style: 0, + filmStripPos: 3, + filmStripOverlay: false, + biographyTheme: 0, + biographyDisplay: 'Image+text', + showFilmStrip: false, + imgSeekerShow: 0, + heading: 1, + summaryShow: true, + summaryCompact: true, + artistView: true, + focus: false, + lockBio: false, + sourceAll: false, + classicalMusicMode: false, + cycPhotoLocation: 0, + covType: 0, + loadCovAllFb: false, + loadCovFolder: false, + artStyleDual: 1, + artReflDual: false, + artShadowDual: false, + covStyleDual: 1, + covReflDual: false, + covShadowDual: false, + artStyleImgOnly: 1, + artReflImgOnly: false, + artShadowImgOnly: false, + covStyleImgOnly: 1, + covReflImgOnly: false, + covShadowImgOnly: false, + filmPhotoStyle: 1, + filmCoverStyle: 1, + photoNum: 10, + cycPic: true, + imgSmoothTrans: false, + cycTimePic: 15 + }; + + /** @public @type {object} Options > Biography settings config name description. */ + this.themeBiographyComments = { + biographyLayout: 'Values: "normal", "full" - Options > Biography > Layout', + biographyLayoutFullPreset: 'Values: true, false - Options > Biography > Layout > Use full preset', + style: 'Values: 0, 1, 2, 3, 4, 5 - Options > Biography > Layout', + filmStripPos: 'Values: 0, 1, 2, 3 - Options > Biography > Layout > Filmstrip', + filmStripOverlay: 'Values: true, false - Options > Biography > Layout > Filmstrip > Overlay image area', + biographyTheme: 'Values: 0, 1, 2, 3 - Options > Biography > Theme', + biographyDisplay: 'Values: "Image+text", "Image", "Text" - Options > Biography > Display', + showFilmStrip: 'Values: true, false - Options > Biography > Display', + imgSeekerShow: 'Values: 0, 1 - Options > Biography > Display', + heading: 'Values: 0, 1 - Options > Biography > Display', + summaryShow: 'Values: true, false - Options > Biography > Display', + summaryCompact: 'Values: true, false - Options > Biography > Display', + artistView: 'Values: true, false - Options > Biography > Display', + focus: 'Values: true, false - Options > Biography > Display', + lockBio: 'Values: true, false - Options > Biography > Source > Text', + sourceAll: 'Values: true, false - Options > Biography > Source > Text > Amalgamate', + classicalMusicMode: 'Values: true, false - Options > Biography > Source > Text > Prefer composition (allmusic && wikipedia review)', + cycPhotoLocation: 'Values: 0, 1, 2 - Options > Biography > Source > Photo', + covType: 'Values: 0, 1, 2, 3, 4 - Options > Biography > Source > Cover', + loadCovAllFb: 'Values: true, false - Options > Biography > Source > Cover > Cycle above', + loadCovFolder: 'Values: true, false - Options > Biography > Source > Cover > Cycle from download folder', + artStyleDual: 'Values: 0, 1, 2 - Options > Biography > Image > Image + text > Photo', + artReflDual: 'Values: true, false - Options > Biography > Image > Image + text > Reflection (Photo)', + artShadowDual: 'Values: true, false - Options > Biography > Image > Image + text > Shadow (Photo)', + covStyleDual: 'Values: 0, 1, 2 - Options > Biography > Image > Image + text > Cover', + covReflDual: 'Values: true, false - Options > Biography > Image > Image + text > Reflection (Cover)', + covShadowDual: 'Values: true, false - Options > Biography > Image > Image + text > Shadow (Cover)', + artStyleImgOnly: 'Values: 0, 1, 2 - Options > Biography > Image > Image only > Photo', + artReflImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Reflection (Photo)', + artShadowImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Shadow (Photo)', + covStyleImgOnly: 'Values: 0, 1, 2 - Options > Biography > Image > Image only > Cover', + covReflImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Reflection (Cover)', + covShadowImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Shadow (Cover)', + filmPhotoStyle: 'Values: 0, 1, 2 - Options > Biography > Image > Filmstrip > Photo', + filmCoverStyle: 'Values: 0, 1, 2 - Options > Biography > Image > Filmstrip > Cover', + photoNum: 'Values: 5, 10, 15, 20 - Options > Biography > Image > Downloads', + cycPic: 'Values: true, false - Options > Biography > Image > Auto cycle > Auto cycle', + imgSmoothTrans: 'Values: true, false - Options > Biography > Image > Auto cycle > Smooth transition', + cycTimePic: 'Values: 5, 10, 15, 30, 60 - Options > Biography > Image > Auto cycle' + }; + + /** @public @type {object} Options > Biography settings config header description. */ + this.themeBiographySchema = new ConfigurationObjectSchema('themeBiography', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* BIOGRAPHY: ' + + '* Top menu Options > Biography ' + + '* You can set and select between various biography settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * LYRICS * // + // #region LYRICS + /** @public @type {object} Options > Lyrics settings with default values. */ + this.themeLyricsDefaults = { + lyricsLayout: 'normal', + lyricsDropShadowLevel: 2, + lyricsFadeScroll: true, + lyricsLargerCurrentSync: true, + lyricsAlbumArt: true, + lyricsRememberPanelState: false, + lyricsScrollSpeed: 'normal', + lyricsScrollRateAvg: 750, + lyricsScrollRateMax: 375 + }; + + /** @public @type {object} Options > Lyrics settings config name dscription. */ + this.themeLyricsComments = { + lyricsLayout: 'Values: "normal", "full" - Options > Lyrics > Layout', + lyricsDropShadowLevel: 'Values: 0, 1, 2, 3 - Options > Lyrics > Display > Show drop shadow', + lyricsFadeScroll: 'Values: true, false - Options > Lyrics > Display > Show fade scroll', + lyricsLargerCurrentSync: 'Values: true, false - Options > Lyrics > Display > Larger current sync', + lyricsAlbumArt: 'Values: true, false - Options > Lyrics > Display > Show lyrics on album art', + lyricsRememberPanelState: 'Values: true, false - Options > Lyrics > Display > Remember lyrics panel state', + lyricsScrollSpeed: 'Values: "fastest", "fast", "normal", "slow", "slowest" - Options > Lyrics > Scroll speed', + lyricsScrollRateAvg: 'Values: false, 300, 500, 750, 1000, 1500 - not in Options, set by lyricsScrollSpeed', + lyricsScrollRateMax: 'Values: false, 150, 250, 375, 500, 725 - not in Options, set by lyricsScrollSpeed' + }; + + /** @public @type {object} Options > Lyrics settings config header dscription. */ + this.themeLyricsSchema = new ConfigurationObjectSchema('themeLyrics', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* LYRICS: ' + + '* Top menu Options > Lyrics ' + + '* You can set and select between various lyrics settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {array} Lyrics filename defaults. */ + this.lyricsFilenameDefaults = [ + '%title%', + '%artist% - %title%', + '%artist% -%title%', + '%tracknumber% - %title%', + '%tracknumber% - %artist% - %title%' + ]; + + /** @public @type {object} Lyrics filename config header description. */ + this.lyricsFilenameSchema = new ConfigurationObjectSchema('lyricsFilenamePatterns', ConfigurationObjectType.Array, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* LYRICS TITLE FORMATTING: ' + + '* The title formatting defined patterns for the names of lyrics files. Do not include file extensions. ' + + '* Special characters which are not allowed in filenames (i.e. / : " etc.) will be stripped from the filenames automatically and replaced with underscores. ' + + '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * SETTINGS * // + // #region SETTINGS + /** @public @type {object} Options > Settings with default values. */ + this.themeSettingsDefaults = { + themeDayNightMode: false, + customThemeFonts: false, + customPreloaderLogo: false, + customThemeImages: false, + albumArtDiskCache: true, + albumArtPreLoad: false, + customLibraryDir: false, + libraryAutoDelete: false, + customBiographyDir: false, + biographyAutoDelete: false, + customLyricsDir: false, + lyricsAutoDelete: false, + customWaveformBarDir: false, + waveformBarAutoDelete: false, + themePerformance: 'balanced', + devTools: false, + disableRightClick: true + }; + + /** @public @type {object} Options > Settings config name description. */ + this.themeSettingsComments = { + themeDayNightMode: 'Values: false, or a custom string value in 24 hour time format e.g "6-18" - Options > Settings > Theme day/night mode', + customThemeFonts: 'Values: true, false - Options > Settings > Theme fonts > Use custom theme fonts', + customPreloaderLogo: 'Values: true, false - Options > Settings > Theme images > Use custom preloader logo', + customThemeImages: 'Values: true, false - Options > Settings > Theme images > Use custom theme images', + albumArtDiskCache: 'Values: true, false - Options > Settings > Theme cache > Library > Image disk cache enabled', + albumArtPreLoad: 'Values: true, false - Options > Settings > Theme cache > Library > Preload images in disk cache', + customLibraryDir: 'Values: true, false - Options > Settings > Theme cache > Library > Use custom library directory', + libraryAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Library > Auto-delete library cache on startup', + customBiographyDir: 'Values: true, false - Options > Settings > Theme cache > Library > Use custom biography directory', + biographyAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Biography > Auto-delete biography cache on startup', + customLyricsDir: 'Values: true, false - Options > Settings > Theme cache > Library > Use custom lyrics directory', + lyricsAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Lyrics > Auto-delete lyrics cache on startup', + customWaveformBarDir: 'Values: true, false - Options > Settings > Theme cache > Waveform bar > Use custom waveform bar directory', + waveformBarAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Waveform bar > Auto-delete waveform bar cache on startup', + themePerformance: 'Values: "lowestQuality", "lowQuality", "balanced", "highQuality", "highestQuality" - Options > Settings > Theme performance', + devTools: 'Values: true, false - Options > Settings > Developer tools', + disableRightClick: 'Values: true, false - Options > Settings > Disable right-click' + }; + + /** @public @type {object} Options > Settings config header description. */ + this.themeSettingsSchema = new ConfigurationObjectSchema('themeSettings', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* SETTINGS: ' + + '* Top menu Options > Settings ' + + '* You can set and select between various lyrics settings that will be used. ' + + '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} General settings with default values. */ + this.settingsDefaults = { + artworkDisplayTime: 30, + discArtBasename: 'cd', + playlistCustomHeaderInfo: '', + playlistCustomTitle: '', + playlistCustomTitleNoHeader: '', + playlistSortDefault: '$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortArtistDate_asc: '$if2(%artist sort order%,%album artist%) $if3(%album sort order%,%original release date%,%originaldate%,%date%) %album% %edition% %codec% %discnumber% %tracknumber%', + playlistSortArtistDate_dsc: '$if2(%artist sort order%,%album artist%) $if3(%album sort order%,$sub(99999,%original release date%),$sub(99999,%originaldate%),$sub(99999,%date%)) %album% %edition% %codec% %discnumber% %tracknumber%', + playlistSortAlbumTitle: '%album% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortAlbumRating_asc: '%albumrating% $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortAlbumRating_dsc: '$sub(99999,%albumrating%) $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortAlbumPlaycount_asc: '%albumplaycount% $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortAlbumPlaycount_dsc: '$sub(99999,%albumplaycount%) $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortTrackTitle: '%title% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortTrackNumber: '%tracknumber% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortTrackRating_asc: '%rating% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortTrackRating_dsc: '$sub(99999,%rating%) $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortTrackPlaycount_asc: '%play_count% $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortTrackPlaycount_dsc: '$sub(99999,%play_count%) $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortYear_asc: '%year% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortYear_dsc: '$sub(99999,%year%) $if3($sub(99999,%original release date%),$sub(99999,%originaldate%),$sub(99999,%date%)) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortGenre: '%genre% %artist% %album% %edition% %codec% %discnumber% %tracknumber%', + playlistSortLabel: '%label% %artist% %album% %edition% %codec% %discnumber% %tracknumber%', + playlistSortCountry: '%artistcountry% %artist% %album% %edition% %codec% %discnumber% %tracknumber%', + playlistSortFilePath: '%path_sort% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', + playlistSortCustom: '$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', + playlistShowBitSampleAlways: false, + extraTrackInfo: '$ifequal(%samplerate%,44100,, |$ifgreater($info(bitspersample),16, $info(bitspersample)bit,) $div(%samplerate%,1000).$left($right(%samplerate%,3),1)kHz)[ | $replace(%replaygain_album_gain%, dB,dB)]', + hideCursor: false, + hidePanelBgWhenCollapsed: false, + doubleClickRefresh: false, + showDebugLog: false, + showDebugThemeLog: false, + showDebugThemeOverlay: false, + stoppedString1: 'foobar2000', + stoppedString1acr: 'fb2k', + stoppedString2: '$replace(%_foobar2000_version%,foobar2000 ,)' + }; + + /** @public @type {object} General settings config name description. */ + this.settingsComments = { + artworkDisplayTime: 'Number of seconds to show each image if more than one is found and "Cycle through all artwork" option is enabled. (Min: 5, Max: 120)', + discArtBasename: 'Do not include extension. Example: "discart", if the image provider uses that name for saving discArt and you want those filtered from showing up as albumArt. Would also filter out discart1.png, etc.', + playlistCustomHeaderInfo: 'You can use your own custom pattern for the playlist header info', + playlistCustomTitle: 'You can use your own custom title pattern for the playlist row', + playlistCustomTitleNoHeader: 'You can use your own custom title pattern for the playlist row - when playlist header is not being displayed', + playlistSortDefault: 'Options > Playlist > Sort order > Default - sort pattern to sort playlists generated from Library selections or clicking on hyperlinks in the Playlist', + playlistSortArtistDate_asc: 'Options > Playlist > Sort order > Artist | date ascending', + playlistSortArtistDate_dsc: 'Options > Playlist > Sort order > Artist | date descending', + playlistSortAlbumTitle: 'Options > Playlist > Sort order > Album', + playlistSortAlbumRating_asc: 'Options > Playlist > Sort order > Album rating | ascending', + playlistSortAlbumRating_dsc: 'Options > Playlist > Sort order > Album rating | descending', + playlistSortAlbumPlaycount_asc: 'Options > Playlist > Sort order > Album playcount | ascending', + playlistSortAlbumPlaycount_dsc: 'Options > Playlist > Sort order > Album playcount | descending', + playlistSortTrackTitle: 'Options > Playlist > Sort order > Track', + playlistSortTrackNumber: 'Options > Playlist > Sort order > Track number', + playlistSortTrackRating_asc: 'Options > Playlist > Sort order > Track rating | ascending', + playlistSortTrackRating_dsc: 'Options > Playlist > Sort order > Track rating | descending', + playlistSortTrackPlaycount_asc: 'Options > Playlist > Sort order > Track playcount | ascending', + playlistSortTrackPlaycount_dsc: 'Options > Playlist > Sort order > Track playcount | descending', + playlistSortYear_asc: 'Options > Playlist > Sort order > Year | ascending', + playlistSortYear_dsc: 'Options > Playlist > Sort order > Year | descending', + playlistSortGenre: 'Options > Playlist > Sort order > Genre', + playlistSortLabel: 'Options > Playlist > Sort order > Label', + playlistSortCountry: 'Options > Playlist > Sort order > Country', + playlistSortFilePath: 'Options > Playlist > Sort order > File path', + playlistSortCustom: 'Options > Playlist > Sort order > Custom', + playlistShowBitSampleAlways: 'Always show the bit depth and sample rate in the playlist header.', + extraTrackInfo: 'Portion of the trackInfo in the upper right, directly under the year. Only part of the info string is customizable', + hideCursor: 'Hides the mouse cursor when song is playing after 10 seconds of no mouse activity', + hidePanelBgWhenCollapsed: 'Hides panel background when playing an album and the playlist or library view is active', + doubleClickRefresh: 'Enables refreshing the theme by double-clicking, for example, on the lower bar', + showDebugLog: 'Enables extra logging in the console. Probably not needed unless you encounter a problem or you\'re asked to enable it.', + showDebugThemeLog: 'Logs the output of the algorithm which determines the primary theme color.', + showDebugThemeOverlay: 'Displays various theme debug logs on the album art as an overlay.', + stoppedString1: 'The bolded portion of text shown above the progress bar when nothing is playing', + stoppedString2: 'The second (non-bold) portion of text shown above the progress bar when nothing is playing' + }; + + /** @public @type {object} General settings config header description. */ + this.settingsSchema = new ConfigurationObjectSchema('settings', ConfigurationObjectType.Object, /* Will display as key/val pairs with comments attached. */ undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* GENERAL SETTINGS: ' + + '* These settings are not in the top menu options. ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + //////////////////////////////////////// + // ! GEORGIA-REBORN-CUSTOM DEFAULTS ! // + //////////////////////////////////////// + + // * CUSTOM LIBRARY DIRECTORY * // + // #region CUSTOM LIBRARY DIRECTORY + /** @public @type {array} Custom library cache directory. */ + this.customLibraryDirDefaults = [ + 'C:\\Replace_this_path_to_your_new_library\\Directory\\' + ]; + + /** @public @type {object} Custom library cache directory config header description. */ + this.customLibraryDirSchema = new ConfigurationObjectSchema('customLibraryDir', ConfigurationObjectType.Array, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM LIBRARY DIRECTORY: ' + + '* You can set your own custom library directory. ' + + '* Replace the path below and activate in top menu Options > Settings > Theme cache > Library > Use custom library directory. Restart foobar to take effect. ' + + '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM BIOGRAPHY DIRECTORY * // + // #region CUSTOM BIOGRAPHY DIRECTORY + /** @public @type {array} Custom biography cache directory. */ + this.customBiographyDirDefaults = [ + 'C:\\Replace_this_path_to_your_new_biography\\Directory\\' + ]; + + /** @public @type {object} Custom biography cache directory config header description. */ + this.customBiographyDirSchema = new ConfigurationObjectSchema('customBiographyDir', ConfigurationObjectType.Array, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM BIOGRAPHY DIRECTORY: ' + + '* You can set your own custom biography directory. ' + + '* Replace the path below and activate in top menu Options > Settings > Theme cache > Library > Use custom biography directory. Restart foobar to take effect. ' + + '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM LYRICS DIRECTORY * // + // #region CUSTOM LYRICS DIRECTORY + /** @public @type {array} Custom lyrics cache directory. */ + this.customLyricsDirDefaults = [ + 'C:\\Replace_this_path_to_your_new_lyrics\\Directory\\' + ]; + + /** @public @type {object} Custom lyrics cache directory config header description. */ + this.customLyricsDirSchema = new ConfigurationObjectSchema('customLyricsDir', ConfigurationObjectType.Array, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM LYRICS DIRECTORY: ' + + '* You can set your own custom lyric directory. ' + + '* Change in foobar\'s Preferences > ESLyric > Lyric options > Location to your custom lyric folder and replace the path below. ' + + '* Activate in top menu Options > Settings > Theme cache > Lyrics > Use custom lyrics directory. Restart foobar to take effect. ' + + '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM WAVEFORM BAR DIRECTORY * // + // #region CUSTOM WAVEFORM BAR DIRECTORY + /** @public @type {array} Custom waveform bar cache directory. */ + this.customWaveformBarDirDefaults = [ + 'C:\\Replace_this_path_to_your_new_waveform\\Directory\\' + ]; + + /** @public @type {object} Custom waveform bar cache directory config header description. */ + this.customWaveformBarDirSchema = new ConfigurationObjectSchema('customWaveformBarDir', ConfigurationObjectType.Array, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM WAVEFORM BAR DIRECTORY: ' + + '* You can set your own custom waveform bar directory. ' + + '* Replace the path below and activate in top menu Options > Settings > Theme cache > Waveform bar > Use custom waveform bar directory. Restart foobar to take effect. ' + + '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM THEME FONTS * // + // #region CUSTOM THEME FONTS + /** @public @type {object} Custom fonts with default Open Sans type. */ + this.customFontsDefaults = { + fontDefault: 'Open Sans', + fontTopMenu: 'Open Sans SemiBold', + fontLowerBarArtist: 'Open Sans SemiBold', + fontLowerBarTitle: 'Open Sans', + fontLowerBarDisc: 'Open Sans', + fontLowerBarTime: 'Open Sans SemiBold', + fontLowerBarLength: 'Open Sans', + fontLowerBarWave: 'Open Sans SemiBold', + fontNotification: 'Open Sans', + fontPopup: 'Open Sans', + fontTooltip: 'Open Sans', + fontGridArtist: 'Open Sans', + fontGridTitle: 'Open Sans', + fontGridTitleBold: 'Open Sans SemiBold', + fontGridAlbum: 'Open Sans', + fontGridKey: 'Open Sans SemiBold', + fontGridValue: 'Open Sans', + playlistTitleNormal: 'Open Sans', + playlistTitleSelected: 'Open Sans', + playlistTitlePlaying: 'Open Sans SemiBold', + playlistArtistNormal: 'Open Sans', + playlistArtistPlaying: 'Open Sans', + playlistArtistNormalCompact: 'Open Sans SemiBold', + playlistArtistPlayingCompact: 'Open Sans SemiBold', + playlistAlbum: 'Open Sans SemiBold', + playlistDate: 'Open Sans SemiBold', + playlistDateCompact: 'Open Sans', + playlistInfo: 'Open Sans', + playlistCover: 'Open Sans', + playlistPlaycount: 'Open Sans', + fontLibrary: 'Open Sans', + fontBiography: 'Open Sans', + fontLyrics: 'Open Sans' + }; + + /** @public @type {object} Custom fonts config name description. */ + this.customFontsComments = { + fontDefault: 'Default font: Segoe UI - panel font used as default and panel related elements in Playlist, Library, Biography', + fontTopMenu: 'Default font: Segoe UI Semibold - theme font used for top menu buttons', + fontLowerBarArtist: 'Default font: HelveticaNeueLT Pro 65 Md - theme artist font used in lower bar', + fontLowerBarTitle: 'Default font: HelveticaNeueLT Pro 45 Lt - theme title font used in lower bar', + fontLowerBarDisc: 'Default font: HelveticaNeueLT Pro 45 Lt - theme disc font used in lower bar', + fontLowerBarTime: 'Default font: HelveticaNeueLT Pro 65 Md - theme time font used in lower bar', + fontLowerBarLength: 'Default font: HelveticaNeueLT Pro 45 Lt - theme length font used in lower bar', + fontLowerBarWave: 'Default font: HelveticaNeueLT Pro 65 Md - theme waveform bar font used in lower bar', + fontNotification: 'Default font: HelveticaNeueLT Pro 65 Md - theme notification font used in jump search and style indicator', + fontPopup: 'Default font: Segoe UI - theme popup font used in custom theme menu and theme log overlay', + fontTooltip: 'Default font: HelveticaNeueLT Pro 65 Md - theme tooltip font used in styled tooltips', + fontGridArtist: 'Default font: HelveticaNeueLT Pro 65 Md - theme artist font used in metadata grid ( Details )', + fontGridTitle: 'Default font: HelveticaNeueLT Pro 45 Lt - theme title font used in metadata grid ( Details )', + fontGridTitleBold: 'Default font: HelveticaNeueLT Pro 65 Md - theme title bold font used in metadata grid ( Details )', + fontGridAlbum: 'Default font: HelveticaNeueLT Pro 65 Md - theme album font used in metadata grid ( Details )', + fontGridKey: 'Default font: HelveticaNeueLT Pro 55 Roman - theme key font used in metadata grid ( Details )', + fontGridValue: 'Default font: HelveticaNeueLT Pro 45 Lt - theme value font used in metadata grid ( Details )', + playlistTitleNormal: 'Default font: Segoe UI - playlist title font used in rows, normal state', + playlistTitleSelected: 'Default font: Segoe UI - playlist title font used in rows, selected state', + playlistTitlePlaying: 'Default font: Segoe UI - playlist title font used in rows, playing state', + playlistArtistNormal: 'Default font: Segoe UI Semibold - playlist artist font used in headers, normal state', + playlistArtistPlaying: 'Default font: Segoe UI Semibold - playlist artist font used in headers, playing state', + playlistArtistNormalCompact: 'Default font: Segoe UI Semibold - playlist artist font for compact mode used in headers, normal state', + playlistArtistPlayingCompact: 'Default font: Segoe UI Semibold - playlist artist font for compact mode used in headers, playing state', + playlistAlbum: 'Default font: Segoe UI Semibold - playlist album font used in headers', + playlistDate: 'Default font: Segoe UI Semibold - playlist date font used in headers', + playlistDateCompact: 'Default font: Segoe UI Semibold - playlist date font for compact mode used in headers', + playlistInfo: 'Default font: Segoe UI - playlist info font used in headers', + playlistCover: 'Default font: Segoe UI Semibold - playlist cover font used in thumbnails', + playlistPlaycount: 'Default font: Segoe UI - playlist playcount font used in rows', + fontLibrary: 'Default font: Segoe UI - library font', + fontBiography: 'Default font: Segoe UI - biography font', + fontLyrics: 'Default font: Segoe UI - lyrics font' + }; + + /** @public @type {object} Custom fonts config header description. */ + this.customFontsSchema = new ConfigurationObjectSchema('customFont', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME FONTS: ' + + '* Top menu Options > Settings > Theme fonts > Use custom theme fonts ' + + '* Here you can set your own custom fonts if top menu Options > Settings > Theme fonts > Use custom theme fonts was activated. ' + + '* If you change the Open Sans placeholder, it needs to be the exact name of the font name/family. ' + + '* Note: If you mix different fonts all together, there will be issues with Y-alignment because fonts have different line heights than others. ' + + '* For example, if you want to use more than one font in the lower bar, the fonts need to have the same line height. ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM STYLE PRESET * // + // #region CUSTOM STYLE PRESET + /** @public @type {object} Custom style preset with default values. */ + this.customStylePresetDefaults = { + theme: 'white', + nighttime: false, + bevel: false, + blend: false, + blend2: false, + gradient: false, + gradient2: false, + alternative: false, + alternative2: false, + blackAndWhite: false, + blackAndWhite2: false, + blackAndWhiteReborn: false, + blackReborn: false, + rebornWhite: false, + rebornBlack: false, + randomPastel: false, + randomDark: false, + randomAutoColor: 'off', + topMenuButtons: 'default', + transportButtons: 'default', + progressBarDesign: 'default', + progressBar: 'default', + progressBarFill: 'default', + volumeBarDesign: 'default', + volumeBar: 'default', + volumeBarFill: 'default', + themeBrightness: 'default' + }; + + /** @public @type {object} Custom style preset config name description. */ + this.customStylePresetComments = { + theme: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold"', + nighttime: 'Values: true, false - special style can only be used with reborn, random, custom theme', + bevel: 'Values: true, false - can be used in all themes', + blend: 'Values: true, false - can be used in all themes', + blend2: 'Values: true, false - can be used in all themes', + gradient: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', + gradient2: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', + alternative: 'Values: true, false - can be used in all themes but not with special styles', + alternative2: 'Values: true, false - can be used in all themes but not with special styles', + blackAndWhite: 'Values: true, false - special white style can only be used with white theme', + blackAndWhite2: 'Values: true, false - special white style can only be used with white theme', + blackAndWhiteReborn: 'Values: true, false - special white style can only be used with white theme', + blackReborn: 'Values: true, false - special black style can only be used with black theme', + rebornWhite: 'Values: true, false - special reborn style can only be used with reborn theme', + rebornBlack: 'Values: true, false - special reborn style can only be used with reborn theme', + randomPastel: 'Values: true, false - special random style can only be used with random theme', + randomDark: 'Values: true, false - special random style can only be used with random theme', + randomAutoColor: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track", - can only be used with random theme', + topMenuButtons: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal"', + transportButtons: 'Values: "default", "bevel", "inner", "emboss", "minimal"', + progressBarDesign: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin"', + progressBar: 'Values: "default", "bevel", "inner"', + progressBarFill: 'Values: "default", "bevel", "inner", "blend"', + volumeBarDesign: 'Values: "default", "rounded"', + volumeBar: 'Values: "default", "bevel", "inner"', + volumeBarFill: 'Values: "default", "bevel", "inner"', + themeBrightness: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50' + }; + + /** @public @type {object} Custom style preset config header description. */ + this.customStylePresetSchema = new ConfigurationObjectSchema('customStylePreset', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM STYLE PRESET: ' + + '* Top menu Options > Preset > User preset > User settings ' + + '* You can set your own custom style preset if top menu Options > Preset > User preset > User settings is selected. ' + + '* First you need to set only one theme in theme: and set the theme to true, all other themes need to be set to false. ' + + '* Second, only one style per column can be set, see top menu Options > Style. For example, only Blend or Blend 2 can be active at the same time. ' + + '* It is the same for Alternative or Alternative 2 or if this particular column has an additional special style, see comment in styles section. ' + + '* Best practice would be to first set each style in top menu Options > Style, note these styles and modify them in the config here. ' + + '* If something goes wrong, you can reset your theme settings in top menu Options > Settings > Theme configuration > Reset all ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM DISC ART PLACEHOLDER * // + // #region CUSTOM DISC ART PLACEHOLDER + /** @public @type {object} Custom disc art placeholder with default values. */ + this.customDiscArtStubDefaults = { + cdName01: 'Custom CD - Name 01', + cdStub01: 'cd-custom01.png', + cdName02: 'Custom CD - Name 02', + cdStub02: '', + cdName03: 'Custom CD - Name 03', + cdStub03: '', + cdName04: 'Custom CD - Name 04', + cdStub04: '', + cdName05: 'Custom CD - Name 05', + cdStub05: '', + vinylName01: 'Custom Vinyl - Name 01', + vinylStub01: 'vinyl-custom01.png', + vinylName02: 'Custom Vinyl - Name 02', + vinylStub02: '', + vinylName03: 'Custom Vinyl - Name 03', + vinylStub03: '', + vinylName04: 'Custom Vinyl - Name 04', + vinylStub04: '', + vinylName05: 'Custom Vinyl - Name 05', + vinylStub05: '' + }; + + /** @public @type {object} Custom disc art placeholder config name description. */ + this.customDiscArtStubComments = { + cdName01: 'Value: the name of the first CD placeholder, e.g. Custom CD - Name 01', + cdStub01: 'Value: the filename of the first CD placeholder, e.g. cd-custom01.png', + cdName02: 'Value: the name of the second CD placeholder, e.g. Custom CD - Name 02', + cdStub02: 'Value: the filename of the second CD placeholder, e.g. cd-custom02.png', + cdName03: 'Value: the name of the third CD placeholder, e.g. Custom CD - Name 03', + cdStub03: 'Value: the filename of the third CD placeholder, e.g. cd-custom03.png', + cdName04: 'Value: the name of the fourth CD placeholder, e.g. Custom CD - Name 04', + cdStub04: 'Value: the filename of the fourth CD placeholder, e.g. cd-custom04.png', + cdName05: 'Value: the name of the fifth CD placeholder, e.g. Custom CD - Name 05', + cdStub05: 'Value: the filename of the fifth CD placeholder, e.g. cd-custom05.png', + vinylName01: 'Value: the name of the first vinyl placeholder, e.g. Custom vinyl - Name 01', + vinylStub01: 'Value: the filename of the first vinyl placeholder, e.g. vinyl-custom01.png', + vinylName02: 'Value: the name of the second vinyl placeholder, e.g. Custom vinyl - Name 02', + vinylStub02: 'Value: the filename of the second vinyl placeholder, e.g. vinyl-custom02.png', + vinylName03: 'Value: the name of the third vinyl placeholder, e.g. Custom vinyl - Name 03', + vinylStub03: 'Value: the filename of the third vinyl placeholder, e.g. vinyl-custom03.png', + vinylName04: 'Value: the name of the fourth vinyl placeholder, e.g. Custom vinyl - Name 04', + vinylStub04: 'Value: the filename of the fourth vinyl placeholder, e.g. vinyl-custom04.png', + vinylName05: 'Value: the name of the fifth vinyl placeholder, e.g. Custom vinyl - Name 05', + vinylStub05: 'Value: the filename of the fifth vinyl placeholder, e.g. vinyl-custom05.png' + }; + + /** @public @type {object} Custom disc art placeholder config header description. */ + this.customDiscArtStubSchema = new ConfigurationObjectSchema('customDiscArtStub', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM DISC ART PLACEHOLDER: ' + + '* Top menu Options > Details > Disc art > Disc art placeholder ' + + '* You can add your own custom CD and Vinyl placeholders that will be dynamically appended to the disc art placeholder menu. ' + + '* It is recommended to use the predefined prefix naming convention for CD and Vinyl placeholders, such as "Custom CD - YourName" or "Custom Vinyl - YourName". ' + + '* The filenames must end with .png, for example, "cd-custom01.png" or "vinyl-custom01.png". ' + + '* If the placeholders cdStub02, cdStub03, cdStub04, cdStub05, vinylStub02, vinylStub03, vinylStub04, vinylStub05 etc, do not have any filename values, they will not be added to the menu. ' + + '* The custom disc art placeholders are located in the directory "georgia-reborn/images/custom/discart". ' + + '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + + // * CUSTOM THEME * // + // #region CUSTOM THEME + /** @public @type {object} Custom theme with default HEX color values. */ + this.customThemeDefaults = { + name: '', + + // * PRELOADER COLORS * // + // #region PRELOADER COLORS + grCol_preloaderBg: '372355', + grCol_preloaderLogo: 'violet-play-logo.png', + grCol_preloaderLowerBarTitle: 'ffffff', + grCol_preloaderProgressBar: '412d64', + grCol_preloaderProgressBarFill: 'ebc841', + grCol_preloaderProgressBarFrame: '372355', + grCol_preloaderUIHacksFrame: '372355', + // #endregion + + // * PLAYLIST COLORS * // + // #region PLAYLIST COLORS + + // * MAIN COLORS * // + pl_col_bg: '321946', + + // * PLAYLIST MANAGER COLORS * // + pl_col_plman_text_normal: 'd2d2d2', + pl_col_plman_text_hovered: 'ffffff', + pl_col_plman_text_pressed: '3cfaa0', + + // * HEADER COLORS * // + pl_col_header_nowplaying_bg: '372355', + pl_col_header_sideMarker: '3cfaa0', + pl_col_header_artist_normal: 'd2d2d2', + pl_col_header_artist_playing: '3cfaa0', + pl_col_header_album_normal: 'd2d2d2', + pl_col_header_album_playing: 'ffffff', + pl_col_header_info_normal: 'd2d2d2', + pl_col_header_info_playing: 'ffffff', + pl_col_header_date_normal: 'd2d2d2', + pl_col_header_date_playing: '3cfaa0', + pl_col_header_line_normal: '503782', + pl_col_header_line_playing: '6450b4', + + // * ROW COLORS * // + pl_col_row_nowplaying_bg: '372355', + pl_col_row_stripes_bg: '372355', + pl_col_row_selection_frame: '503782', + pl_col_row_sideMarker: '3cfaa0', + pl_col_row_title_normal: 'd2d2d2', + pl_col_row_title_playing: 'ffffff', + pl_col_row_title_selected: '3cfaa0', + pl_col_row_title_hovered: 'ffffff', + pl_col_row_rating_color: 'ebc841', + pl_col_row_disc_subheader_line: '503782', + pl_col_row_drag_line: '6450b4', + pl_col_row_drag_line_reached: '3cfaa0', + + // * SCROLLBAR COLORS * // + pl_col_sbar_btn_normal: 'd2d2d2', + pl_col_sbar_btn_hovered: 'ebc841', + pl_col_sbar_thumb_normal: '412d64', + pl_col_sbar_thumb_hovered: 'ebc841', + pl_col_sbar_thumb_drag: '3cfaa0', + // #endregion + + // * LIBRARY COLORS * // + // #region LIBRARY COLORS + + // * MAIN COLORS * // + lib_ui_col_bg: '321946', + lib_ui_col_rowStripes: '372355', + + // * ROW COLORS * // + lib_ui_col_nowPlayingBg: '372350', + lib_ui_col_sideMarker: '3cfaa0', + lib_ui_col_selectionFrame: '503782', + lib_ui_col_selectionFrame2: '503782', + lib_ui_col_hoverFrame: '503782', + + // * NODE COLORS * // + lib_ui_col_iconPlus: 'ebc841', + lib_ui_col_iconPlus_h: '3cfaa0', + lib_ui_col_iconPlus_sel: '3cfaa0', + lib_ui_col_iconPlusBg: '372355', + lib_ui_col_iconMinus_e: 'ebc841', + lib_ui_col_iconMinus_c: 'ebc841', + lib_ui_col_iconMinus_h: 'ebc841', + + // * TEXT COLORS * // + lib_ui_col_text: 'd2d2d2', + lib_ui_col_text_h: 'ffffff', + lib_ui_col_text_nowp: 'ffffff', + lib_ui_col_textSel: '3cfaa0', + lib_ui_col_txt: 'd2d2d2', + lib_ui_col_txt_h: 'ffffff', + lib_ui_col_txt_box: 'ffffff', + lib_ui_col_search: 'ffffff', + + // * BUTTON COLORS * // + lib_ui_col_searchBtn: 'ffffff', + lib_ui_col_crossBtn: 'ffffff', + lib_ui_col_filterBtn: 'ffffff', + lib_ui_col_settingsBtn: 'ffffff', + lib_ui_col_line: '503782', + lib_ui_col_s_line: '503782', + + // * SCROLLBAR COLORS * // + lib_ui_col_sbarBtns: 'd2d2d2', + lib_ui_col_sbarNormal: '504673', + lib_ui_col_sbarHovered: 'ebc841', + lib_ui_col_sbarDrag: '3cfaa0', + // #endregion + + // * BIOGRAPHY COLORS * // + // #region BIOGRAPHY COLORS + + // * MAIN COLORS * // + bio_ui_col_bg: '321946', + bio_ui_col_rowStripes: '372350', + + // * HEADER COLORS * // + bio_ui_col_headingText: 'ffffff', + bio_ui_col_bottomLine: '503782', + bio_ui_col_centerLine: '503782', + bio_ui_col_sectionLine: '503782', + + // * TEXT COLORS * // + bio_ui_col_accent: 'ebc841', + bio_ui_col_source: 'd2d2d2', + bio_ui_col_summary: 'd2d2d2', + bio_ui_col_text: 'd2d2d2', + + // * MISC COLORS * // + bio_ui_col_lyricsNormal: 'd2d2d2', + bio_ui_col_lyricsHighlight: 'ebc841', + bio_ui_col_noPhotoStubBg: '372355', + bio_ui_col_noPhotoStubText: '3cfaa0', + + // * SCROLLBAR COLORS * // + bio_ui_col_sbarBtns: 'd2d2d2', + bio_ui_col_sbarNormal: '504673', + bio_ui_col_sbarHovered: 'ebc841', + bio_ui_col_sbarDrag: '3cfaa0', + // #endregion + + // * MAIN COLORS * // + // #region MAIN COLORS + + // * MAIN COLORS * // + grCol_bg: '372355', + grCol_shadow: '000000', + grCol_discArtShadow: '000000', + grCol_noAlbumArtStub: '3cfaa0', + grCol_lowerBarArtist: '3cfaa0', + grCol_lowerBarTitle: 'ffffff', + grCol_lowerBarTime: 'ffffff', + grCol_lowerBarLength: 'ffffff', + grCol_lyricsNormal: 'ffffff', + grCol_lyricsHighlight: 'ebc841', + grCol_lyricsShadow: '000000', + + // * DETAILS * // + grCol_detailsBg: '321946', + grCol_detailsText: 'd2d2d2', + grCol_detailsRating: 'ebc841', + grCol_timelineAdded: '3cfaa0', + grCol_timelinePlayed: '32d287', + grCol_timelineUnplayed: '28aa6e', + grCol_timelineFrame: '321946', + + // * POPUP COLORS * // + grCol_popupBg: '372355', + grCol_popupText: 'd2d2d2', + + // * TOP MENU BUTTON COLORS * // + grCol_menuBgColor: '503782', + grCol_menuStyleBg: '372355', + grCol_menuRectStyleEmbossTop: '6450b4', + grCol_menuRectStyleEmbossBottom: '151515', + grCol_menuRectNormal: '6450b4', + grCol_menuRectHovered: '6450b4', + grCol_menuRectDown: '6450b4', + grCol_menuTextNormal: 'ffffff', + grCol_menuTextHovered: 'ebc841', + grCol_menuTextDown: '3cfaa0', + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol_transportEllipseBg: '503782', + grCol_transportEllipseNormal: '503c8c', + grCol_transportEllipseHovered: '6450b4', + grCol_transportEllipseDown: '6450b4', + grCol_transportStyleBg: '372355', + grCol_transportStyleTop: '503c8c', + grCol_transportStyleBottom: '151515', + grCol_transportIconNormal: '3cfaa0', + grCol_transportIconHovered: 'ebc841', + grCol_transportIconDown: '3cfaa0', + + // * PROGRESS BAR COLORS * // + grCol_progressBar: '412d64', + grCol_progressBarStreaming: 'cf0005', + grCol_progressBarFrame: '372355', + grCol_progressBarFill: 'ebc841', + + // * PEAKMETER BAR COLORS * // + grCol_peakmeterBarProg: '412d64', + grCol_peakmeterBarProgFill: 'ebc841', + grCol_peakmeterBarFillTop: '3cfaa0', + grCol_peakmeterBarFillMiddle: '32d287', + grCol_peakmeterBarFillBack: '28aa6e', + grCol_peakmeterBarVertProgFill: 'ebc841', + grCol_peakmeterBarVertFill: 'ebc841', + grCol_peakmeterBarVertFillPeaks: 'fff0af', + + // * WAVEFORM BAR COLORS * // + grCol_waveformBarFillFront: 'ebc841', + grCol_waveformBarFillBack: 'b99b32', + grCol_waveformBarFillPreFront: '7d55cd', + grCol_waveformBarFillPreBack: '5f419b', + grCol_waveformBarIndicator: '3cfaa0', + + // * VOLUME BAR COLORS * // + grCol_volumeBar: '412d64', + grCol_volumeBarFrame: '372355', + grCol_volumeBarFill: '3cfaa0', + + // * STYLE COLORS * // + grCol_styleBevel: '1e0f2d', + grCol_styleGradient: '1e1428', + grCol_styleGradient2: '5a003c', + grCol_styleProgressBar: '372355', + grCol_styleProgressBarLineTop: '321946', + grCol_styleProgressBarLineBottom: '503782', + grCol_styleProgressBarFill: '8c6914', + grCol_styleVolumeBar: '372355', + grCol_styleVolumeBarFill: '19826e' + // #endregion + }; + + /** @public @type {object} Custom theme config name description. */ + this.customThemeComments = { + name: 'Custom theme name will be displayed in the options theme menu', + + // * PRELOADER COLORS * // + // #region PRELOADER COLORS + grCol_preloaderBg: 'Preloader background color. If this value is empty, the default color will be used', + grCol_preloaderLogo: 'Preloader logo must end with .png, for example, logo.png. If this value is empty, the default logo will be used', + grCol_preloaderLowerBarTitle: 'Preloader lower bar title color. If this value is empty, the default color will be used', + grCol_preloaderProgressBar: 'Preloader progress bar color. If this value is empty, the default color will be used', + grCol_preloaderProgressBarFill: 'Preloader progress bar fill color. If this value is empty, the default color will be used', + grCol_preloaderProgressBarFrame: 'Preloader progress bar frame color. If this value is empty, the default color will be used', + grCol_preloaderUIHacksFrame: 'Preloader UIHacks frame color. Should have the same color as preloaderBg. If this value is empty, the default color will be used', + // #endregion + + // * PLAYLIST COLORS * // + // #region PLAYLIST COLORS + + // * MAIN COLORS * // + pl_col_bg: 'Playlist main background color', + + // * PLAYLIST MANAGER COLORS * // + pl_col_plman_text_normal: 'Playlist manager text color in normal state', + pl_col_plman_text_hovered: 'Playlist manager text color in hovered state', + pl_col_plman_text_pressed: 'Playlist manager text color in pressed state', + + // * HEADER COLORS * // + pl_col_header_nowplaying_bg: 'Playlist header nowplaying bg color', + pl_col_header_sideMarker: 'Playlist header side marker color', + pl_col_header_artist_normal: 'Playlist header artist text color in normal state', + pl_col_header_artist_playing: 'Playlist header artist text color in playing state', + pl_col_header_album_normal: 'Playlist header album text color in normal state', + pl_col_header_album_playing: 'Playlist header album text color in playing state', + pl_col_header_info_normal: 'Playlist header info text color in normal state', + pl_col_header_info_playing: 'Playlist header info text color in playing state', + pl_col_header_date_normal: 'Playlist header date text color in normal state', + pl_col_header_date_playing: 'Playlist header date text color in playing state', + pl_col_header_line_normal: 'Playlist header line color in normal state', + pl_col_header_line_playing: 'Playlist header line color in playing state', + + // * ROW COLORS * // + pl_col_row_nowplaying_bg: 'Playlist row nowplaying bg color', + pl_col_row_stripes_bg: 'Playlist row alternate bg color', + pl_col_row_selection_frame: 'Playlist row selection frame color', + pl_col_row_sideMarker: 'Playlist row side marker color', + pl_col_row_title_normal: 'Playlist row title text color in normal state', + pl_col_row_title_playing: 'Playlist row title text color in playing state', + pl_col_row_title_selected: 'Playlist row title text color in selected state', + pl_col_row_title_hovered: 'Playlist row title text color in hovered state', + pl_col_row_rating_color: 'Playlist row rating color', + pl_col_row_disc_subheader_line: 'Playlist row disc sub header line color', + pl_col_row_drag_line: 'Playlist track reorder drag line color', + pl_col_row_drag_line_reached: 'Playlist track reorder reached drag line color', + + // * SCROLLBAR COLORS * // + pl_col_sbar_btn_normal: 'Playlist scrollbar button color in normal state', + pl_col_sbar_btn_hovered: 'Playlist scrollbar button color in hovered state', + pl_col_sbar_thumb_normal: 'Playlist scrollbar thumb color in normal state', + pl_col_sbar_thumb_hovered: 'Playlist scrollbar thumb color in hovered state', + pl_col_sbar_thumb_drag: 'Playlist scrollbar thumb color in dragged state', + // #endregion + + // * LIBRARY COLORS * // + // #region LIBRARY COLORS + + // * MAIN COLORS * // + lib_ui_col_bg: 'Library main background color', + lib_ui_col_rowStripes: 'Library row stripes background color', + + // * ROW COLORS * // + lib_ui_col_nowPlayingBg: 'Library row now playing bg color', + lib_ui_col_sideMarker: 'Library row side marker color', + lib_ui_col_selectionFrame: 'Library row selection frame color', + lib_ui_col_selectionFrame2: 'Library row selection frame color 2', + lib_ui_col_hoverFrame: 'Library row hover frame color', + + // * NODE COLORS * // + lib_ui_col_iconPlus: 'Library tree plus node icon color', + lib_ui_col_iconPlus_h: 'Library tree plus node icon color in hovered state', + lib_ui_col_iconPlus_sel: 'Library tree plus node icon color in selected state', + lib_ui_col_iconPlusBg: 'Library tree plus node icon background color for traditional tree', + lib_ui_col_iconMinus_e: 'Library tree minus node icon color in expanded state for traditional tree', + lib_ui_col_iconMinus_c: 'Library tree minus node icon color in collapsed state for traditional tree', + lib_ui_col_iconMinus_h: 'Library tree minus node icon color in hovered state for traditional tree', + + // * TEXT COLORS * // + lib_ui_col_text: 'Library row text color', + lib_ui_col_text_h: 'Library row text color in hovered state', + lib_ui_col_text_nowp: 'Library row now playing text color', + lib_ui_col_textSel: 'Library row text color in selected state', + lib_ui_col_txt: 'Library user interface text color', + lib_ui_col_txt_h: 'Library user interface text color in hovered state', + lib_ui_col_txt_box: 'Library user interface text box color', + lib_ui_col_search: 'Library search text color', + + // * BUTTON COLORS * // + lib_ui_col_searchBtn: 'Library search button color', + lib_ui_col_crossBtn: 'Library cross button color', + lib_ui_col_filterBtn: 'Library filter button color', + lib_ui_col_settingsBtn: 'Library settings button color', + lib_ui_col_line: 'Library line color', + lib_ui_col_s_line: 'Library line color when selected', + + // * SCROLLBAR COLORS, ALSO LINKED WITH BIOGRAPHY SCROLLBAR COLORS * // + lib_ui_col_sbarBtns: 'Library scrollbar button color', + lib_ui_col_sbarNormal: 'Library scrollbar normal color', + lib_ui_col_sbarHovered: 'Library scrollbar hovered color', + lib_ui_col_sbarDrag: 'Library scrollbar drag color', + // #endregion + + // * BIOGRAPHY COLORS * // + // #region BIOGRAPHY COLORS + + // * MAIN COLORS * // + bio_ui_col_bg: 'Biography main background color', + bio_ui_col_rowStripes: 'Biography row stripes background color', + + // * HEADER COLORS * // + bio_ui_col_headingText: 'Biography header text color', + bio_ui_col_bottomLine: 'Biography header bottom line color', + bio_ui_col_centerLine: 'Biography header center line color', + bio_ui_col_sectionLine: 'Biography header section line color', + + // * TEXT COLORS * // + bio_ui_col_source: 'Biography source text color', + bio_ui_col_accent: 'Biography accent text color (used in item properties)', + bio_ui_col_summary: 'Biography summary text color', + bio_ui_col_text: 'Biography text color', + + // * MISC COLORS * // + bio_ui_col_lyricsNormal: 'Biography lyrics normal color', + bio_ui_col_lyricsHighlight: 'Biography lyrics highlight color', + bio_ui_col_noPhotoStubBg: 'Biography no photo stub background color', + bio_ui_col_noPhotoStubText: 'Biography no photo stub text color', + + // * SCROLLBAR COLORS * // + bio_ui_col_sbarBtns: 'Biography scrollbar button color', + bio_ui_col_sbarNormal: 'Biography scrollbar thumb normal color', + bio_ui_col_sbarHovered: 'Biography scrollbar thumb hovered color', + bio_ui_col_sbarDrag: 'Biography scrollbar thumb drag color', + // #endregion + + // * MAIN COLORS * // + // #region MAIN COLORS + + // * MAIN COLORS * // + grCol_bg: 'Main background color', + grCol_shadow: 'Panel shadow color', + grCol_discArtShadow: 'Disc art shadow color', + grCol_noAlbumArtStub: 'No album art stub color', + grCol_lowerBarArtist: 'Lower bar artist text color', + grCol_lowerBarTitle: 'Lower bar title text color', + grCol_lowerBarTime: 'Lower bar playback time text color', + grCol_lowerBarLength: 'Lower bar playback length text color', + grCol_lyricsNormal: 'Lyrics normal color', + grCol_lyricsHighlight: 'Lyrics highlight color', + grCol_lyricsShadow: 'Lyrics shadow color', + + // * DETAILS * // + grCol_detailsBg: 'Details background color', + grCol_detailsText: 'Details text color', + grCol_detailsRating: 'Details rating color', + grCol_timelineAdded: 'Details timeline added color', + grCol_timelinePlayed: 'Details timeline played color', + grCol_timelineUnplayed: 'Details timeline unplayed color', + grCol_timelineFrame: 'Details timeline frame color', + + // * POPUP COLORS * // + grCol_popupBg: 'Popup background color', + grCol_popupText: 'Popup text color', + + // * TOP MENU BUTTON COLORS * // + grCol_menuBgColor: 'Top menu background color', + grCol_menuStyleBg: 'Top menu style background color', + grCol_menuRectStyleEmbossTop: 'Top menu rectangle style emboss top color', + grCol_menuRectStyleEmbossBottom: 'Top menu rectangle style emboss bottom color', + grCol_menuRectNormal: 'Top menu rectangle color', + grCol_menuRectHovered: 'Top menu rectangle color when hovered', + grCol_menuRectDown: 'Top menu rectangle color when down', + grCol_menuTextNormal: 'Top menu text color', + grCol_menuTextHovered: 'Top menu text color when hovered', + grCol_menuTextDown: 'Top menu text color when down', + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol_transportEllipseBg: 'Lower bar transport ellipse background color', + grCol_transportEllipseNormal: 'Lower bar transport ellipse color', + grCol_transportEllipseHovered: 'Lower bar transport ellipse color when hovered', + grCol_transportEllipseDown: 'Lower bar transport ellipse color when down', + grCol_transportStyleBg: 'Lower bar transport style background color', + grCol_transportStyleTop: 'Lower bar transport style top color', + grCol_transportStyleBottom: 'Lower bar transport style bottom color', + grCol_transportIconNormal: 'Lower bar transport icon color', + grCol_transportIconHovered: 'Lower bar transport icon color when hovered', + grCol_transportIconDown: 'Lower bar transport icon color when down', + + // * PROGRESS BAR COLORS * // + grCol_progressBar: 'Progress bar color', + grCol_progressBarStreaming: 'Progress bar when streaming color', + grCol_progressBarFrame: 'Progress bar frame color', + grCol_progressBarFill: 'Progress bar fill color', + + // * PEAKMETER BAR COLORS * // + grCol_peakmeterBarProg: 'Peakmeter bar progress bg color', + grCol_peakmeterBarProgFill: 'Peakmeter bar progress fill color', + grCol_peakmeterBarFillTop: 'Peakmeter bar top fill color', + grCol_peakmeterBarFillMiddle: 'Peakmeter bar middle fill color', + grCol_peakmeterBarFillBack: 'Peakmeter bar back fill color', + grCol_peakmeterBarVertProgFill: 'Peakmeter bar vertical progress fill color', + grCol_peakmeterBarVertFill: 'Peakmeter bar vertical fill color', + grCol_peakmeterBarVertFillPeaks: 'Peakmeter bar vertical peaks fill color', + + // * WAVEFORM BAR COLORS * // + grCol_waveformBarFillFront: 'Waveform bar front fill color', + grCol_waveformBarFillBack: 'Waveform bar back fill color', + grCol_waveformBarFillPreFront: 'Waveform bar prepaint front fill color', + grCol_waveformBarFillPreBack: 'Waveform bar prepaint back fill color', + grCol_waveformBarIndicator: 'Waveform bar progress indicator color', + + // * VOLUME BAR COLORS * // + grCol_volumeBar: 'Volume bar color', + grCol_volumeBarFrame: 'Volume bar frame color', + grCol_volumeBarFill: 'Volume bar fill color', + + // * STYLE COLORS * // + grCol_styleBevel: 'Style bevel color', + grCol_styleGradient: 'Style gradient color', + grCol_styleGradient2: 'Style gradient 2 color', + grCol_styleProgressBar: 'Style progress bar color', + grCol_styleProgressBarLineTop: 'Style progress bar line top color', + grCol_styleProgressBarLineBottom: 'Style progress bar line bottom color', + grCol_styleProgressBarFill: 'Style progress bar fill color', + grCol_styleVolumeBar: 'Style volume bar color', + grCol_styleVolumeBarFill: 'Style volume bar fill color' + // #endregion + }; + + /** @public @type {object} Custom theme 01 config header description. */ + this.customTheme01Schema = new ConfigurationObjectSchema('customTheme01', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 01: ' + + '* Top menu Options > Theme > Custom > Theme 01 ' + + '* This is the first custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 02 config header description. */ + this.customTheme02Schema = new ConfigurationObjectSchema('customTheme02', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 02: ' + + '* Top menu Options > Theme > Custom > Theme 02 ' + + '* This is the second custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 03 config header description. */ + this.customTheme03Schema = new ConfigurationObjectSchema('customTheme03', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 03: ' + + '* Top menu Options > Theme > Custom > Theme 03 ' + + '* This is the third custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 04 config header description. */ + this.customTheme04Schema = new ConfigurationObjectSchema('customTheme04', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 04: ' + + '* Top menu Options > Theme > Custom > Theme 04 ' + + '* This is the fourth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 05 config header description. */ + this.customTheme05Schema = new ConfigurationObjectSchema('customTheme05', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 05: ' + + '* Top menu Options > Theme > Custom > Theme 05 ' + + '* This is the fifth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 06 config header description. */ + this.customTheme06Schema = new ConfigurationObjectSchema('customTheme06', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 06: ' + + '* Top menu Options > Theme > Custom > Theme 06 ' + + '* This is the sixth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 07 config header description. */ + this.customTheme07Schema = new ConfigurationObjectSchema('customTheme07', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 07: ' + + '* Top menu Options > Theme > Custom > Theme 07 ' + + '* This is the seventh custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 08 config header description. */ + this.customTheme08Schema = new ConfigurationObjectSchema('customTheme08', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 08: ' + + '* Top menu Options > Theme > Custom > Theme 08 ' + + '* This is the eighth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 09 config header description. */ + this.customTheme09Schema = new ConfigurationObjectSchema('customTheme09', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 09: ' + + '* Top menu Options > Theme > Custom > Theme 09 ' + + '* This is the ninth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + + /** @public @type {object} Custom theme 10 config header description. */ + this.customTheme10Schema = new ConfigurationObjectSchema('customTheme10', ConfigurationObjectType.Object, undefined, + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + + '* CUSTOM THEME 10: ' + + '* Top menu Options > Theme > Custom > Theme 10 ' + + '* This is the tenth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + + '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + + '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); + // #endregion + } +} diff --git a/profile/georgia-reborn/scripts/Base/gr-config.js b/profile/georgia-reborn/scripts/Base/gr-config.js new file mode 100644 index 00000000..7e5e91e8 --- /dev/null +++ b/profile/georgia-reborn/scripts/Base/gr-config.js @@ -0,0 +1,962 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Configuration * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +/////////////////////// +// * CONFIGURATION * // +/////////////////////// +/** + * A class that defines the structure and properties of an object in a config file. + */ +class ConfigurationObjectSchema { + /** + * Creates the `ConfigurationObjectSchema` instance. + * @param {string} name - The name for the object in the config file. If the object is `grid: {}`, then name should be `grid`. + * @param {string} container - The type of container for the object. Should be of ConfigurationObjectType. + * @param {Array} [fields] - The fields for each entry in the object. If undefined, uses key/value pairs for objects, or comma separated values for arrays. + * @param {string} [comment] - Adds a '//' field as first entry in the object. Used for explaining things to the user. + */ + constructor(name, container, fields = undefined, comment = undefined) { + /** @protected @type {string} */ + this.name = name; + /** @protected @type {string} */ + this.container = container; + /** @protected @type {Array} */ + this.fields = fields; + /** @protected @type {string} */ + this.comment = comment; + } +} + + +/** + * A class that reads and writes the theme config to a JSON file. + */ +class Configuration { + /** + * Represents the definition of a single field within a configuration object. + * @typedef {object} FieldDefinition + * @property {string} name - The identifier for the field. + * @property {boolean} optional - Indicates whether the field is optional. + */ + + /** + * Represents a configuration object that is used to store values and comments associated with the object schema. + * @typedef {object} ConfigurationObject + * @property {ConfigurationObjectSchema} definition - The schema definition of the configuration object. + * @property {Array} values - The actual stored values for the configuration object. + * @property {Array} comments - Optional comments associated with the configuration values. + */ + + /** + * Creates the `Configuration` instance. + * Instantiates a configuration object and specifies a file to read from. + * @param {string} configurationPath - The path to the config file. + */ + constructor(configurationPath) { + /** @protected @type {string} */ + this.path = configurationPath; + /** @protected @type {Array} */ + this._configuration = []; + + if (!configurationPath.includes('.jsonc')) { + console.log(''); + } + } + + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS + /** + * Checks if the config file exists. + * @returns {boolean} True or false. + */ + get fileExists() { + return IsFile(this.path); + } + // #endregion + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Gets a config by theme name. + * @param {string} name - The theme config name. + * @returns {ConfigurationThemeSettings} The config. + * @private + */ + _getConfigObject(name) { + const obj = this._configuration.find(c => c.definition.name === name); + return new ConfigurationThemeSettings(this, name, obj); + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Gets the path to the config file. + * @returns {string} The path to the config file. + */ + getPath() { + return this.path; + } + + /** + * Adds a config object to an array, replacing an existing object if it already exists. + * @param {ConfigurationObjectSchema} objectDefinition - The description of the object. + * @param {*} values - The values that will be written. + * @param {string[]} comments - The comment for the entry. + * @returns {ConfigurationThemeSettings} Provides getters and setters to automatically update the config file when config value changes. + */ + addConfigurationObject(objectDefinition, values, comments = []) { + /** @type {ConfigurationObject} */ + const obj = { definition: objectDefinition, values, comments }; + const idx = this._configuration.findIndex(c => c.definition.name === objectDefinition.name); + if (idx !== -1) { + // Replace existing object + this._configuration.splice(idx, 1, obj); + } else { + this._configuration.push(obj); + } + return this._getConfigObject(objectDefinition.name); + } + + /** + * Updates the values of the config for the given theme name. + * @param {string} objectName - The theme config name. + * @param {*} values - The values that will be written. + * @param {boolean} writeConfig - Whether to write the config to disk. + * @throws {InvalidTypeError} When values provided are not an array and it is expected. + */ + updateConfigObjValues(objectName, values, writeConfig = false) { + const configObj = this._configuration.find(c => c.definition.name === objectName); + if (Array.isArray(configObj.values)) { + if (Array.isArray(values)) { + configObj.values.splice(0, Infinity, ...values); + } else { + throw new InvalidTypeError('values', typeof values, 'array', 'Don\'t call updateConfigObjValues() to update Array values with non Array objects.'); + } + } else { + Object.assign(configObj.values, values); + } + if (writeConfig) { + this.writeConfiguration(); + } + } + + /** + * Reads the config from disk. + * @returns {object} An object containing. + * @throws {ThemeError} If the configuration file could not be read or if the JSON is invalid. + */ + readConfiguration() { + try { + const f = fso.GetFile(this.path); + const p = f.OpenAsTextStream(FileMode.Read, FileType.Unicode); + const jsonString = StripJsonComments(p.ReadAll()); + const config = JSON.parse(jsonString); + p.Close(); + return config; + } + catch (e) { + throw new ThemeError( + `Could not read config file:\n${this.path}\n\n` + + 'The JSON appears to be invalid. If you have edited the config file,\n' + + 'ensure that your modifications have the correct syntax and values:\n' + + `${e.message}\n\n` + + 'The error typically occurs on the line before the one indicated in the error message.\n\n' + + 'You can also delete or rename the config file, and on the next startup,\n' + + 'a new default one will be created. As a last resort, you can replace your\n' + + 'current config with a backup config file.' + ); + } + } + + /** + * Resets the config to the default values. + */ + resetConfiguration() { + fso.DeleteFile(this.path); + setTimeout(() => { + window.Reload(); + }, 1); + } + + /** + * Writes the config file to the path specified when Configuration was instantiated. + * + * Only needs to be called manually the very first time, or if not calling updateConfigObjValues. + * ( Only happens if not using a ConfigurationThemeSettings object received from addConfigurationObject ). + */ + writeConfiguration() { + const p = fso.CreateTextFile(this.path, true, true); + + p.WriteLine('/////////////////////////////////////////////////////////////////////////////////'); + p.WriteLine('// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * //'); + p.WriteLine('// * Description: Georgia-ReBORN Configuration File * //'); + p.WriteLine('// * Author: TT * //'); + p.WriteLine('// * Org. Author: Mordred * //'); + p.WriteLine('// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * //'); + p.WriteLine('// * * //'); + p.WriteLine('// * Manual changes to this file will take effect on the next reload. * //'); + p.WriteLine('// * To ensure changes are not overwritten or lost, reload the theme * //'); + p.WriteLine('// * immediately after manually changing values. * //'); + p.WriteLine('/////////////////////////////////////////////////////////////////////////////////'); + p.WriteLine(''); + p.WriteLine(''); + p.WriteLine('{'); + p.WriteLine('/////////////////////////////////////////////////////////////////////////////////'); + p.WriteLine(`\t"configVersion": "${grCfg.currentVersion}",`); + p.WriteLine('/////////////////////////////////////////////////////////////////////////////////'); + p.WriteLine(''); + + for (const conf of this._configuration) { + p.WriteLine(''); + const container = conf.definition.container === ConfigurationObjectType.Array ? '[' : '{'; + p.WriteLine(`\t"${conf.definition.name}": ${container}`); + + if (conf.definition.comment) { + let line = conf.definition.comment; + let done = false; + while (!done) { + const lineLen = 200; + if (line.length < lineLen) { + p.WriteLine(`\t\t// ${line.trim()}`); + done = true; + } else { + const idx = line.lastIndexOf(' ', lineLen); + p.WriteLine(`\t\t// ${line.substr(0, idx).trim()}`); + line = line.substr(idx); + } + } + } + + if (conf.definition.fields) { + // Array of fields + for (const value of conf.values) { + let entry = ''; + if (conf.definition.container === ConfigurationObjectType.Array) { + for (const field of conf.definition.fields) { + if (!field.optional || value[field.name]) { + const quotes = typeof value[field.name] === 'string' ? '"' : ''; + entry += `, "${field.name}": ${quotes}${value[field.name]}${quotes}`; + } + } + entry = `{${entry.substr(1)} }`; + } + const comment = value.comment ? ` // ${value.comment}` : ''; + p.WriteLine(`\t\t${entry}${value !== conf.values[conf.values.length - 1] ? ',' : ''}${comment}`); + } + } + else if (conf.definition.container === ConfigurationObjectType.Array) { + // Array of comma separated entries + for (const val of conf.values) { + p.WriteLine(`\t\t"${val.replace(/\\/g, '\\\\')}"${val !== conf.values[conf.values.length - 1] ? ',' : ''}`); + } + } + else { + // Object with key/value pairs + const keys = Object.keys(conf.values); + for (const key of keys) { + const comment = conf.comments[key] ? ` // ${conf.comments[key]}` : ''; + const quotes = typeof conf.values[key] === 'string' ? '"' : ''; + p.WriteLine(`\t\t"${key}": ${quotes}${conf.values[key]}${quotes}${key !== keys[keys.length - 1] ? ',' : ''}${comment}`); + } + } + const closeContainer = conf.definition.container === ConfigurationObjectType.Array ? ']' : '}'; + p.WriteLine(`\t${closeContainer}${conf !== this._configuration[this._configuration.length - 1] ? ',' : ''}`); + } + + p.WriteLine('}'); + p.Close(); + } + // #endregion +} + + +/** + * A class that manages configuration settings by reading and writing configuration files, as well as performing update checks. + */ +class ConfigurationManager { + /** + * Creates the `ConfigurationManager` instance. + */ + constructor() { + // * BASE CONFIG SETUP * // + // #region BASE CONFIG SETUP + /** @public @type {string} The Georgia-ReBORN config file path. */ + this.configPath = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc`; + /** @public @type {string} The Georgia-ReBORN custom config file path. */ + this.configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; + /** @public @type {Configuration} The instance of the `Configuration` config object. */ + this.config = new Configuration(this.configPath); + /** @public @type {Configuration} The instance of the `Configuration` custom config object. */ + this.configCustom = new Configuration(this.configPathCustom); + /** @public @type {string} The Georgia-ReBORN current version. */ + this.currentVersion = '3.0-DEV'; + /** @public @type {string} The Georgia-ReBORN version will be overwritten when loaded from config file. */ + this.configVersion = this.currentVersion; + /** @public @type {string} The Georgia-ReBORN version will be shown on the right side of the lower bar when nothing is playing. */ + this.lowerBarStoppedTime = `Georgia-ReBORN v${this.currentVersion}`; + /** @public @type {boolean} The update state if a new update is available on Github releases page. */ + this.updateAvailable = false; + /** @public @type {boolean} The update link will be shown on the right side of the lower bar. */ + this.updateHyperlink = false; + /** @private @type {number} The update retry, don't hammer the server if it's not working, used only in checkForUpdates(). */ + this.updateRetryCount = 0; + /** @private @type {number} The update timeout timer used only in scheduleUpdateCheck(). */ + this.updateTimer = 0; + // #endregion + + // * GEORGIA-REBORN-CONFIG.JSONC SETUP * // + // #region GEORGIA-REBORN-CONFIG.JSONC SETUP + /** @public @type {object} The config global theme settings in the main config. */ + this.settings = {}; + /** @public @type {object} The config `Theme` settings in the main config. */ + this.theme = {}; + /** @public @type {object} The config `Style` settings in the main config. */ + this.style = {}; + /** @public @type {object} The config `Preset` settings in the main config. */ + this.preset = {}; + /** @public @type {object} The config `Player size` settings in the main config. */ + this.themePlayerSize = {}; + /** @public @type {object} The config `Layout` settings in the main config. */ + this.themeLayout = {}; + /** @public @type {object} The config `Display` settings in the main config. */ + this.themeDisplay = {}; + /** @public @type {object} The config `Brightness` settings in the main config. */ + this.themeBrightness = {}; + /** @public @type {object} The config `Font size` settings in the main config. */ + this.themeFontSize = {}; + /** @public @type {object} The config `Player controls` settings in the main config. */ + this.themeControls = {}; + /** @public @type {object} The config `Playlist` settings in the main config. */ + this.themePlaylist = {}; + /** @public @type {object} The config `Details` settings in the main config. */ + this.themeDetails = {}; + /** @public @type {object} The config `Library` settings in the main config. */ + this.themeLibrary = {}; + /** @public @type {object} The config `Biography` settings in the main config. */ + this.themeBiography = {}; + /** @public @type {object} The config `Lyrics` settings in the main config. */ + this.themeLyrics = {}; + /** @public @type {object} The config `Settings` settings in the main config. */ + this.themeSettings = {}; + /** @public @type {MetadataGridEntry[]} The metadata grid config entries in the main config. */ + this.metadataGrid = []; + // #endregion + + // * GEORGIA-REBORN-CUSTOM.JSONC SETUP * // + // #region GEORGIA-REBORN-CUSTOM.JSONC SETUP + /** @public @type {object} The custom theme font in the custom config. */ + this.customFont = {}; + /** @public @type {object} The custom style preset in the custom config. */ + this.customStylePreset = {}; + /** @public @type {object} The custom disc art stubs in the custom config. */ + this.customDiscArtStub = {}; + /** @public @type {object} The custom theme object containing custom theme colors. */ + this.cTheme = {}; + /** @public @type {object} The first custom theme in the custom config. */ + this.customTheme01 = {}; + /** @public @type {object} The second custom theme in the custom config. */ + this.customTheme02 = {}; + /** @public @type {object} The third custom theme in the custom config. */ + this.customTheme03 = {}; + /** @public @type {object} The fourth custom theme in the custom config. */ + this.customTheme04 = {}; + /** @public @type {object} The fifth custom theme in the custom config. */ + this.customTheme05 = {}; + /** @public @type {object} The sixth custom theme in the custom config. */ + this.customTheme06 = {}; + /** @public @type {object} The seventh custom theme in the custom config. */ + this.customTheme07 = {}; + /** @public @type {object} The eight custom theme in the custom config. */ + this.customTheme08 = {}; + /** @public @type {object} The ninth custom theme in the custom config. */ + this.customTheme09 = {}; + /** @public @type {object} The tenth custom theme in the custom config. */ + this.customTheme10 = {}; + // #endregion + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Checks if settings exist in the configuration file. + * @param {object} settings - The settings object from the configuration file. + * @param {...string} settingNames - The names of the settings to check. + * @returns {boolean} Returns true if all specified settings exist, otherwise false. + * @private + */ + _checkSettings(settings, ...settingNames) { + return settingNames.some(settingName => Object.prototype.hasOwnProperty.call(settings, settingName)); + } + + /** + * Checks if settings with specified prefixes exist in the configuration file. + * @param {object} settings - The settings object from the configuration file. + * @param {...string} prefixes - The prefixes of the settings to check. + * @returns {boolean} Returns true if any keys start with the specified prefixes, otherwise false. + * @private + */ + _checkSettingsByPrefix(settings, ...prefixes) { + return prefixes.some(prefix => + Object.keys(settings).some(key => key.startsWith(prefix)) + ); + } + + /** + * Deletes settings from the configuration file. + * @param {object} settings - The settings object from the configuration file. + * @param {...string} settingNames - The names of the settings to remove. + * @private + */ + _deleteSettings(settings, ...settingNames) { + for (const settingName of settingNames) { + delete settings[settingName]; + } + } + + /** + * Renames settings based on a prefix in the configuration file. + * @param {object} settings - The settings object from the configuration file. + * @param {string[]} oldPrefixes - The old prefixes to be replaced. + * @param {string[]} newPrefixes - The new prefixes that will replace the old ones. + * @private + */ + _renameSettings(settings, oldPrefixes, newPrefixes) { + if (oldPrefixes.length !== newPrefixes.length) { + console.log('oldPrefixes and newPrefixes arrays must be of the same length'); + return; + } + + for (const key of Object.keys(settings)) { + for (let index = 0; index < oldPrefixes.length; index++) { + const oldPrefix = oldPrefixes[index]; + if (key.startsWith(oldPrefix)) { + const newKey = key.replace(oldPrefix, newPrefixes[index]); + if (settings[key] !== undefined) { // Make sure the old key has a value + settings[newKey] = settings[key]; + console.log(`Renaming ${key} to ${newKey}`); + } else { + console.log(`Skipping rename for undefined ${key}`); + } + delete settings[key]; + break; + } + } + } + } + + /** + * Checks if specific entry exist in the metadata grid configuration. + * @param {MetadataGridEntry[]} grid - Each element in the array is an object with a `label` property. + * @param {...string} labels - The labels of the settings to check. + * @returns {boolean} Returns true if all specified labels exist, otherwise false. + * @private + */ + _gridCheckEntry(grid, ...labels) { + return labels.every(label => grid.some(gridEntry => gridEntry.label.toLowerCase() === label.toLowerCase())); + } + + /** + * Renames an entry in the metadata grid with a new label name. + * @param {MetadataGridEntry[]} grid - Each element in the array is an object with a `label` property. + * @param {string} oldLabel - The old label name to rename. + * @param {string} newLabel - The new label name that will be replaced in the config file. + * @private + */ + _gridRenameEntry(grid, oldLabel, newLabel) { + const entryIdx = grid.findIndex(gridEntry => gridEntry && gridEntry.label.toLowerCase() === oldLabel.toLowerCase()); + if (entryIdx >= 0) { + grid[entryIdx].label = newLabel; + } + } + + /** + * Adds or Replaces values in the metadata grid with updated string from defaults. + * @param {MetadataGridEntry[]} grid - Each element in the array is an object with a `label` property. + * @param {string} label - The label of the value to add or replace. + * @param {number} position - 0-based index of place to insert new value if existing entry not found. + * @private + */ + _gridReplaceEntry(grid, label, position) { + const entryIdx = grid.findIndex(gridEntry => gridEntry && gridEntry.label.toLowerCase() === label.toLowerCase()); + const newVal = grDef.metadataGridDefaults[grDef.metadataGridDefaults.findIndex(e => e && e.label.toLowerCase() === label.toLowerCase())]; + if (entryIdx >= 0) { + grid[entryIdx] = newVal; + } else { + grid.splice(position, 0, newVal); + } + } + // #endregion + + // * PUBLIC METHODS - CONFIG FILE * // + // #region PUBLIC METHODS - CONFIG FILE + /** + * Initializes the configuration by reading from the default and custom configuration files. + * If the configuration files do not exist, it creates them with default values. + */ + initializeConfigs() { + if (this.config.fileExists) { + this.readDefaultConfig(); + } else { + this.writeDefaultConfig(); + } + if (this.configCustom.fileExists) { + this.readCustomConfig(); + } else { + this.writeCustomConfig(); + } + } + + /** + * Reads the default configuration from the configuration file and sets up the configuration objects. + */ + readDefaultConfig() { + const prefs = this.config.readConfiguration(); + /** + * While we've read all the values in, we still need to call addConfigurationObject to add the getters/setters + * for the objects so that the file gets automatically written when a setting is changed. + */ + this.config.addConfigurationObject(grDef.titleFormatSchema, Object.assign({}, grDef.titleFormatDefaults, prefs.title_format_strings), grDef.titleFormatComments); + this.config.addConfigurationObject(grDef.imgPathSchema, prefs.imgPaths); + + this.theme = this.config.addConfigurationObject(grDef.themeSchema, Object.assign({}, grDef.themeDefaults, prefs.theme), grDef.themeComments); + this.style = this.config.addConfigurationObject(grDef.themeStyleSchema, Object.assign({}, grDef.themeStyleDefaults, prefs.style), grDef.themeStyleComments); + this.preset = this.config.addConfigurationObject(grDef.themePresetSchema, Object.assign({}, grDef.themePresetDefaults, prefs.preset), grDef.themePresetComments); + this.themePlayerSize = this.config.addConfigurationObject(grDef.themePlayerSizeSchema, Object.assign({}, grDef.themePlayerSizeDefaults, prefs.themePlayerSize), grDef.themePlayerSizeComments); + this.themeLayout = this.config.addConfigurationObject(grDef.themeLayoutSchema, Object.assign({}, grDef.themeLayoutDefaults, prefs.themeLayout), grDef.themeLayoutComments); + this.themeDisplay = this.config.addConfigurationObject(grDef.themeDisplaySchema, Object.assign({}, grDef.themeDisplayDefaults, prefs.themeDisplay), grDef.themeDisplayComments); + this.themeBrightness = this.config.addConfigurationObject(grDef.themeBrightnessSchema, Object.assign({}, grDef.themeBrightnessDefaults, prefs.themeBrightness), grDef.themeBrightnessComments); + this.themeFontSize = this.config.addConfigurationObject(grDef.themeFontSizesSchema, Object.assign({}, grDef.themeFontSizesDefaults, prefs.themeFontSize), grDef.themeFontSizesComments); + this.themeControls = this.config.addConfigurationObject(grDef.themePlayerControlsSchema, Object.assign({}, grDef.themePlayerControlsDefaults, prefs.themeControls), grDef.themePlayerControlsComments); + + this.themePlaylist = this.config.addConfigurationObject(grDef.themePlaylistSchema, Object.assign({}, grDef.themePlaylistDefaults, prefs.themePlaylist), grDef.themePlaylistComments); + this.config.addConfigurationObject(grDef.themePlaylistGroupingPresetsSchema, prefs.themePlaylistGroupingPresets || grDef.themePlaylistGroupingPresets); + + this.themeDetails = this.config.addConfigurationObject(grDef.themeDetailsSchema, Object.assign({}, grDef.themeDetailsDefaults, prefs.themeDetails), grDef.themeDetailsComments); + if (prefs.metadataGrid) { + for (const entry of prefs.metadataGrid) { + // Copy comments over to existing object so they aren't lost + const gridEntryDefinition = grDef.metadataGridDefaults.find(gridDefItem => gridDefItem.label === entry.label); + if (gridEntryDefinition && gridEntryDefinition.comment) { + entry.comment = gridEntryDefinition.comment; + } + } + } + this.config.addConfigurationObject(grDef.metadataGridSchema, prefs.metadataGrid || grDef.metadataGridDefaults); // Can't Object.assign here to add new fields. Add new fields in the upgrade section of migrateCheck + + this.themeLibrary = this.config.addConfigurationObject(grDef.themeLibrarySchema, Object.assign({}, grDef.themeLibraryDefaults, prefs.themeLibrary), grDef.themeLibraryComments); + this.themeBiography = this.config.addConfigurationObject(grDef.themeBiographySchema, Object.assign({}, grDef.themeBiographyDefaults, prefs.themeBiography), grDef.themeBiographyComments); + this.themeLyrics = this.config.addConfigurationObject(grDef.themeLyricsSchema, Object.assign({}, grDef.themeLyricsDefaults, prefs.themeLyrics), grDef.themeLyricsComments); + this.config.addConfigurationObject(grDef.lyricsFilenameSchema, prefs.lyricsFilenamePatterns || grDef.lyricsFilenameDefaults); + this.themeSettings = this.config.addConfigurationObject(grDef.themeSettingsSchema, Object.assign({}, grDef.themeSettingsDefaults, prefs.themeSettings), grDef.themeSettingsComments); + this.settings = this.config.addConfigurationObject(grDef.settingsSchema, Object.assign({}, grDef.settingsDefaults, prefs.settings), grDef.settingsComments); + + // Safety checks. Fix up potentially bad vals from config + this.settings.discArtBasename = this.settings.discArtBasename && this.settings.discArtBasename.trim().length ? this.settings.discArtBasename.trim() : 'cd'; + this.settings.artworkDisplayTime = Math.min(Math.max(this.settings.artworkDisplayTime, 5), 120); // Ensure min of 5sec and max of 120sec + + this.imgPaths = prefs.imgPaths; + this.lyricsFilenamePatterns = prefs.lyricsFilenamePatterns; + this.metadataGrid = prefs.metadataGrid; + this.configVersion = prefs.configVersion || prefs.version; + // When adding new objects to the config file, add them in the version check below + + // Safe guard when playlist grouping presets or metadata grid do not exist in the config + if (!prefs.themePlaylistGroupingPresets || !prefs.metadataGrid) { + const fileName = 'georgia-reborn\\configs\\georgia-reborn-config-backup.jsonc'; + fso.CopyFile(this.configPath, fb.ProfilePath + fileName); + this.config.writeConfiguration(); + } + } + + /** + * Reads the custom configuration from the custom configuration file and sets up the custom configuration objects. + */ + readCustomConfig() { + const prefs = this.configCustom.readConfiguration(); + + this.configCustom.addConfigurationObject(grDef.customLibraryDirSchema, prefs.customLibraryDir || grDef.customLibraryDirDefaults); + this.configCustom.addConfigurationObject(grDef.customBiographyDirSchema, prefs.customBiographyDir || grDef.customBiographyDirDefaults); + this.configCustom.addConfigurationObject(grDef.customLyricsDirSchema, prefs.customLyricsDir || grDef.customLyricsDirDefaults); + this.configCustom.addConfigurationObject(grDef.customWaveformBarDirSchema, prefs.customWaveformBarDir || grDef.customWaveformBarDirDefaults); + + this.customFont = this.configCustom.addConfigurationObject(grDef.customFontsSchema, Object.assign({}, grDef.customFontsDefaults, prefs.customFont), grDef.customFontsComments); + this.customStylePreset = this.configCustom.addConfigurationObject(grDef.customStylePresetSchema, Object.assign({}, grDef.customStylePresetDefaults, prefs.customStylePreset), grDef.customStylePresetComments); + this.customDiscArtStub = this.configCustom.addConfigurationObject(grDef.customDiscArtStubSchema, Object.assign({}, grDef.customDiscArtStubDefaults, prefs.customDiscArtStub), grDef.customDiscArtStubComments); + this.customTheme01 = this.configCustom.addConfigurationObject(grDef.customTheme01Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme01), grDef.customThemeComments); + this.customTheme02 = this.configCustom.addConfigurationObject(grDef.customTheme02Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme02), grDef.customThemeComments); + this.customTheme03 = this.configCustom.addConfigurationObject(grDef.customTheme03Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme03), grDef.customThemeComments); + this.customTheme04 = this.configCustom.addConfigurationObject(grDef.customTheme04Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme04), grDef.customThemeComments); + this.customTheme05 = this.configCustom.addConfigurationObject(grDef.customTheme05Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme05), grDef.customThemeComments); + this.customTheme06 = this.configCustom.addConfigurationObject(grDef.customTheme06Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme06), grDef.customThemeComments); + this.customTheme07 = this.configCustom.addConfigurationObject(grDef.customTheme07Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme07), grDef.customThemeComments); + this.customTheme08 = this.configCustom.addConfigurationObject(grDef.customTheme08Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme08), grDef.customThemeComments); + this.customTheme09 = this.configCustom.addConfigurationObject(grDef.customTheme09Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme09), grDef.customThemeComments); + this.customTheme10 = this.configCustom.addConfigurationObject(grDef.customTheme10Schema, Object.assign({}, grDef.customThemeDefaults, prefs.customTheme10), grDef.customThemeComments); + + this.customLibraryDir = prefs.customLibraryDir; + this.customBiographyDir = prefs.customBiographyDir; + this.customLyricsDir = prefs.customLyricsDir; + this.customWaveformBarDir = prefs.customWaveformBarDir; + this.customFont = prefs.customFont; + this.customStylePreset = prefs.customStylePreset; + this.customDiscArtStub = prefs.customDiscArtStub; + this.configVersion = prefs.configVersion || prefs.version; + } + + /** + * Writes the default configuration to the configuration file with default values for all settings. + */ + writeDefaultConfig() { + this.config.addConfigurationObject(grDef.titleFormatSchema, grDef.titleFormatDefaults, grDef.titleFormatComments); + this.config.addConfigurationObject(grDef.imgPathSchema, grDef.imgPathDefaults); + + this.theme = this.config.addConfigurationObject(grDef.themeSchema, grDef.themeDefaults, grDef.themeComments); + this.style = this.config.addConfigurationObject(grDef.themeStyleSchema, grDef.themeStyleDefaults, grDef.themeStyleComments); + this.preset = this.config.addConfigurationObject(grDef.themePresetSchema, grDef.themePresetDefaults, grDef.themePresetComments); + this.themePlayerSize = this.config.addConfigurationObject(grDef.themePlayerSizeSchema, grDef.themePlayerSizeDefaults, grDef.themePlayerSizeComments); + this.themeLayout = this.config.addConfigurationObject(grDef.themeLayoutSchema, grDef.themeLayoutDefaults, grDef.themeLayoutComments); + this.themeDisplay = this.config.addConfigurationObject(grDef.themeDisplaySchema, grDef.themeDisplayDefaults, grDef.themeDisplayComments); + this.themeBrightness = this.config.addConfigurationObject(grDef.themeBrightnessSchema, grDef.themeBrightnessDefaults, grDef.themeBrightnessComments); + this.themeFontSize = this.config.addConfigurationObject(grDef.themeFontSizesSchema, grDef.themeFontSizesDefaults, grDef.themeFontSizesComments); + this.themeControls = this.config.addConfigurationObject(grDef.themePlayerControlsSchema, grDef.themePlayerControlsDefaults, grDef.themePlayerControlsComments); + + this.themePlaylist = this.config.addConfigurationObject(grDef.themePlaylistSchema, grDef.themePlaylistDefaults, grDef.themePlaylistComments); + this.config.addConfigurationObject(grDef.themePlaylistGroupingPresetsSchema, grDef.themePlaylistGroupingPresets); + + this.themeDetails = this.config.addConfigurationObject(grDef.themeDetailsSchema, grDef.themeDetailsDefaults, grDef.themeDetailsComments); + this.config.addConfigurationObject(grDef.metadataGridSchema, grDef.metadataGridDefaults); // We don't assign an object here because these aren't key/value pairs and thus can't use the get/setters + + this.themeLibrary = this.config.addConfigurationObject(grDef.themeLibrarySchema, grDef.themeLibraryDefaults, grDef.themeLibraryComments); + this.themeBiography = this.config.addConfigurationObject(grDef.themeBiographySchema, grDef.themeBiographyDefaults, grDef.themeBiographyComments); + this.themeLyrics = this.config.addConfigurationObject(grDef.themeLyricsSchema, grDef.themeLyricsDefaults, grDef.themeLyricsComments); + this.config.addConfigurationObject(grDef.lyricsFilenameSchema, grDef.lyricsFilenameDefaults); + this.themeSettings = this.config.addConfigurationObject(grDef.themeSettingsSchema, grDef.themeSettingsDefaults, grDef.themeSettingsComments); + this.settings = this.config.addConfigurationObject(grDef.settingsSchema, grDef.settingsDefaults, grDef.settingsComments); + + console.log('> Writing', this.configPath); + this.config.writeConfiguration(); + window.Reload(); + } + + /** + * Writes the custom configuration to the custom configuration file with default values for all custom settings. + */ + writeCustomConfig() { + this.configCustom.addConfigurationObject(grDef.customLibraryDirSchema, grDef.customLibraryDirDefaults); + this.configCustom.addConfigurationObject(grDef.customBiographyDirSchema, grDef.customBiographyDirDefaults); + this.configCustom.addConfigurationObject(grDef.customLyricsDirSchema, grDef.customLyricsDirDefaults); + this.configCustom.addConfigurationObject(grDef.customWaveformBarDirSchema, grDef.customWaveformBarDirDefaults); + + this.customFont = this.configCustom.addConfigurationObject(grDef.customFontsSchema, grDef.customFontsDefaults, grDef.customFontsComments); + this.customStylePreset = this.configCustom.addConfigurationObject(grDef.customStylePresetSchema, grDef.customStylePresetDefaults, grDef.customStylePresetComments); + this.customDiscArtStub = this.configCustom.addConfigurationObject(grDef.customDiscArtStubSchema, grDef.customDiscArtStubDefaults, grDef.customDiscArtStubComments); + this.customTheme01 = this.configCustom.addConfigurationObject(grDef.customTheme01Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme02 = this.configCustom.addConfigurationObject(grDef.customTheme02Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme03 = this.configCustom.addConfigurationObject(grDef.customTheme03Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme04 = this.configCustom.addConfigurationObject(grDef.customTheme04Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme05 = this.configCustom.addConfigurationObject(grDef.customTheme05Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme06 = this.configCustom.addConfigurationObject(grDef.customTheme06Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme07 = this.configCustom.addConfigurationObject(grDef.customTheme07Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme08 = this.configCustom.addConfigurationObject(grDef.customTheme08Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme09 = this.configCustom.addConfigurationObject(grDef.customTheme09Schema, grDef.customThemeDefaults, grDef.customThemeComments); + this.customTheme10 = this.configCustom.addConfigurationObject(grDef.customTheme10Schema, grDef.customThemeDefaults, grDef.customThemeComments); + + console.log('> Writing', this.configPathCustom); + this.configCustom.writeConfiguration(); + window.Reload(); + } + // #endregion + + // * PUBLIC METHODS - UPDATE CHECK * // + // #region PUBLIC METHODS - UPDATE CHECK + /** + * Checks if there is a new update available, also called in Options > Help > Theme > Updates > Check for latest theme update. + * @param {boolean} openUrl - Opens the Georgia-ReBORN Github releases page when hyperlink is available. + */ + checkForUpdates(openUrl) { + MakeHttpRequest('GET', 'https://api.github.com/repos/TT-ReBORN/Georgia-ReBORN/tags', (resp) => { + try { + /** @type {boolean} The current Github master version, used to prevent notifying users for new update when in development state. */ + const developVersion = this.currentVersion.endsWith('DEV'); + const respObj = JSON.parse(resp); + this.updateAvailable = developVersion ? false : IsNewerVersion(this.currentVersion, respObj[0].name); + console.log(`Current released version of Georgia-ReBORN: v${respObj[0].name}`); + if (this.updateAvailable) { + console.log('>>> Georgia-ReBORN new update available. Download it from here: https://github.com/TT-ReBORN/Georgia-ReBORN/releases'); + this.updateHyperlink = new Hyperlink('New Update Available', grFont.lowerBarTitle, 'update', 0, 0, window.Width); + if (this.updateHyperlink) { + this.lowerBarStoppedTime = ''; + if (!fb.IsPlaying) { + grStr.time = this.lowerBarStoppedTime; + RepaintWindow(); + } + if (openUrl) { + this.updateHyperlink.click(); + } + } + } else { + console.log('You are on the most current version of Georgia-ReBORN'); + } + } catch (e) { + if (!this.updateHyperlink && this.updateRetryCount < 3) { + // this.updateHyperlink failed to be created somehow. Let's check again after 1 minute. + this.updateRetryCount++; + this.updateAvailable = false; + this.configManager.scheduleUpdateCheck(61000); + } + } + }); + } + + /** + * Compares the latest version with the existing config version. + * @param {string} version - The latest version. + * @param {string} storedVersion - The config version. + */ + migrateCheck(version, storedVersion) { + const configFile = this.config.readConfiguration(); + const configFileCustom = this.configCustom.readConfiguration(); + const fileName = `georgia-reborn\\configs\\georgia-reborn-config-${storedVersion}.jsonc`; + const fileNameCustom = `georgia-reborn\\configs\\georgia-reborn-custom-${storedVersion}.jsonc`; + + // * ADJUST OLD CONFIG SETTINGS/NAMES - SOON TO BE REMOVED WHEN ENOUGH TIME HAS PASSED AND EVERYONE HAS THEIR UPDATED CONFIGS * // + // #region ADJUST OLD CONFIG SETTINGS/NAMES - SOON TO BE REMOVED WHEN ENOUGH TIME HAS PASSED AND EVERYONE HAS THEIR UPDATED CONFIGS + // * Remove old settings from the config file and add the new ones + const oldSettings = ['playlistSortArtistDateAsc', 'playlistSortArtistDateDesc', 'playlistSortAlbum', 'playlistSortTitle', 'playlistSortTracknum', 'playlistSortArtistYearAsc', 'playlistSortArtistYearDesc']; + if (this._checkSettings(configFile.settings, ...oldSettings)) { + fso.CopyFile(this.configPath, fb.ProfilePath + fileName); + this.config.writeConfiguration(); + this._deleteSettings(configFile.settings, ...oldSettings); + this.config.addConfigurationObject(grDef.settingsSchema, configFile.settings); + this.config.addConfigurationObject(grDef.settingsSchema, grDef.settingsDefaults, grDef.settingsComments); + this.config.writeConfiguration(configFile.settings); + } + if (!this._gridCheckEntry(configFile.metadataGrid, 'Channels')) { + fso.CopyFile(this.configPath, fb.ProfilePath + fileName); + this.config.addConfigurationObject(grDef.metadataGridSchema, grDef.metadataGridDefaults); + this.config.writeConfiguration(); + window.Reload(); // Reinit new config + } + + // * Rename old color names in the custom config file with the new ones + const oldColorNames = [ + 'preloaderBg', 'preloaderLogo', 'preloaderLowerBarTitle', + 'preloaderProgressBar', 'preloaderProgressBarFill', 'preloaderProgressBarFrame', + 'preloaderUIHacksFrame', + 'g_pl_colors_', 'ui_col_', 'uiBio_col_', 'col_' + ]; + const newColorNames = [ + 'grCol_preloaderBg', 'grCol_preloaderLogo', 'grCol_preloaderLowerBarTitle', + 'grCol_preloaderProgressBar', 'grCol_preloaderProgressBarFill', 'grCol_preloaderProgressBarFrame', + 'grCol_preloaderUIHacksFrame', + 'pl_col_', 'lib_ui_col_', 'bio_ui_col_', 'grCol_' + ]; + if (this._checkSettingsByPrefix(configFileCustom.customTheme01, ...oldColorNames)) { + this._renameSettings(configFileCustom.customTheme01, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme02, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme03, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme04, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme05, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme06, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme07, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme08, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme09, oldColorNames, newColorNames); + this._renameSettings(configFileCustom.customTheme10, oldColorNames, newColorNames); + configFileCustom.customTheme01 = this.configCustom.addConfigurationObject(grDef.customTheme01Schema, configFileCustom.customTheme01, grDef.customThemeComments); + configFileCustom.customTheme02 = this.configCustom.addConfigurationObject(grDef.customTheme02Schema, configFileCustom.customTheme02, grDef.customThemeComments); + configFileCustom.customTheme03 = this.configCustom.addConfigurationObject(grDef.customTheme03Schema, configFileCustom.customTheme03, grDef.customThemeComments); + configFileCustom.customTheme04 = this.configCustom.addConfigurationObject(grDef.customTheme04Schema, configFileCustom.customTheme04, grDef.customThemeComments); + configFileCustom.customTheme05 = this.configCustom.addConfigurationObject(grDef.customTheme05Schema, configFileCustom.customTheme05, grDef.customThemeComments); + configFileCustom.customTheme06 = this.configCustom.addConfigurationObject(grDef.customTheme06Schema, configFileCustom.customTheme06, grDef.customThemeComments); + configFileCustom.customTheme07 = this.configCustom.addConfigurationObject(grDef.customTheme07Schema, configFileCustom.customTheme07, grDef.customThemeComments); + configFileCustom.customTheme08 = this.configCustom.addConfigurationObject(grDef.customTheme08Schema, configFileCustom.customTheme08, grDef.customThemeComments); + configFileCustom.customTheme09 = this.configCustom.addConfigurationObject(grDef.customTheme09Schema, configFileCustom.customTheme09, grDef.customThemeComments); + configFileCustom.customTheme10 = this.configCustom.addConfigurationObject(grDef.customTheme10Schema, configFileCustom.customTheme10, grDef.customThemeComments); + this.configCustom.writeConfiguration(); + window.Reload(); // Reinit new config + } + // #endregion + + // * Update config settings which have changed since last update + if (version !== storedVersion) { + switch (storedVersion) { + /* eslint-disable no-fallthrough */ + case '3.0-RC1': + this._gridRenameEntry(configFile.metadataGrid, 'Catalog #', 'Catalog'); + this.config.addConfigurationObject(grDef.metadataGridSchema, configFile.metadataGrid); + case '3.0-RC2': + this.config.addConfigurationObject(grDef.themePlaylistGroupingPresetsSchema, grDef.themePlaylistGroupingPresets); + case '3.0-DEV': + // This default block ( latest version ) should appear after all previous versions have fallen through + console.log('> Upgrading Georgia-ReBORN theme settings from', storedVersion); + console.log(`> Backing up Georgia-ReBORN configuration file to ${fileName}`); + fso.CopyFile(this.configPath, fb.ProfilePath + fileName); + this.config.writeConfiguration(); + fso.CopyFile(this.configPathCustom, fb.ProfilePath + fileNameCustom); + this.configCustom.writeConfiguration(); + break; + default: + break; + } + } + + grSet.version = this.currentVersion; // Always update the version panel property + } + + /** + * Schedules an update check. Sets at startup and then typically every 24 hours after unless an update is found. + * @param {number} delay - In milliseconds. + */ + scheduleUpdateCheck(delay) { + clearTimeout(this.updateTimer); + this.updateTimer = setTimeout(() => { + if (!this.updateAvailable) { + this.checkForUpdates(false); + this.scheduleUpdateCheck(1000 * 60 * 60 * 24); // Check every 24 hours + } + }, delay); + } + // #endregion +} + + +////////////////////////////////////// +// * CONFIGURATION THEME SETTINGS * // +////////////////////////////////////// +/** + * A class that provides methods to get and set the value for config theme settings. + */ +class ConfigurationThemeSetting { + /** + * Creates the `ConfigurationThemeSetting` instance. + * @param {string} name - The name of the setting. + * @param {*} settingVal - The value of the setting. + */ + constructor(name, settingVal) { + /** @protected @constant @type {string} */ + this.name = name; + /** @protected @type {*} */ + this.value = settingVal; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Gets the value of the config. + * @returns {*} The config. + */ + get() { + return this.value; + } + + /** + * Sets the value of the config. + * @param {*} newValue - The new value of the config. + */ + set(newValue) { + if (this.value !== newValue) { + this.value = newValue; + } + } + // #endregion +} + + +/** + * A class that manages a collection of `ConfigurationThemeSetting` instances for a specific theme. + */ +class ConfigurationThemeSettings { + /** + * Creates the `ConfigurationThemeSettings` instance. + * @param {Configuration} config - The theme configuration object. + * @param {string} objName - The name of the settings collection. + * @param {object} properties - An object containing the initial settings as key-value pairs. + */ + constructor(config, objName, properties = {}) { + /** @protected @type {Array} */ + this._propertiesNameList = []; + /** @protected @type {Configuration} */ + this._config = config; + /** @private @type {string} */ + this.objName = objName; + + if (properties) { + this.addProperties(properties.values); + } + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Adds a new ConfigurationThemeSetting instance to the collection with dynamic getter and setter. + * @param {Array} setting - An array with two elements: the name of the setting and its default value. + * @param {string} itemId - A unique identifier for the setting. + * @private + */ + _addConfigItem(setting, itemId) { + this._propertiesNameList[setting[0]] = 1; + + this[`${itemId}_internal`] = new ConfigurationThemeSetting(itemId, setting); + + Object.defineProperty(this, itemId, { + get() { + return this[`${itemId}_internal`].get(); + }, + set(newValue) { + if (this[`${itemId}_internal`].get() !== newValue) { + this[`${itemId}_internal`].set(newValue); + this._config.updateConfigObjValues(this.objName, { [itemId]: newValue }, true); + } + } + }); + } + + /** + * TODO: validation for item? + * + * Validates a setting before adding it to ensure it has the correct format and is not using a reserved identifier. + * @param {*} item - The setting to validate. + * @param {string} itemId - The identifier for the setting. + * @throws {ArgumentError} Thrown if itemId is using a reserved identifier or if the identifier is already occupied. + * @private + */ + _validateConfigItem(item, itemId) { + // if (!Array.isArray(item) || item.length !== 2 || !IsString(item[0])) { + // throw new InvalidTypeError('property', typeof item, '{ string, [string, any] }', 'Usage: addProperties({\n property_id: [property_name, property_default_value]\n})'); + // } + if (itemId === 'add_properties') { + throw new ArgumentError('property_id', itemId, 'This id is reserved'); + } + if (this[itemId] || this[`${itemId}_internal`]) { + throw new ArgumentError('property_id', itemId, 'This id is already occupied'); + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Adds multiple ConfigurationThemeSetting instances to the collection based on the provided properties object. + * @param {object} properties - An object containing settings as key-value pairs. + */ + addProperties(properties) { + for (const key of Object.keys(properties)) { + this._validateConfigItem(properties[key], key); + this._addConfigItem(properties[key], key); + } + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Base/gr-configuration.js b/profile/georgia-reborn/scripts/Base/gr-configuration.js deleted file mode 100644 index 92cb6882..00000000 --- a/profile/georgia-reborn/scripts/Base/gr-configuration.js +++ /dev/null @@ -1,384 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Configuration * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -/////////////////////// -// * CONFIGURATION * // -/////////////////////// -/** @type {Object} Creates a config object. */ -const ConfigurationObjectType = { - Array: 'array', - Object: 'object', - Value: 'value' // Not currently handled -}; - -/** - * @typedef {Object} FieldDefinition - * @property {string} name - * @property {boolean=} optional - */ - -/** - * Defines the structure and properties of an object in a config file. - */ -class ConfigurationObjectSchema { - /** - * @param {string} name The name for the object in the config file. i.e. if the object is `grid: {}`, then name should be `'grid'`. - * @param {string} container The type of container for the object. Should be of ConfigurationObjectType. - * @param {Array=} fields The fields for each entry in the object. If undefined, uses key/value pairs for objects, or comma separated values for arrays. - * @param {string=} comment Adds a '//' field as first entry in the object. Used for explaining things to the user. - */ - constructor(name, container, fields = undefined, comment = undefined) { - this.name = name; - this.container = container; - this.fields = fields; - this.comment = comment; - } -} - -/** - * @typedef {Object} ConfigurationObject - * @property {ConfigurationObjectSchema} definition - * @property {Array} values - * @property {Array} comments - */ - -/** - * Reads and writes the theme config to a JSON file. - */ -class Configuration { - /** - * Instantiates a Configuration object and specifies a file to read from. - * @param {string} configurationPath The path to the config file. - */ - constructor(configurationPath) { - this.path = configurationPath; - if (!configurationPath.includes('.jsonc')) { - console.log(''); - } - - /** - * @protected - * @type {Array} - */ - this._configuration = []; - } - - /** - * Checks if the config file exists. - * @returns {boolean} True or false. - */ - get fileExists() { - return IsFile(this.path); - } - - /** - * Adds a config object to an array, replacing an existing object if it already exists. - * @param {ConfigurationObjectSchema} objectDefinition The description of the object. - * @param {*} values The values that will be written. - * @param {*} comments The comment for the entry. - * @returns {ThemeSettings} Provides getters and setters to automatically update the config file when config value changes. - */ - addConfigurationObject(objectDefinition, values, comments = []) { - /** @type {ConfigurationObject} */ - const obj = { definition: objectDefinition, values, comments }; - const idx = this._configuration.findIndex(c => c.definition.name === objectDefinition.name); - if (idx !== -1) { - // Replace existing object - this._configuration.splice(idx, 1, obj); - } else { - this._configuration.push(obj); - } - return this.getConfigObject(objectDefinition.name); - } - - /** - * Gets a config by theme name. - * @param {String} name The theme config name. - * @returns {ThemeSettings} The config. - */ - getConfigObject(name) { - const obj = this._configuration.find(c => c.definition.name === name); - return new ThemeSettings(this, name, obj); - } - - /** - * Updates the values of the config for the given theme name. - * @param {String} objectName The theme config name. - * @param {*} values The values that will be written. - * @param {boolean} writeConfig Whether to write the config to disk. - */ - updateConfigObjValues(objectName, values, writeConfig = false) { - const configObj = this._configuration.find(c => c.definition.name === objectName); - if (Array.isArray(configObj.values)) { - if (Array.isArray(values)) { - configObj.values.splice(0, Infinity, ...values); - } else { - throw new InvalidTypeError('values', typeof values, 'array', 'Don\'t call updateConfigObjValues() to update Array values with non Array objects.'); - } - } else { - Object.assign(configObj.values, values); - } - if (writeConfig) { - this.writeConfiguration(); - } - } - - /** - * Reads the config from disk. - * @returns {Object} An object containing. - */ - readConfiguration() { - try { - const f = fso.GetFile(this.path); - const p = f.OpenAsTextStream(FileMode.Read, FileType.Unicode); - const jsonString = StripJsonComments(p.ReadAll()); - const config = JSON.parse(jsonString); - p.Close(); - return config; - } - catch (e) { - throw new ThemeError( - `Could not read config file:\n${this.path}\n\n` - + 'The JSON appears to be invalid. If you have edited the config file,\n' - + 'ensure that your modifications have the correct syntax and values:\n' - + `${e.message}\n\n` - + 'The error typically occurs on the line before the one indicated in the error message.\n\n' - + 'You can also delete or rename the config file, and on the next startup,\n' - + 'a new default one will be created. As a last resort, you can replace your\n' - + 'current config with a backup config file.' - ); - } - } - - /** - * Writes the config file to the path specified when Configuration was instantiated. - * - * Only needs to be called manually the very first time, or if not calling updateConfigObjValues. - * ( Only happens if not using a ThemeSettings object received from addConfigurationObject ). - */ - writeConfiguration() { - const p = fso.CreateTextFile(this.path, true, true); - - p.WriteLine('/////////////////////////////////////////////////////////////////////////////'); - p.WriteLine('// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * //'); - p.WriteLine('// * Description: Georgia-ReBORN Configuration File * //'); - p.WriteLine('// * Author: TT * //'); - p.WriteLine('// * Org. Author: Mordred * //'); - p.WriteLine('// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * //'); - p.WriteLine('// * * //'); - p.WriteLine('// * Manual changes to this file will take effect on the next reload. * //'); - p.WriteLine('// * To ensure changes are not overwritten or lost, reload the theme * //'); - p.WriteLine('// * immediately after manually changing values. * //'); - p.WriteLine('/////////////////////////////////////////////////////////////////////////////'); - p.WriteLine(''); - p.WriteLine(''); - p.WriteLine('{'); - p.WriteLine('/////////////////////////////////////////////////////////////////////////////'); - p.WriteLine(`\t"configVersion": "${currentVersion}",`); - p.WriteLine('/////////////////////////////////////////////////////////////////////////////'); - p.WriteLine(''); - - for (const conf of this._configuration) { - p.WriteLine(''); - const container = conf.definition.container === ConfigurationObjectType.Array ? '[' : '{'; - p.WriteLine(`\t"${conf.definition.name}": ${container}`); - - if (conf.definition.comment) { - let line = conf.definition.comment; - let done = false; - while (!done) { - const lineLen = 200; - if (line.length < lineLen) { - p.WriteLine(`\t\t// ${line.trim()}`); - done = true; - } else { - const idx = line.lastIndexOf(' ', lineLen); - p.WriteLine(`\t\t// ${line.substr(0, idx).trim()}`); - line = line.substr(idx); - } - } - } - - if (conf.definition.fields) { - // Array of fields - for (const value of conf.values) { - let entry = ''; - if (conf.definition.container === ConfigurationObjectType.Array) { - for (const field of conf.definition.fields) { - if (!field.optional || value[field.name]) { - const quotes = typeof value[field.name] === 'string' ? '"' : ''; - entry += `, "${field.name}": ${quotes}${value[field.name]}${quotes}`; - } - } - entry = `{${entry.substr(1)} }`; - } - const comment = value.comment ? ` // ${value.comment}` : ''; - p.WriteLine(`\t\t${entry}${value !== conf.values[conf.values.length - 1] ? ',' : ''}${comment}`); - } - } - else if (conf.definition.container === ConfigurationObjectType.Array) { - // Array of comma separated entries - for (const val of conf.values) { - p.WriteLine(`\t\t"${val.replace(/\\/g, '\\\\')}"${val !== conf.values[conf.values.length - 1] ? ',' : ''}`); - } - } - else { - // Object with key/value pairs - const keys = Object.keys(conf.values); - for (const key of keys) { - const comment = conf.comments[key] ? ` // ${conf.comments[key]}` : ''; - const quotes = typeof conf.values[key] === 'string' ? '"' : ''; - p.WriteLine(`\t\t"${key}": ${quotes}${conf.values[key]}${quotes}${key !== keys[keys.length - 1] ? ',' : ''}${comment}`); - } - } - const closeContainer = conf.definition.container === ConfigurationObjectType.Array ? ']' : '}'; - p.WriteLine(`\t${closeContainer}${conf !== this._configuration[this._configuration.length - 1] ? ',' : ''}`); - } - - p.WriteLine('}'); - p.Close(); - } - - /** - * Gets the path to the config file - * @returns {string} The path to the config file. - */ - getPath() { - return this.path; - } - - /** - * Resets the config to the default values. - */ - resetConfiguration() { - fso.DeleteFile(this.path); - setTimeout(() => { - window.Reload(); - }, 1); - } -} - - -//////////////////////// -// * THEME SETTINGS * // -//////////////////////// -/** - * Provides methods to get and set the value of the config. - */ -class ThemeSetting { - /** - * @param {string} name The name of the setting. - * @param {*} settingVal The value of the setting. - */ - constructor(name, settingVal) { - /** @const {string} */ - this.name = name; - this.value = settingVal; - } - - /** - * Gets the value of the config. - * @returns {*} The config. - */ - get() { - return this.value; - } - - /** - * Sets the value of the config. - * @param {*} new_value The new value of the config. - */ - set(new_value) { - if (this.value !== new_value) { - this.value = new_value; - } - } -} - - -/** - * Creates and manages config settings for a theme. - */ -class ThemeSettings { - /** - * @param {Configuration} config The config. - * @param {String} objName The name of the config. - * @param {ConfigurationObject} properties The properties to add to the config. - */ - constructor(config, objName, properties = undefined) { - /** @protected */ - this._properties_name_list = []; - /** @protected */ - /** @type {Configuration} */ - this._config = config; - this.objName = objName; - if (properties) { - this.add_properties(properties.values); - } - } - - /** - * Adds a set of properties to the config. - * @param {ConfigurationObject|*} properties Each item in array is an array of objects - */ - add_properties(properties) { - for (const key of Object.keys(properties)) { - this.validate_config_item(properties[key], key); - this.add_config_item(properties[key], key); - } - } - - /** - * TODO: validation for item? - * - * Validates a config item throwing an InvalidTypeError if it's not valid when added to the config. - * @param {*} item The config item to validate. - * @param {String} item_id The id of the config item. - */ - validate_config_item(item, item_id) { - // if (!Array.isArray(item) || item.length !== 2 || !IsString(item[0])) { - // throw new InvalidTypeError('property', typeof item, '{ string, [string, any] }', 'Usage: add_properties({\n property_id: [property_name, property_default_value]\n})'); - // } - if (item_id === 'add_properties') { - throw new ArgumentError('property_id', item_id, 'This id is reserved'); - } - if (this[item_id] || this[`${item_id}_internal`]) { - throw new ArgumentError('property_id', item_id, 'This id is already occupied'); - } - } - - /** - * Adds a new item to a config object with getter and setter methods for accessing and updating the item's value. - * @param {Array} setting An array with two elements: the name of the setting and its default value. - * @param {string} item_id A unique identifier for the config item used to create a property on the object with the same name. - */ - add_config_item(setting, item_id) { - this._properties_name_list[setting[0]] = 1; - - this[`${item_id}_internal`] = new ThemeSetting(item_id, setting); - - Object.defineProperty(this, item_id, { - get() { - return this[`${item_id}_internal`].get(); - }, - set(new_value) { - if (this[`${item_id}_internal`].get() !== new_value) { - this[`${item_id}_internal`].set(new_value); - this._config.updateConfigObjValues(this.objName, { [item_id]: new_value }, true); - } - } - }); - } -} diff --git a/profile/georgia-reborn/scripts/Base/gr-context-menu.js b/profile/georgia-reborn/scripts/Base/gr-context-menu.js deleted file mode 100644 index 1648a4d0..00000000 --- a/profile/georgia-reborn/scripts/Base/gr-context-menu.js +++ /dev/null @@ -1,1789 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Context Menu Control * // -// * Author: TT * // -// * Org. Author: TheQwertiest * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -///////////////////// -// * BASE OBJECT * // -///////////////////// -/** - * The base object for creating context menus. - */ -class ContextBaseObject { - /** - * @param {string} text_arg The text value that will be assigned to the `text` property of the object. - */ - constructor(text_arg) { - /** @const {string} */ - this.text = text_arg; - - /** @type {?number} */ - this.idx = undefined; - } - - /** - * @param {number} start_idx The index at which the menu should start initializing. - * @returns {number} The end_idx. - * @protected - * @abstract - */ - initialize_menu_idx(start_idx) { - throw new LogicError('initialize_menu_idx not implemented'); - } - - /** - * @param {ContextMenu} parent_menu The parent of the menu being initialized. - * @protected - * @abstract - */ - initialize_menu(parent_menu) { - throw new LogicError('initialize_menu not implemented'); - } - - /** - * @param {number} idx The index of the menu option that needs to be executed. - * @returns {boolean} True or false. - * @protected - * @abstract - */ - execute_menu(idx) { - throw new LogicError('execute_menu not implemented'); - } -} - - -////////////////////// -// * CONTEXT MENU * // -////////////////////// -/** - * Provides methods for adding items to the context menu and handling user interactions. - * @extends {ContextBaseObject} - */ -class ContextMenu extends ContextBaseObject { - /** - * Initializes properties and creates a context menu. - * @param {string} text_arg The text value for the constructor. - * @param {object} [optional_args={}] Additional parameters that can be passed to the constructor. - * @param {boolean=} [optional_args.is_grayed_out=false] The item will be grayed out. - * @param {boolean=} [optional_args.is_checked=false] The item will be checked. - */ - constructor(text_arg, optional_args) { - super(text_arg); - - /** @const {boolean} */ - this.is_grayed_out = !!(optional_args && optional_args.is_grayed_out); - - /** @protected */ - this.menu_items = []; - - this.cm = window.CreatePopupMenu(); - } - - // public: - - /** - * Adds an item to the "menu_items" array. - * @param {ContextBaseObject} item The item to be append to the "menu_items" array. - */ - append(item) { - if (!(item instanceof ContextBaseObject)) { - throw new InvalidTypeError('context_item', typeof item, 'instanceof ContextBaseObject'); - } - - this.menu_items.push(item); - } - - /** - * Appends a new ContextItem object to the current context. - * @param {string} text_arg The text content of the item to be append. - * @param {function} callback_fn_arg A function that will be called when the appended item is clicked or activated. - * @param {object} [optional_args={}] Additional parameters that can be passed to the function. - * @param {boolean=} [optional_args.is_grayed_out=false] The item will be grayed out. - * @param {boolean=} [optional_args.is_checked=false] The item will be checked. - * @param {boolean=} [optional_args.is_radio_checked=false] The item will be radio checked. - */ - append_item(text_arg, callback_fn_arg, optional_args) { - this.append(new ContextItem(text_arg, callback_fn_arg, optional_args)); - } - - /** - * Appends a menu separator to the current context menu. - */ - append_separator() { - this.append(new ContextSeparator()); - } - - /** - * Checks a specific item in a menu and sets it to a radio checked state. - * @param {number} start_idx The starting index of the menu_items array where the radio_check should begin checking. - * @param {number} check_idx The index of the menu item that you want to perform a radio check on. - */ - radio_check(start_idx, check_idx) { - const item = this.menu_items[start_idx + check_idx]; - if (!item) { - throw new ArgumentError('check_idx', check_idx, 'Value is out of bounds'); - } - - if (start_idx >= this.menu_items.length) { - throw new ArgumentError('start_idx', start_idx, 'Value is out of bounds'); - } - - if (item instanceof ContextSeparator) { - throw new ArgumentError('check_idx', check_idx, 'Index points to MenuSeparator'); - } - - item.radio_check(true); - } - - /** - * Checks if the menu_items array is empty. - * @returns {boolean} True or false. - */ - is_empty() { - return IsEmpty(this.menu_items); - } - - /** - * Disposes of each item in the menu_items array and sets the menu_items property to null. - */ - dispose() { - this.cm = null; - - const items = this.menu_items; - for (let i = 0; i < items.length; ++i) { - if (items[i].dispose) { - items[i].dispose(); - } - items[i] = null; - } - - this.menu_items = null; - } - - /** - * Initializes the menu index for each menu item recursively, starting from a given index. - * @param {number} start_idx The index at which the menu should start initializing. - * @returns {number} The end_idx. - * @protected - */ - initialize_menu_idx(start_idx) { - let cur_idx = start_idx; - - this.idx = cur_idx++; - for (const item of this.menu_items) { - if (!item.initialize_menu_idx) { - continue; - } - cur_idx = item.initialize_menu_idx(cur_idx); - } - - return cur_idx; - } - - /** - * Initializes a menu by appending menu items to it and setting their properties based on the state of the parent menu. - * @param {ContextMenu} parent_menu The menu to which the current menu will be appended. - * @protected - */ - initialize_menu(parent_menu) { - for (const item of this.menu_items) { - item.initialize_menu(this); - } - - this.cm.AppendTo(parent_menu.cm, this.is_grayed_out ? MF_GRAYED : MF_STRING, this.text); - } - - /** - * Executes a menu item based on the given index. - * @param {number} idx The index of the menu item that needs to be executed. - * @returns {boolean} The result of calling the `execute_menu` method on the menu item that matches the given index (`idx`). - * @protected - */ - execute_menu(idx) { - for (let i = 0; i < this.menu_items.length; ++i) { - const items = this.menu_items; - const item = items[i]; - const next_item = items[i + 1]; - - if (idx === item.idx || (idx > item.idx && (!next_item || idx < next_item.idx))) { - return item.execute_menu(idx); - } - } - } -} - - -////////////// -// * ITEM * // -////////////// -/** - * Context menu items with various properties and methods for customization and interaction. - * @extends {ContextBaseObject} - */ -class ContextItem extends ContextBaseObject { - /** - * Initializes properties and creates a menu item. - * @param {string} text_arg The text value for the constructor. - * @param {function} callback_fn_arg A callback function that will be assigned to the`callback_fn` property. - * @param {object} [optional_args={}] Additional parameters that can be passed to the constructor. - * @param {boolean=} [optional_args.is_grayed_out=false] The item will be grayed out. - * @param {boolean=} [optional_args.is_checked=false] The item will be checked. - * @param {boolean=} [optional_args.is_radio_checked=false] The ratio item will be checked. - */ - constructor(text_arg, callback_fn_arg, optional_args) { - super(text_arg); - - // const - /** @const {function} */ - this.callback_fn = callback_fn_arg; - - /** @const {boolean} */ - this.is_grayed_out = !!(optional_args && optional_args.is_grayed_out); - - this.is_checked = !!(optional_args && optional_args.is_checked); - this.is_radio_checked = !!(optional_args && optional_args.is_radio_checked); - } - - // public: - - /** - * Sets the state of the check mark ✓ in menu items. - * @param {boolean} is_checked_arg Whether something is checked or not. - */ - check(is_checked_arg) { - this.is_checked = is_checked_arg; - } - - /** - * Sets the state of the radio mark 🔘 in menu items. - * @param {boolean} is_checked_arg Whether something is checked or not. - */ - radio_check(is_checked_arg) { - this.is_radio_checked = is_checked_arg; - } - - // protected: - - /** - * Initializes a menu index and returns the incremented value. - * @param {number} start_idx The initial value for the menu index. - * @returns {number} The end_idx. - * @protected - */ - initialize_menu_idx(start_idx) { - this.idx = start_idx; - return this.idx + 1; - } - - /** - * Initializes a menu item by appending it to a parent menu and setting its properties such as grayed out, checked, or radio checked. - * @param {ContextMenu} parent_menu The menu object that the current menu item belongs to. - * @protected - */ - initialize_menu(parent_menu) { - parent_menu.cm.AppendMenuItem(this.is_grayed_out ? MF_GRAYED : MF_STRING, this.idx, this.text); - if (this.is_checked) { - parent_menu.cm.CheckMenuItem(this.idx, true); - } - else if (this.is_radio_checked) { - parent_menu.cm.CheckMenuRadioItem(this.idx, this.idx, this.idx); - } - } - - /** - * Executes a menu item's callback function if the provided index matches the stored index. - * @param {number} idx The index of the menu item that needs to be executed. - * @returns {boolean} True or false. - * @protected - */ - execute_menu(idx) { - if (this.idx !== idx) { - return false; - } - - this.callback_fn(); - return true; - } -} - - -/////////////////// -// * SEPARATOR * // -/////////////////// -/** - * Handles a separator in a context menu. - * @extends {ContextBaseObject} - */ -class ContextSeparator extends ContextBaseObject { - constructor() { - super(''); - } - - /** - * Initializes a menu index and returns the incremented value. - * @param {number} start_idx The initial value for the menu index. - * @returns {number} The end_idx. - * @protected - */ - initialize_menu_idx(start_idx) { - this.idx = start_idx; - return this.idx + 1; - } - - /** - * Initializes a menu by appending a separator to the parent menu. - * @param {ContextMenu} parent_menu The menu to which the new menu item will be added as a child. - * @protected - */ - initialize_menu(parent_menu) { - parent_menu.cm.AppendMenuSeparator(); - } - - /** - * Execute menu returns false. - * @param {number} idx The index of the menu item to execute. - * @returns {boolean} False if the menu item was executed successfully, true otherwise. - * @protected - */ - execute_menu(idx) { - return false; - } -} - - -///////////////////// -// * FOOBAR MENU * // -///////////////////// -/** - * Provides methods for initializing and executing the foobar2000 context menu. - * @extends {ContextBaseObject} - */ -class ContextFoobarMenu extends ContextBaseObject { - /** - * Initializes a context menu manager. - * @param {FbMetadbHandleList} metadb_handles_arg An array of media database handles. - */ - constructor(metadb_handles_arg) { - super(''); - - /** @private {IContextMenuManager} */ - this.cm = fb.CreateContextMenuManager(); - - this.metadb_handles = metadb_handles_arg; - } - - /** - * Disposes the foobar menu. - */ - dispose() { - this.cm = null; - } - - /** - * Initializes a menu index and returns the index plus 5000. - * @param {number} start_idx The initial value for the menu index. - * @returns {number} The end_idx. - * @protected - */ - initialize_menu_idx(start_idx) { - this.idx = start_idx; - return this.idx + 5000; - } - - /** - * Initializes a menu by initializing the context and building the menu items. - * @param {ContextMenu} parent_menu The parent menu to which the new menu will be added as a child. - * @protected - */ - initialize_menu(parent_menu) { - this.cm.InitContext(this.metadb_handles); - this.cm.BuildMenu(parent_menu.cm, this.idx); - } - - /** - * Executes a menu item based on its index. - * @param {number} idx The index of the menu that needs to be executed. - * @returns {boolean} The result of executing the command with the specified id. - * @protected - */ - execute_menu(idx) { - return this.cm.ExecuteByID(idx - this.idx); - } -} - - -/////////////////// -// * MAIN MENU * // -/////////////////// -/** - * The main context menu with multiple items that can be executed when clicked. - * @extends {ContextMenu} - */ -class ContextMainMenu extends ContextMenu { - /** - * @final - */ - constructor() { - super(''); - } - - // public: - - /** - * Executes a menu by initializing it, displaying it and executing the selected menu item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True if some item was clicked. - */ - execute(x, y) { - // Initialize menu - let cur_idx = 1; - for (const item of this.menu_items) { - if (!item.initialize_menu_idx) { - continue; - } - cur_idx = item.initialize_menu_idx(cur_idx); - } - - for (const item of this.menu_items) { - item.initialize_menu(this); - } - - // Execute menu - const idx = this.cm.TrackPopupMenu(x, y); - if (!idx) { - return false; - } - - return this.execute_menu(idx); - } -} - - -////////////////////////////// -// * DEFAULT CONTEXT MENU * // -////////////////////////////// -/** - * Contains some basic and SMP related options. - * Displayed when shift right clicking in playlist panel or playlist manager. - */ -Object.assign(qwr_utils, { - /** - * @param {ContextMenu} cm The context menu object. - */ - append_default_context_menu_to(cm) { - if (!cm) { - return; - } - - if (!cm.is_empty()) { - cm.append_separator(); - } - - cm.append_item('Console', () => { - fb.ShowConsole(); - }); - - cm.append_item('Restart', () => { - fb.RunMainMenuCommand('File/Restart'); - }); - - cm.append_item('Preferences...', () => { - fb.RunMainMenuCommand('File/Preferences'); - }); - - cm.append_separator(); - - cm.append_item('Configure panel...', () => { - window.ShowConfigure(); - }); - - cm.append_item('Panel properties...', () => { - window.ShowProperties(); - }); - } -}); - - -/////////////////////////////// -// * TOP MENU CONTEXT MENU * // -/////////////////////////////// -Object.assign(qwr_utils, { - /** - * @param {ContextMenu} cmac The context menu object. - */ - append_top_menu_context_menu_to(cmac) { - const updateButtons = () => { - createButtonImages(); - createButtonObjects(ww, wh); - RepaintWindow(); - }; - - const topMenuDisplayMenu = new ContextMenu('Display'); - - // * DISPLAY - SHOW TOP MENU BUTTONS - DEFAULT * // - const topMenuDisplayMenuDefault = new ContextMenu('Default'); - topMenuDisplayMenuDefault.append_item('Details', () => { - pref.showPanelDetails_default = !pref.showPanelDetails_default; - updateButtons(); - }, { is_checked: pref.showPanelDetails_default }); - topMenuDisplayMenuDefault.append_item('Library', () => { - pref.showPanelLibrary_default = !pref.showPanelLibrary_default; - updateButtons(); - }, { is_checked: pref.showPanelLibrary_default }); - topMenuDisplayMenuDefault.append_item('Biography', () => { - pref.showPanelBiography_default = !pref.showPanelBiography_default; - updateButtons(); - }, { is_checked: pref.showPanelBiography_default }); - topMenuDisplayMenuDefault.append_item('Lyrics', () => { - pref.showPanelLyrics_default = !pref.showPanelLyrics_default; - updateButtons(); - }, { is_checked: pref.showPanelLyrics_default }); - topMenuDisplayMenuDefault.append_item('Rating', () => { - pref.showPanelRating_default = !pref.showPanelRating_default; - updateButtons(); - }, { is_checked: pref.showPanelRating_default }); - topMenuDisplayMenu.append(topMenuDisplayMenuDefault); - - // * DISPLAY - SHOW TOP MENU BUTTONS - ARTWORK * // - const topMenuDisplayMenuArtwork = new ContextMenu('Artwork'); - topMenuDisplayMenuArtwork.append_item('Details', () => { - pref.showPanelDetails_artwork = !pref.showPanelDetails_artwork; - updateButtons(); - }, { is_checked: pref.showPanelDetails_artwork }); - topMenuDisplayMenuArtwork.append_item('Library', () => { - pref.showPanelLibrary_artwork = !pref.showPanelLibrary_artwork; - updateButtons(); - }, { is_checked: pref.showPanelLibrary_artwork }); - topMenuDisplayMenuArtwork.append_item('Biography', () => { - pref.showPanelBiography_artwork = !pref.showPanelBiography_artwork; - updateButtons(); - }, { is_checked: pref.showPanelBiography_artwork }); - topMenuDisplayMenuArtwork.append_item('Lyrics', () => { - pref.showPanelLyrics_artwork = !pref.showPanelLyrics_artwork; - updateButtons(); - }, { is_checked: pref.showPanelLyrics_artwork }); - topMenuDisplayMenuArtwork.append_item('Rating', () => { - pref.showPanelRating_artwork = !pref.showPanelRating_artwork; - updateButtons(); - }, { is_checked: pref.showPanelRating_artwork }); - topMenuDisplayMenu.append(topMenuDisplayMenuArtwork); - topMenuDisplayMenu.append_separator(); - - // * DISPLAY - ALIGN TOP MENU BUTTONS * // - const topMenuDisplayAlign = [['Align left', 'left'], ['Align center', 'center']]; - for (const align of topMenuDisplayAlign) { - topMenuDisplayMenu.append_item(align[0], function (align) { - pref.topMenuAlignment = align; - updateButtons(); - }.bind(null, align[1]), { is_radio_checked: align[1] === pref.topMenuAlignment }); - } - topMenuDisplayMenu.append_separator(); - topMenuDisplayMenu.append_item('Compact top menu', () => { - pref.topMenuCompact = !pref.topMenuCompact; - if (!pref.topMenuCompact) { - topMenuCompact(false); - topMenuCompactExpanded = false; - } - }, { is_checked: pref.topMenuCompact }); - - cmac.append(topMenuDisplayMenu); - - // * STYLE - TOP MENU BUTTONS * // - const topMenuStyleMenu = new ContextMenu('Style'); - const topMenuButtonStyle = [ - ['Default', 'default'], - ['Filled', 'filled'], - ['Bevel', 'bevel'], - ['Inner', 'inner'], - ['Emboss', 'emboss'], - ['Minimal', 'minimal'] - ]; - for (const style of topMenuButtonStyle) { - topMenuStyleMenu.append_item(style[0], function (style) { - pref.styleTopMenuButtons = style; - if (!pref.themeSandbox) pref.savedStyleTopMenuButtons = pref.styleTopMenuButtons = style; else pref.styleTopMenuButtons = style; - updateStyle(); - }.bind(null, style[1]), { is_radio_checked: style[1] === pref.styleTopMenuButtons }); - } - cmac.append(topMenuStyleMenu); - } -}); - - -//////////////////////////////// -// * ALBUM ART CONTEXT MENU * // -//////////////////////////////// -/** - * Contains some options not find in top menu "Options" and append panel related top menu "Options" for quick access. - * Displayed when right clicking on the big album art on the left side. - */ -Object.assign(qwr_utils, { - /** - * @param {ContextMenu} cmac The context menu object. - */ - append_album_cover_context_menu_to(cmac) { - if (!pref.showTransportControls_default) { - cmac.append_item('Stop', () => { - fb.Stop(); - }); - cmac.append_item('Previous', () => { - fb.Prev(); - }); - cmac.append_item(fb.IsPlaying ? 'Pause' : 'Play', () => { - fb.PlayOrPause(); - }); - cmac.append_item('Next', () => { - fb.Next(); - }); - cmac.append_separator(); - - const playbackOrderMenu = new ContextMenu('Playback order'); - const playbackOrderModes = ['default', 'repeatPlaylist', 'repeatTrack', 'shuffle']; - for (const playbackOrder of playbackOrderModes) { - playbackOrderMenu.append_item(playbackOrder, () => { - switch (playbackOrder) { - case 'default': - pref.playbackOrder = 'default'; - fb.RunMainMenuCommand('Playback/Order/Default'); - break; - case 'repeatPlaylist': - pref.playbackOrder = 'repeatPlaylist'; - fb.RunMainMenuCommand('Playback/Order/Repeat (playlist)'); - break; - case 'repeatTrack': - pref.playbackOrder = 'repeatTrack'; - fb.RunMainMenuCommand('Playback/Order/Repeat (track)'); - break; - case 'shuffle': - pref.playbackOrder = 'shuffle'; - fb.RunMainMenuCommand('Playback/Order/Shuffle (tracks)'); - break; - } - }, { is_radio_checked: playbackOrder === pref.playbackOrder }); - } - cmac.append(playbackOrderMenu); - cmac.append_separator(); - } - - // * Top menu options - Playlist, Details, Library, Lyrics - context menu - const showPlaylist = pref.layout === 'artwork' ? displayPlaylistArtwork && !pref.displayLyrics : displayPlaylist && !pref.displayLyrics; - - const showDetails = pref.layout === 'artwork' ? displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography && !pref.displayLyrics : - !displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography && !pref.displayLyrics; - - const showArtworkLayoutAlbumArt = pref.layout === 'artwork' && !displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography && !pref.displayLyrics; - - if (!showArtworkLayoutAlbumArt) { - cmac.append_item(showPlaylist ? 'Playlist options menu' : showDetails ? 'Details options menu' : displayLibrary ? 'Library options menu' : 'Lyrics options menu', () => { - if (showPlaylist) { - topMenuOptions(displayBiography ? state.mouse_x * 2 : state.mouse_x, state.mouse_y, true, true); - } - else if (showDetails) { - topMenuOptions(state.mouse_x, state.mouse_y, true, false, true); - } - else if (displayLibrary) { - topMenuOptions(state.mouse_x, state.mouse_y, true, false, false, true); - } - else if (pref.displayLyrics) { - topMenuOptions(state.mouse_x, state.mouse_y, true, false, false, false, false, true); - } - }); - } - - if (pref.theme === 'random') { - cmac.append_item('Generate new color', () => { - getRandomThemeColorContextMenu = true; - initTheme(); - setTimeout(() => { getRandomThemeColorContextMenu = false }, 200); - }); - cmac.append_separator(); - } - - if (pref.layout === 'default' && pref.theme.startsWith('custom')) { - cmac.append_separator(); - cmac.append_item('Edit custom theme', () => { - displayCustomThemeMenu = true; - if (showPlaylist) { - displayPanel('playlist'); - initCustomThemeMenu('pl_bg'); - } - else if (showDetails) { - displayPanel('details'); - initCustomThemeMenu(false, 'main_bg'); - } - else if (displayLibrary) { - displayPanel('library'); - initCustomThemeMenu(false, false, 'lib_bg'); - } - else if (pref.displayLyrics) { - displayPanel('lyrics'); - initCustomThemeMenu(false, 'main_text'); - } - window.Repaint(); - }); - } - - if (showDetails) { - cmac.append_item('Edit metadata grid', () => { - if (pref.layout === 'default') { - displayMetadataGridMenu = !displayMetadataGridMenu; - if (!displayDetails) { - displayDetails = true; - displayPlaylist = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - resizeArtwork(true); - initButtonState(); - } - initMetadataGridMenu(1); - RepaintWindow(); - } else { - const configPath = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc`; - fb.ShowPopupMessage(`Metadata grid can only be live edited in default layout:\nOptions > Layout > Default\n\nYou could manually edit your config file while reloading to take effect:\n${configPath}\n`, 'Metadata grid live editing'); - } - }); - cmac.append_separator(); - } - - if (pref.layout === 'default') { - if (displayPlaylist && !displayBiography && !pref.displayLyrics) { - cmac.append_item(displayPlaylist && pref.playlistLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { - pref.playlistLayout = pref.playlistLayout === 'normal' ? 'full' : 'normal'; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - RepaintWindowRectAreas(); - g_properties.auto_collapse = false; - playlist.expand_header(); - playlist.on_size(ww, wh); - jumpSearch.on_size(); - initButtonState(); - }); - cmac.append_separator(); - } - else if (displayLibrary) { - cmac.append_separator(); - cmac.append_item(displayLibrary && pref.libraryLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { - pref.libraryLayout = pref.libraryLayout === 'normal' ? 'full' : 'normal'; - displayPlaylist = pref.libraryLayout === 'split'; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - initLibraryLayout(); - initButtonState(); - }); - if (pref.libraryLayout === 'normal') { - cmac.append_item(displayLibrary && pref.libraryLayout === 'normal' ? 'Change layout to split' : 'Change layout to normal', () => { - pref.libraryLayout = pref.libraryLayout === 'normal' ? 'split' : 'normal'; - displayPlaylist = pref.libraryLayout === 'split'; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - initLibraryLayout(); - initButtonState(); - }); - cmac.append_separator(); - } - } - else if (pref.displayLyrics) { - cmac.append_item(pref.displayLyrics && pref.lyricsLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { - pref.lyricsLayout = pref.lyricsLayout === 'normal' ? 'full' : 'normal'; - displayPlaylist = !displayPlaylist; - lyricsLayoutFullWidth = pref.lyricsLayout === 'full'; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - RepaintWindowRectAreas(); - resizeArtwork(true); - initButtonState(); - }); - cmac.append_separator(); - } - } - - cmac.append_item(pref.layout !== 'artwork' && (displayPlaylist || pref.displayLyrics && pref.lyricsLayout === 'full') ? 'Details' : 'Playlist', () => { - if (pref.layout !== 'artwork') { - btns.details.onClick(); - if (displayPlaylist && !pref.lyricsActiveState) pref.displayLyrics = false; - } - else if (pref.layout === 'artwork') { - btns.playlistArtworkLayout.onClick(); - if (displayPlaylistArtwork) pref.displayLyrics = false; - } - playlist.on_size(ww, wh); - resizeArtwork(true); - initButtonState(); - window.Repaint(); - }); - cmac.append_separator(); - - cmac.append_item(pref.displayLyrics ? 'Hide lyrics' : 'Display lyrics', () => { - if (pref.layout === 'artwork' && displayPlaylist) btns.details.onClick(); - pref.displayLyrics = !pref.displayLyrics; - if (!pref.displayLyrics && pref.lyricsLayout === 'full' || noAlbumArtStub) { - displayPlaylist = true; - } - if (pref.displayLyrics && pref.lyricsLayout === 'full') { - displayPlaylist = false; - } - playlist.on_size(ww, wh); - initLyrics(); - on_playback_seek(); - resizeArtwork(true); - initButtonState(); - window.Repaint(); - }); - - if (albumArtList.length > 1) { - const loadImage = () => { - setTimeout(() => { - loadImageFromAlbumArtList(albumArtIndex); - if (pref.theme === 'reborn' || pref.theme === 'random' || pref.styleBlackAndWhiteReborn || pref.styleBlackReborn) { - newTrackFetchingArtwork = true; - getThemeColors(albumArt); - initTheme(); - DebugLog('\n>>> initTheme -> Album cover context menu -> Display next/previous artwork <<<\n'); - } - window.Repaint(); - }, !activeMenu); - } - if (albumArtIndex !== albumArtList.length - 1) { - cmac.append_item(fb.IsPlaying ? 'Display next artwork' : '', () => { - albumArtIndex = (albumArtIndex + 1) % albumArtList.length; - loadImage(); - }); - } - if (albumArtIndex !== 0) { - cmac.append_item(fb.IsPlaying ? 'Display previous artwork' : '', () => { - albumArtIndex = (albumArtIndex - 1) % albumArtList.length; - loadImage(); - }); - } - } - - cmac.append_separator(); - - const query = $('$if3(%album artist%, %artist, %composer%)', fb.GetNowPlaying()).replace(/ /g, '%20'); - cmac.append_item('Get disc art', () => { - RunCmd(`https://fanart.tv/?s=${query}§=2`); - }); - - const discArtMenu = new ContextMenu('Disc art placeholder'); - discArtMenu.append_item('Show placeholder if no disc art found', () => { - pref.showDiscArtStub = !pref.showDiscArtStub; - pref.noDiscArtStub = false; - fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }, { is_checked: pref.showDiscArtStub }); - discArtMenu.append_separator(); - discArtMenu.append_item('No placeholder', () => { - pref.noDiscArtStub = !pref.noDiscArtStub; - pref.showDiscArtStub = false; - discArt = disposeDiscArt(discArt); - discArtCover = disposeDiscArt(discArtCover); - discArtArray = []; - discArtArrayCover = []; - if (!pref.noDiscArtStub) fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }, { is_checked: pref.noDiscArtStub }); - discArtMenu.append_separator(); - const displayCdArtMenu = [ - ['CD - Album cover', 'cdAlbumCover'], - ['CD - White', 'cdWhite'], - ['CD - Black', 'cdBlack'], - ['CD - Blank', 'cdBlank'], - ['CD - Transparent', 'cdTrans'] - ]; - for (const cdArt of displayCdArtMenu) { - discArtMenu.append_item(cdArt[0], function (cdArt) { - pref.discArtStub = cdArt; - pref.noDiscArtStub = false; - discArtCover = disposeDiscArt(discArtCover); - discArtArrayCover = []; - fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }.bind(null, cdArt[1]), { is_radio_checked: cdArt[1] === pref.discArtStub }); - } - discArtMenu.append_separator(); - const displayVinylArtMenu = [ - ['Vinyl - Album cover', 'vinylAlbumCover'], - ['Vinyl - White', 'vinylWhite'], - ['Vinyl - Void', 'vinylVoid'], - ['Vinyl - Cold fusion', 'vinylColdFusion'], - ['Vinyl - Ring of fire', 'vinylRingOfFire'], - ['Vinyl - Maple', 'vinylMaple'], - ['Vinyl - Black', 'vinylBlack'], - ['Vinyl - Black hole', 'vinylBlackHole'], - ['Vinyl - Ebony', 'vinylEbony'], - ['Vinyl - Transparent', 'vinylTrans'] - ]; - for (const vinylArt of displayVinylArtMenu) { - discArtMenu.append_item(vinylArt[0], function (vinylArt) { - pref.discArtStub = vinylArt; - pref.noDiscArtStub = false; - discArtCover = disposeDiscArt(discArtCover); - discArtArrayCover = []; - fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }.bind(null, vinylArt[1]), { is_radio_checked: vinylArt[1] === pref.discArtStub }); - } - cmac.append(discArtMenu); - cmac.append_separator(); - - cmac.append_item('Open containing folder', () => { - fb.RunContextCommand('Open Containing Folder'); - }); - - cmac.append_item('Properties', () => { - fb.RunContextCommand('Properties'); - }); - - cmac.append_separator(); - - cmac.append_item('Reload theme', () => { - window.Reload(); - }); - } -}); - - -//////////////////////////////// -// * LOWER BAR CONTEXT MENU * // -//////////////////////////////// -/** - * Contains all seekbar ( progress bar, peakmeter bar and waveform bar ) top menu "Options" for quick access. - * Displayed when right clicking on the lower bar. - */ -Object.assign(qwr_utils, { - /** - * @param {ContextMenu} cmac The context menu object. - */ - append_lower_bar_context_menu_to(cmac) { - const updateButtons = () => { - createButtonImages(); - createButtonObjects(ww, wh); - RepaintWindow(); - }; - - const updateSeekbar = () => { - setGeometry(); - resizeArtwork(true); - RepaintWindow(); - }; - - // * TRANSPORT BUTTON SIZE * // - const transportSizeMenu = new ContextMenu('Transport button size'); - const transportSizeMenuDefault = new ContextMenu('Default'); - const transportSizeDefault = [['28px', 28], ['30px', 30], ['32px (default)', 32], ['34px', 34], ['36px', 36], ['38px', 38], ['40px', 40], ['42px', 42]]; - for (const size of transportSizeDefault) { - transportSizeMenuDefault.append_item(size[0], function (size) { - pref.transportButtonSize_default = size; - if (size === -1) { - pref.transportButtonSize_default -= 2; - } else if (size === 999) { - pref.transportButtonSize_default += 2; - } else { - pref.transportButtonSize_default = size; - } - createFonts(); - resizeArtwork(true); - updateButtons(); - }.bind(null, size[1]), { is_radio_checked: size[1] === pref.transportButtonSize_default }); - } - transportSizeMenu.append(transportSizeMenuDefault); - - const transportSizeMenuArtwork = new ContextMenu('Artwork'); - const transportSizeArtwork = [['28px', 28], ['30px', 30], ['32px (default)', 32], ['34px', 34], ['36px', 36], ['38px', 38], ['40px', 40], ['42px', 42]]; - for (const size of transportSizeArtwork) { - transportSizeMenuArtwork.append_item(size[0], function (size) { - pref.transportButtonSize_artwork = size; - if (size === -1) { - pref.transportButtonSize_artwork -= 2; - } else if (size === 999) { - pref.transportButtonSize_artwork += 2; - } else { - pref.transportButtonSize_artwork = size; - } - createFonts(); - resizeArtwork(true); - updateButtons(); - }.bind(null, size[1]), { is_radio_checked: size[1] === pref.transportButtonSize_artwork }); - } - transportSizeMenu.append(transportSizeMenuArtwork); - - const transportSizeMenuCompact = new ContextMenu('Compact'); - const transportSizeCompact = [['28px', 28], ['30px', 30], ['32px (default)', 32], ['34px', 34], ['36px', 36], ['38px', 38], ['40px', 40], ['42px', 42]]; - for (const size of transportSizeCompact) { - transportSizeMenuCompact.append_item(size[0], function (size) { - pref.transportButtonSize_compact = size; - if (size === -1) { - pref.transportButtonSize_compact -= 2; - } else if (size === 999) { - pref.transportButtonSize_compact += 2; - } else { - pref.transportButtonSize_compact = size; - } - createFonts(); - resizeArtwork(true); - updateButtons(); - }.bind(null, size[1]), { is_radio_checked: size[1] === pref.transportButtonSize_compact }); - } - transportSizeMenu.append(transportSizeMenuCompact); - cmac.append(transportSizeMenu); - - // * TRANSPORT BUTTON SPACING * // - const transportSpacingMenu = new ContextMenu('Transport button spacing'); - const transportSpacingMenuDefault = new ContextMenu('Default'); - const transportSpacingDefault = [['-2', -1], ['3px', 3], ['5px (default)', 5], ['7px', 7], ['10px', 10], ['15px', 15], ['+2', 999]]; - for (const spacing of transportSpacingDefault) { - transportSpacingMenuDefault.append_item(spacing[0], function (spacing) { - pref.transportButtonSpacing_default = spacing; - if (spacing === -1) { - pref.transportButtonSpacing_default -= 2; - } else if (spacing === 999) { - pref.transportButtonSpacing_default += 2; - } else { - pref.transportButtonSpacing_default = spacing; - } - updateStyle(); - }.bind(null, spacing[1]), { is_radio_checked: spacing[1] === pref.transportButtonSpacing_default }); - } - transportSpacingMenu.append(transportSpacingMenuDefault); - - const transportSpacingMenuArtwork = new ContextMenu('Artwork'); - const transportSpacingArtwork = [['-2', -1], ['3px', 3], ['5px (default)', 5], ['7px', 7], ['10px', 10], ['15px', 15], ['+2', 999]]; - for (const spacing of transportSpacingArtwork) { - transportSpacingMenuArtwork.append_item(spacing[0], function (spacing) { - pref.transportButtonSpacing_artwork = spacing; - if (spacing === -1) { - pref.transportButtonSpacing_artwork -= 2; - } else if (spacing === 999) { - pref.transportButtonSpacing_artwork += 2; - } else { - pref.transportButtonSpacing_artwork = spacing; - } - updateStyle(); - }.bind(null, spacing[1]), { is_radio_checked: spacing[1] === pref.transportButtonSpacing_artwork }); - } - transportSpacingMenu.append(transportSpacingMenuArtwork); - - const transportSpacingMenuCompact = new ContextMenu('Compact'); - const transportSpacingCompact = [['-2', -1], ['3px', 3], ['5px (default)', 5], ['7px', 7], ['10px', 10], ['15px', 15], ['+2', 999]]; - for (const spacing of transportSpacingCompact) { - transportSpacingMenuCompact.append_item(spacing[0], function (spacing) { - pref.transportButtonSpacing_compact = spacing; - if (spacing === -1) { - pref.transportButtonSpacing_compact -= 2; - } else if (spacing === 999) { - pref.transportButtonSpacing_compact += 2; - } else { - pref.transportButtonSpacing_compact = spacing; - } - updateStyle(); - }.bind(null, spacing[1]), { is_radio_checked: spacing[1] === pref.transportButtonSpacing_compact }); - } - transportSpacingMenu.append(transportSpacingMenuCompact); - cmac.append(transportSpacingMenu); - - cmac.append_separator(); - const transportButtonDisplayMenu = new ContextMenu('Display'); - - // * SHOW TRANSPORT CONTROLS * // - const transportControlsMenu = new ContextMenu('Show transport controls'); - transportControlsMenu.append_item('Default', () => { - pref.showTransportControls_default = !pref.showTransportControls_default; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showTransportControls_default }); - transportControlsMenu.append_item('Artwork', () => { - pref.showTransportControls_artwork = !pref.showTransportControls_artwork; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showTransportControls_artwork }); - transportControlsMenu.append_item('Compact', () => { - pref.showTransportControls_compact = !pref.showTransportControls_compact; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showTransportControls_compact }); - transportButtonDisplayMenu.append(transportControlsMenu); - - // * SHOW PLAYBACK ORDER BUTTON * // - const playbackOrderBtnMenu = new ContextMenu('Show playback order button'); - playbackOrderBtnMenu.append_item('Default', () => { - pref.showPlaybackOrderBtn_default = !pref.showPlaybackOrderBtn_default; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showPlaybackOrderBtn_default }); - playbackOrderBtnMenu.append_item('Artwork', () => { - pref.showPlaybackOrderBtn_artwork = !pref.showPlaybackOrderBtn_artwork; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showPlaybackOrderBtn_artwork }); - playbackOrderBtnMenu.append_item('Compact', () => { - pref.showPlaybackOrderBtn_compact = !pref.showPlaybackOrderBtn_compact; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showPlaybackOrderBtn_compact }); - transportButtonDisplayMenu.append(playbackOrderBtnMenu); - - // * SHOW RELOAD BUTTON * // - const reloadBtnMenu = new ContextMenu('Show reload button'); - reloadBtnMenu.append_item('Default', () => { - pref.showReloadBtn_default = !pref.showReloadBtn_default; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showReloadBtn_default }); - reloadBtnMenu.append_item('Artwork', () => { - pref.showReloadBtn_artwork = !pref.showReloadBtn_artwork; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showReloadBtn_artwork }); - reloadBtnMenu.append_item('Compact', () => { - pref.showReloadBtn_compact = !pref.showReloadBtn_compact; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showReloadBtn_compact }); - transportButtonDisplayMenu.append(reloadBtnMenu); - - // * SHOW VOLUME BUTTON * // - const volumeBtnMenu = new ContextMenu('Show volume button'); - volumeBtnMenu.append_item('Default', () => { - pref.showVolumeBtn_default = !pref.showVolumeBtn_default; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showVolumeBtn_default }); - volumeBtnMenu.append_item('Artwork', () => { - pref.showVolumeBtn_artwork = !pref.showVolumeBtn_artwork; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showVolumeBtn_artwork }); - volumeBtnMenu.append_item('Compact', () => { - pref.showVolumeBtn_compact = !pref.showVolumeBtn_compact; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.showVolumeBtn_compact }); - volumeBtnMenu.append_separator(); - volumeBtnMenu.append_item('Auto-hide bar', () => { - pref.autoHideVolumeBar = !pref.autoHideVolumeBar; - resizeArtwork(true); - updateButtons(); - }, { is_checked: pref.autoHideVolumeBar }); - transportButtonDisplayMenu.append(volumeBtnMenu); - transportButtonDisplayMenu.append_separator(); - - // * SHOW PLAYBACK TIME IN LOWER BAR * // - const playbackTimeMenu = new ContextMenu('Show playback time'); - playbackTimeMenu.append_item('Default', () => { - pref.showPlaybackTime_default = !pref.showPlaybackTime_default; - updateButtons(); - }, { is_checked: pref.showPlaybackTime_default }); - playbackTimeMenu.append_item('Artwork', () => { - pref.showPlaybackTime_artwork = !pref.showPlaybackTime_artwork; - updateButtons(); - }, { is_checked: pref.showPlaybackTime_artwork }); - playbackTimeMenu.append_item('Compact', () => { - pref.showPlaybackTime_compact = !pref.showPlaybackTime_compact; - updateButtons(); - }, { is_checked: pref.showPlaybackTime_compact }); - transportButtonDisplayMenu.append(playbackTimeMenu); - - // * SHOW ARTIST IN LOWER BAR * // - const showArtistMenu = new ContextMenu('Show artist'); - showArtistMenu.append_item('Default', () => { - pref.showLowerBarArtist_default = !pref.showLowerBarArtist_default; - RepaintWindow(); - }, { is_checked: pref.showLowerBarArtist_default }); - showArtistMenu.append_item('Artwork', () => { - pref.showLowerBarArtist_artwork = !pref.showLowerBarArtist_artwork; - RepaintWindow(); - }, { is_checked: pref.showLowerBarArtist_artwork }); - showArtistMenu.append_item('Compact', () => { - pref.showLowerBarArtist_compact = !pref.showLowerBarArtist_compact; - RepaintWindow(); - }, { is_checked: pref.showLowerBarArtist_compact }); - transportButtonDisplayMenu.append(showArtistMenu); - - // * SHOW TRACK NUMBER IN LOWER BAR * // - const showTrackNumberMenu = new ContextMenu('Show track number'); - showTrackNumberMenu.append_item('Default', () => { - pref.showLowerBarTrackNum_default = !pref.showLowerBarTrackNum_default; - on_metadb_changed(); - RepaintWindow(); - }, { is_checked: pref.showLowerBarTrackNum_default }); - showTrackNumberMenu.append_item('Artwork', () => { - pref.showLowerBarTrackNum_artwork = !pref.showLowerBarTrackNum_artwork; - on_metadb_changed(); - RepaintWindow(); - }, { is_checked: pref.showLowerBarTrackNum_artwork }); - showTrackNumberMenu.append_item('Compact', () => { - pref.showLowerBarTrackNum_compact = !pref.showLowerBarTrackNum_compact; - on_metadb_changed(); - RepaintWindow(); - }, { is_checked: pref.showLowerBarTrackNum_compact }); - transportButtonDisplayMenu.append(showTrackNumberMenu); - - // * SHOW SONG TITLE IN LOWER BAR * // - const showTitleMenu = new ContextMenu('Show song title'); - showTitleMenu.append_item('Default', () => { - pref.showLowerBarTitle_default = !pref.showLowerBarTitle_default; - RepaintWindow(); - }, { is_checked: pref.showLowerBarTitle_default }); - showTitleMenu.append_item('Artwork', () => { - pref.showLowerBarTitle_artwork = !pref.showLowerBarTitle_artwork; - RepaintWindow(); - }, { is_checked: pref.showLowerBarTitle_artwork }); - showTitleMenu.append_item('Compact', () => { - pref.showLowerBarTitle_compact = !pref.showLowerBarTitle_compact; - RepaintWindow(); - }, { is_checked: pref.showLowerBarTitle_compact }); - transportButtonDisplayMenu.append(showTitleMenu); - - // * SHOW COMPOSER IN LOWER BAR * // - const showComposerMenu = new ContextMenu('Show composer'); - showComposerMenu.append_item('Default', () => { - pref.showLowerBarComposer_default = !pref.showLowerBarComposer_default; - RepaintWindow(); - }, { is_checked: pref.showLowerBarComposer_default }); - showComposerMenu.append_item('Artwork', () => { - pref.showLowerBarComposer_artwork = !pref.showLowerBarComposer_artwork; - RepaintWindow(); - }, { is_checked: pref.showLowerBarComposer_artwork }); - showComposerMenu.append_item('Compact', () => { - pref.showLowerBarComposer_compact = !pref.showLowerBarComposer_compact; - RepaintWindow(); - }, { is_checked: pref.showLowerBarComposer_compact }); - transportButtonDisplayMenu.append(showComposerMenu); - - // * SHOW ARTIST COUNTRY FLAGS IN LOWER BAR * // - const showArtistFlagsMenu = new ContextMenu('Show artist country flags'); - showArtistFlagsMenu.append_item('Default', () => { - pref.showLowerBarArtistFlags_default = !pref.showLowerBarArtistFlags_default; - loadCountryFlags(); - RepaintWindow(); - }, { is_checked: pref.showLowerBarArtistFlags_default }); - showArtistFlagsMenu.append_item('Artwork', () => { - pref.showLowerBarArtistFlags_artwork = !pref.showLowerBarArtistFlags_artwork; - loadCountryFlags(); - RepaintWindow(); - }, { is_checked: pref.showLowerBarArtistFlags_artwork }); - showArtistFlagsMenu.append_item('Compact', () => { - pref.showLowerBarArtistFlags_compact = !pref.showLowerBarArtistFlags_compact; - loadCountryFlags(); - RepaintWindow(); - }, { is_checked: pref.showLowerBarArtistFlags_compact }); - transportButtonDisplayMenu.append(showArtistFlagsMenu); - - // * SHOW SOFTWARE VERSION IN LOWER BAR * // - const showSoftwareVersionMenu = new ContextMenu('Show software version'); - showSoftwareVersionMenu.append_item('Default', () => { - pref.showLowerBarVersion_default = !pref.showLowerBarVersion_default; - initMain(); - }, { is_checked: pref.showLowerBarVersion_default }); - showSoftwareVersionMenu.append_item('Artwork', () => { - pref.showLowerBarVersion_artwork = !pref.showLowerBarVersion_artwork; - initMain(); - }, { is_checked: pref.showLowerBarVersion_artwork }); - showSoftwareVersionMenu.append_item('Compact', () => { - pref.showLowerBarVersion_compact = !pref.showLowerBarVersion_compact; - initMain(); - }, { is_checked: pref.showLowerBarVersion_compact }); - transportButtonDisplayMenu.append(showSoftwareVersionMenu); - transportButtonDisplayMenu.append_separator(); - - // * SHOW PROGRESS BAR * // - const progressBarMenu = new ContextMenu('Show progress bar'); - progressBarMenu.append_item('Default', () => { - pref.showProgressBar_default = !pref.showProgressBar_default; - updateSeekbar(); - }, { is_checked: pref.showProgressBar_default }); - progressBarMenu.append_item('Artwork', () => { - pref.showProgressBar_artwork = !pref.showProgressBar_artwork; - updateSeekbar(); - }, { is_checked: pref.showProgressBar_artwork }); - progressBarMenu.append_item('Compact', () => { - pref.showProgressBar_compact = !pref.showProgressBar_compact; - updateSeekbar(); - }, { is_checked: pref.showProgressBar_compact }); - transportButtonDisplayMenu.append(progressBarMenu); - - // * SHOW PEAKMETER BAR * // - const peakmeterBarMenu = new ContextMenu('Show peakmeter bar'); - peakmeterBarMenu.append_item('Default', () => { - pref.showPeakmeterBar_default = !pref.showPeakmeterBar_default; - updateSeekbar(); - }, { is_checked: pref.showPeakmeterBar_default }); - peakmeterBarMenu.append_item('Artwork', () => { - pref.showPeakmeterBar_artwork = !pref.showPeakmeterBar_artwork; - updateSeekbar(); - }, { is_checked: pref.showPeakmeterBar_artwork }); - peakmeterBarMenu.append_item('Compact', () => { - pref.showPeakmeterBar_compact = !pref.showPeakmeterBar_compact; - updateSeekbar(); - }, { is_checked: pref.showPeakmeterBar_compact }); - transportButtonDisplayMenu.append(peakmeterBarMenu); - - // * SHOW WAVEFORM BAR * // - const waveformBarMenu = new ContextMenu('Show waveform bar'); - waveformBarMenu.append_item('Default', () => { - pref.showWaveformBar_default = !pref.showWaveformBar_default; - updateSeekbar(); - }, { is_checked: pref.showWaveformBar_default }); - waveformBarMenu.append_item('Artwork', () => { - pref.showWaveformBar_artwork = !pref.showWaveformBar_artwork; - updateSeekbar(); - }, { is_checked: pref.showWaveformBar_artwork }); - waveformBarMenu.append_item('Compact', () => { - pref.showWaveformBar_compact = !pref.showWaveformBar_compact; - updateSeekbar(); - }, { is_checked: pref.showWaveformBar_compact }); - transportButtonDisplayMenu.append(waveformBarMenu); - - cmac.append(transportButtonDisplayMenu); - cmac.append_separator(); - - // * STYLES - TRANSPORT BUTTONS * // - const transportButtonStyleMenu = new ContextMenu('Style buttons'); - const transportButtonStyles = [ - ['Default', 'default'], - ['Bevel', 'bevel'], - ['Inner', 'inner'], - ['Emboss', 'emboss'], - ['Minimal', 'minimal'] - ]; - for (const style of transportButtonStyles) { - transportButtonStyleMenu.append_item(style[0], function (style) { - pref.styleTransportButtons = style; - if (!pref.themeSandbox) pref.savedStyleTransportButtons = pref.styleTransportButtons = style; else pref.styleTransportButtons = style; - updateStyle(); - }.bind(null, style[1]), { is_radio_checked: style[1] === pref.styleTransportButtons }); - } - cmac.append(transportButtonStyleMenu); - - // * STYLES - VOLUME BAR * // - const transportVolumeBarStyleMenu = new ContextMenu('Style volume bar'); - const transportVolumeBarStylesDesignMenu = new ContextMenu('Design'); - const transportVolumeBarStylesDesign = [['Default', 'default'], ['Rounded', 'rounded']]; - for (const design of transportVolumeBarStylesDesign) { - transportVolumeBarStylesDesignMenu.append_item(design[0], function (design) { - pref.styleVolumeBarDesign = design; - if (!pref.themeSandbox) pref.savedStyleVolumeBarDesign = pref.styleVolumeBarDesign = design; else pref.styleVolumeBarDesign = design; - updateStyle(); - }.bind(null, design[1]), { is_radio_checked: design[1] === pref.styleVolumeBarDesign }); - } - transportVolumeBarStyleMenu.append(transportVolumeBarStylesDesignMenu); - - const transportVolumeBarStylesBgMenu = new ContextMenu('Background'); - const transportVolumeBarStylesBg = [['Default', 'default'], ['Bevel', 'bevel'], ['Inner', 'inner']]; - for (const style of transportVolumeBarStylesBg) { - transportVolumeBarStylesBgMenu.append_item(style[0], function (style) { - pref.styleVolumeBar = style; - if (!pref.themeSandbox) pref.savedStyleVolumeBar = pref.styleVolumeBar = style; else pref.styleVolumeBar = style; - updateStyle(); - }.bind(null, style[1]), { is_radio_checked: style[1] === pref.styleVolumeBar }); - } - transportVolumeBarStyleMenu.append(transportVolumeBarStylesBgMenu); - - const transportVolumeBarStylesFillMenu = new ContextMenu('Fill'); - const transportVolumeBarStylesFill = [['Default', 'default'], ['Bevel', 'bevel'], ['Inner', 'inner']]; - for (const style of transportVolumeBarStylesFill) { - transportVolumeBarStylesFillMenu.append_item(style[0], function (style) { - pref.styleVolumeBarFill = style; - if (!pref.themeSandbox) pref.savedStyleVolumeBarFill = pref.styleVolumeBarFill = style; else pref.styleVolumeBarFill = style; - updateStyle(); - }.bind(null, style[1]), { is_radio_checked: style[1] === pref.styleVolumeBarFill }); - } - transportVolumeBarStyleMenu.append(transportVolumeBarStylesFillMenu); - cmac.append(transportVolumeBarStyleMenu); - } -}); - - -////////////////////////////// -// * SEEKBAR CONTEXT MENU * // -////////////////////////////// -/** - * Contains all seekbar ( progress bar, peakmeter bar and waveform bar ) top menu "Options" for quick access. - * Displayed when right clicking on the lower bar. - */ -Object.assign(qwr_utils, { - /** - * @param {ContextMenu} cmac The context menu object. - */ - append_seekbar_context_menu_to(cmac) { - const seekbar = [['Progress bar', 'progressbar'], ['Peakmeter bar', 'peakmeterbar'], ['Waveform bar', 'waveformbar']]; - for (const type of seekbar) { - cmac.append_item(type[0], () => { - pref.seekbar = type[1]; - setGeometry(); - setProgressBarRefresh(); - if (pref.seekbar === 'waveformbar') waveformBar.updateBar(); - RepaintWindow(); - }, { is_radio_checked: type[1] === pref.seekbar }); - } - - // * PROGRESS BAR * // - if (pref.seekbar === 'progressbar') { - cmac.append_separator(); - const progressBarStyleMenu = new ContextMenu('Style'); - const progressBarStyle = [['Default', 'default'], ['Rounded', 'rounded'], ['Lines', 'lines'], ['Blocks', 'blocks'], ['Dots', 'dots'], ['Thin', 'thin']]; - for (const sec of progressBarStyle) { - progressBarStyleMenu.append_item(sec[0], () => { - pref.styleProgressBarDesign = sec[1]; - setGeometry(); - RepaintWindow(); - }, { is_radio_checked: sec[1] === pref.styleProgressBarDesign }); - } - cmac.append(progressBarStyleMenu); - - const progressBarSeekSpeedMenu = new ContextMenu('Mouse wheel seek speed'); - const progressBarSeekSpeed = [[' 1 sec', 1], [' 2 sec', 2], [' 3 sec', 3], [' 4 sec', 4], [' 5 sec (default)', 5], [' 6 sec', 6], [' 7 sec', 7], [' 8 sec', 8], [' 9 sec', 9], ['10 sec', 10]]; - for (const sec of progressBarSeekSpeed) { - progressBarSeekSpeedMenu.append_item(sec[0], () => { - pref.progressBarWheelSeekSpeed = sec[1]; - }, { is_radio_checked: sec[1] === pref.progressBarWheelSeekSpeed }); - } - cmac.append(progressBarSeekSpeedMenu); - - const progressBarRefreshMenu = new ContextMenu('Refresh rate'); - const progressBarRefresh = [['1000 ms (very slow CPU)', 1000], [' 500 ms', 500], [' 333 ms', 333], [' Variable (default)', 'variable'], [' 250 ms', 250], [' 200 ms', 200], [' 150 ms', 150], [' 100 ms', 100], [' 60 ms', 60], [' 30 ms (very fast CPU)', 30]]; - for (const rate of progressBarRefresh) { - progressBarRefreshMenu.append_item(rate[0], () => { - pref.progressBarRefreshRate = rate[1]; - setProgressBarRefresh(); - }, { is_radio_checked: rate[1] === pref.progressBarRefreshRate }); - } - cmac.append(progressBarRefreshMenu); - } - // * PEAKMETER BAR * // - else if (pref.seekbar === 'peakmeterbar') { - cmac.append_separator(); - const peakmeterBarDesignMenu = new ContextMenu('Style'); - const peakmeterBarDesign = [['Horizontal', 'horizontal'], ['Horizontal center', 'horizontal_center'], ['Vertical', 'vertical']]; - for (const design of peakmeterBarDesign) { - peakmeterBarDesignMenu.append_item(design[0], () => { - pref.peakmeterBarDesign = design[1]; - peakmeterBar.on_size(ww, wh); - RepaintWindow(); - }, { is_radio_checked: design[1] === pref.peakmeterBarDesign }); - } - cmac.append(peakmeterBarDesignMenu); - - if (pref.peakmeterBarDesign === 'vertical') { - const peakmeterBarVertSizeMenu = new ContextMenu('Size'); - const peakmeterBarVertSize = [[' 0 px', 0], [' 2 px', 2], [' 4 px', 4], [' 6 px', 6], [' 8 px', 8], ['10 px', 10], [pref.layout !== 'default' ? '12 px (default)' : '12 px', 12], ['14 px', 14], ['16 px', 16], ['18 px', 18], [pref.layout !== 'default' ? '20 px' : '20 px (default)', 20], ['25 px', 25], ['30 px', 30], ['35 px', 35], ['40 px', 40], ['Minimum', 'min']]; - for (const size of peakmeterBarVertSize) { - peakmeterBarVertSizeMenu.append_item(size[0], () => { - pref.peakmeterBarVertSize = size[1]; - peakmeterBar = new PeakmeterBar(ww, wh); - RepaintWindow(); - }, { is_radio_checked: size[1] === pref.peakmeterBarVertSize }); - } - cmac.append(peakmeterBarVertSizeMenu); - - const peakmeterBarVertDbRangeMenu = new ContextMenu('Decibel range'); - const peakmeterBarVertDbRange = [['2 to -20 db (default)', 220], ['2 to -15 db', 215], ['2 to -10 db', 210], ['3 to -20 db', 320], ['3 to -15 db', 315], ['3 to -10 db', 310], ['5 to -20 db', 520], ['5 to -15 db', 515], ['5 to -10 db', 510]]; - for (const range of peakmeterBarVertDbRange) { - peakmeterBarVertDbRangeMenu.append_item(range[0], () => { - pref.peakmeterBarVertDbRange = range[1]; - peakmeterBar = new PeakmeterBar(ww, wh); - RepaintWindow(); - }, { is_radio_checked: range[1] === pref.peakmeterBarVertDbRange }); - } - cmac.append(peakmeterBarVertDbRangeMenu); - } - - const peakmeterBarDisplayMenu = new ContextMenu('Display'); - if (pref.peakmeterBarDesign === 'horizontal' || pref.peakmeterBarDesign === 'horizontal_center') { - peakmeterBarDisplayMenu.append_item('Show over bars', () => { - pref.peakmeterBarOverBars = !pref.peakmeterBarOverBars; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarOverBars }); - peakmeterBarDisplayMenu.append_separator(); - peakmeterBarDisplayMenu.append_item('Show outer bars', () => { - pref.peakmeterBarOuterBars = !pref.peakmeterBarOuterBars; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarOuterBars }); - peakmeterBarDisplayMenu.append_item('Show outer peaks', () => { - pref.peakmeterBarOuterPeaks = !pref.peakmeterBarOuterPeaks; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarOuterPeaks }); - peakmeterBarDisplayMenu.append_separator(); - peakmeterBarDisplayMenu.append_item('Show main bars', () => { - pref.peakmeterBarMainBars = !pref.peakmeterBarMainBars; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarMainBars }); - peakmeterBarDisplayMenu.append_item('Show main peaks', () => { - pref.peakmeterBarMainPeaks = !pref.peakmeterBarMainPeaks; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarMainPeaks }); - peakmeterBarDisplayMenu.append_separator(); - peakmeterBarDisplayMenu.append_item('Show middle bars', () => { - pref.peakmeterBarMiddleBars = !pref.peakmeterBarMiddleBars; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarMiddleBars }); - } - - peakmeterBarDisplayMenu.append_item('Show progress bar', () => { - pref.peakmeterBarProgBar = !pref.peakmeterBarProgBar; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarProgBar }); - - if (pref.peakmeterBarDesign === 'horizontal' || pref.peakmeterBarDesign === 'horizontal_center') { - peakmeterBarDisplayMenu.append_separator(); - peakmeterBarDisplayMenu.append_item('Show gaps', () => { - pref.peakmeterBarGaps = !pref.peakmeterBarGaps; - peakmeterBar.on_size(ww, wh); - RepaintWindow(); - }, { is_checked: pref.peakmeterBarGaps }); - peakmeterBarDisplayMenu.append_item('Show grid', () => { - pref.peakmeterBarGrid = !pref.peakmeterBarGrid; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarGrid }); - } - - if (pref.peakmeterBarDesign === 'vertical') { - peakmeterBarDisplayMenu.append_item('Show peaks', () => { - pref.peakmeterBarVertPeaks = !pref.peakmeterBarVertPeaks; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarVertPeaks }); - peakmeterBarDisplayMenu.append_item('Show baseline', () => { - pref.peakmeterBarVertBaseline = !pref.peakmeterBarVertBaseline; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarVertBaseline }); - } - - peakmeterBarDisplayMenu.append_item(pref.layout !== 'default' ? 'Show info (only available in Default layout)' : 'Show info', () => { - pref.peakmeterBarInfo = !pref.peakmeterBarInfo; - RepaintWindow(); - }, { is_checked: pref.peakmeterBarInfo }); - - cmac.append(peakmeterBarDisplayMenu); - - const peakmeterBarRefreshMenu = new ContextMenu('Refresh rate'); - const peakmeterBarRefresh = [['200 ms (very slow CPU)', 200], ['150 ms', 150], ['120 ms', 120], ['100 ms', 100], [' 80 ms (default)', 80], [' 60 ms', 60], [' 30 ms (very fast CPU)', 30]]; - for (const rate of peakmeterBarRefresh) { - peakmeterBarRefreshMenu.append_item(rate[0], () => { - pref.peakmeterBarRefreshRate = rate[1]; - setProgressBarRefresh(); - }, { is_radio_checked: rate[1] === pref.peakmeterBarRefreshRate }); - } - cmac.append(peakmeterBarRefreshMenu); - } - // * WAVEFORM BAR * // - else if (pref.seekbar === 'waveformbar') { - cmac.append_separator(); - const waveformBarAnalysisMenu = new ContextMenu('Analysis'); - const waveformBarAnalysis = [['RMS level', 'rms_level'], ['Peak level', 'peak_level'], ['RMS peak', 'rms_peak']]; - for (const type of waveformBarAnalysis) { - waveformBarAnalysisMenu.append_item(type[0] + (pref.waveformBarMode === 'ffprobe' ? '' : '\t (ffprobe only)'), () => { - pref.waveformBarAnalysis = type[1]; - waveformBar.updateConfig({ preset: { analysisMode: type[1] } }); - waveformBar.updateBar(); - RepaintWindow(); - }, { - is_grayed_out: pref.waveformBarMode !== 'ffprobe', - is_radio_checked: type[1] === pref.waveformBarAnalysis - } - ); - } - waveformBarAnalysisMenu.append_separator(); - waveformBarAnalysisMenu.append_item('Delete analysis files', () => { - const msg = 'Do you want to delete all waveform bar cache?\n\nThis will permanently delete analyzed files.\n\nContinue?\n\n\n'; - - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) DeleteWaveformBarCache(); - }); - }); - waveformBarAnalysisMenu.append_separator(); - waveformBarAnalysisMenu.append_item('Auto-delete analysis files', () => { - pref.waveformBarAutoDelete = !pref.waveformBarAutoDelete; - waveformBar.updateConfig({ analysis: { autoDelete: pref.waveformBarAutoDelete } }); - }, { is_grayed_out: pref.waveformBarMode === 'visualizer' }); - cmac.append(waveformBarAnalysisMenu); - - const waveformBarModeMenu = new ContextMenu('Mode'); - const waveformBarMode = [['FFprobe', 'ffprobe'], ['Audiowaveform', 'audiowaveform'], ['Visualizer', 'visualizer']]; - if (!IsFile(waveformBar.binaries.ffprobe)) { - waveformBarModeMenu.append_item('Download FFprobe', () => { - waveformBar.getFFprobe(); - }); - waveformBarModeMenu.append_separator(); - } - for (const mode of waveformBarMode) { - const found = IsFile(waveformBar.binaries[mode[1]]); - waveformBarModeMenu.append_item(mode[0] + (found ? '' : '\t(not found)'), () => { - pref.waveformBarMode = mode[1]; - waveformBar.updateConfig({ analysis: { binaryMode: pref.waveformBarMode } }); - waveformBar.updateBar(true); - RepaintWindow(); - }, { - is_grayed_out: !found, - is_radio_checked: mode[1] === pref.waveformBarMode - } - ); - } - cmac.append(waveformBarModeMenu); - - const waveformBarDesignMenu = new ContextMenu('Style'); - const waveformBarDesign = [['Waveform', 'waveform'], ['Bars', 'bars'], ['Dots', 'dots'], ['Halfbars', 'halfbars']]; - for (const design of waveformBarDesign) { - waveformBarDesignMenu.append_item(design[0], () => { - pref.waveformBarDesign = design[1]; - waveformBar.updateConfig({ preset: { barDesign: design[1] } }); - }, { is_radio_checked: design[1] === pref.waveformBarDesign }); - } - cmac.append(waveformBarDesignMenu); - - const waveformBarSizeMenu = new ContextMenu('Size'); - const waveformBarSizeWaveMenu = new ContextMenu('Waveform'); - const waveformBarSizeWave = [['1', 1], ['2', 2], ['3 (Default)', 3], ['4', 4], ['5', 5]]; - for (const size of waveformBarSizeWave) { - waveformBarSizeWaveMenu.append_item(size[0], () => { - pref.waveformBarSizeWave = size[1]; - waveformBar.updateConfig({ ui: { sizeWave: size[1] } }); - waveformBar.updateBar(); - RepaintWindow(); - }, { is_radio_checked: size[1] === pref.waveformBarSizeWave }); - } - waveformBarSizeMenu.append(waveformBarSizeWaveMenu); - const waveformBarSizeBarsMenu = new ContextMenu('Bars'); - const waveformBarSizeBars = [['1 (Default)', 1], ['2', 2], ['3', 3], ['4', 4], ['5', 5]]; - for (const size of waveformBarSizeBars) { - waveformBarSizeBarsMenu.append_item(size[0], () => { - pref.waveformBarSizeBars = size[1]; - waveformBar.updateConfig({ ui: { sizeBars: size[1] } }); - waveformBar.updateBar(); - RepaintWindow(); - }, { is_radio_checked: size[1] === pref.waveformBarSizeBars }); - } - waveformBarSizeMenu.append(waveformBarSizeBarsMenu); - const waveformBarSizeDotsMenu = new ContextMenu('Dots'); - const waveformBarSizeDots = [['1', 1], ['2 (Default)', 2], ['3', 3], ['4', 4], ['5', 5]]; - for (const size of waveformBarSizeDots) { - waveformBarSizeDotsMenu.append_item(size[0], () => { - pref.waveformBarSizeDots = size[1]; - waveformBar.updateConfig({ ui: { sizeDots: size[1] } }); - waveformBar.updateBar(); - RepaintWindow(); - }, { is_radio_checked: size[1] === pref.waveformBarSizeDots }); - } - waveformBarSizeMenu.append(waveformBarSizeDotsMenu); - const waveformBarSizeHalfMenu = new ContextMenu('Halfbars'); - const waveformBarSizeHalf = [['1', 1], ['2', 2], ['3', 3], ['4 (Default)', 4], ['5', 5]]; - for (const size of waveformBarSizeHalf) { - waveformBarSizeHalfMenu.append_item(size[0], () => { - pref.waveformBarSizeHalf = size[1]; - waveformBar.updateConfig({ ui: { sizeHalf: size[1] } }); - waveformBar.updateBar(); - RepaintWindow(); - }, { is_radio_checked: size[1] === pref.waveformBarSizeHalf }); - } - waveformBarSizeMenu.append(waveformBarSizeHalfMenu); - waveformBarSizeMenu.append_separator(); - waveformBarSizeMenu.append_item('Normalize width', () => { - pref.waveformBarSizeNormalize = !pref.waveformBarSizeNormalize; - waveformBar.updateConfig({ ui: { sizeNormalizeWidth: pref.waveformBarSizeNormalize } }); - waveformBar.updateBar(); - RepaintWindow(); - }, { is_checked: pref.waveformBarSizeNormalize }); - cmac.append(waveformBarSizeMenu); - - const waveformBarDisplayMenu = new ContextMenu('Display'); - const waveformBarDisplay = [['Full', 'full'], ['Partial', 'partial']]; - for (const paint of waveformBarDisplay) { - waveformBarDisplayMenu.append_item(paint[0], () => { - pref.waveformBarPaint = paint[1]; - waveformBar.updateConfig({ preset: { paintMode: paint[1] } }); - }, { is_radio_checked: paint[1] === pref.waveformBarPaint }); - } - waveformBarDisplayMenu.append_separator(); - - waveformBarDisplayMenu.append_item(`Prepaint${pref.waveformBarPaint === 'full' ? '\t(partial only)' : ''}`, () => { - pref.waveformBarPrepaint = !pref.waveformBarPrepaint; - waveformBar.updateConfig({ preset: { prepaint: pref.waveformBarPrepaint } }); - }, { - is_grayed_out: pref.waveformBarPaint === 'full', - is_checked: pref.waveformBarPrepaint - } - ); - - const waveformBarPrepaintMenuDisabled = pref.waveformBarPaint === 'full' || pref.waveformBarMode === 'visualizer' || !pref.waveformBarPrepaint; - const waveformBarPrepaintMenu = new ContextMenu('Prepaint front', { is_grayed_out: waveformBarPrepaintMenuDisabled }); - const waveformBarPrepaint = [[' 2 secs', 2], [' 5 secs', 5], ['10 secs', 10], [' Full', Infinity]]; - for (const time of waveformBarPrepaint) { - waveformBarPrepaintMenu.append_item(time[0], () => { - pref.waveformBarPrepaintFront = time[1]; - waveformBar.updateConfig({ preset: { prepaintFront: time[1] } }); - }, { - is_grayed_out: waveformBarPrepaintMenuDisabled, - is_radio_checked: time[1] === pref.waveformBarPrepaintFront - } - ); - } - waveformBarDisplayMenu.append(waveformBarPrepaintMenu); - waveformBarDisplayMenu.append_separator(); - - waveformBarDisplayMenu.append_item('Animate', () => { - pref.waveformBarAnimate = !pref.waveformBarAnimate; - waveformBar.updateConfig({ preset: { animate: pref.waveformBarAnimate } }); - }, { is_checked: pref.waveformBarAnimate }); - - waveformBarDisplayMenu.append_item(`Use BPM${pref.waveformBarPaint === 'full' && pref.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`, () => { - pref.waveformBarBPM = !pref.waveformBarBPM; - if (pref.waveformBarBPM) pref.waveformBarRefreshRateVar = true; - waveformBar.updateConfig({ - preset: { useBPM: pref.waveformBarBPM }, - ui: { refreshRateVar: pref.waveformBarRefreshRateVar } - }); - }, { - is_grayed_out: !(pref.waveformBarPaint === 'partial' && pref.waveformBarPrepaint || pref.waveformBarMode === 'visualizer'), - is_checked: pref.waveformBarBPM - } - ); - - waveformBarDisplayMenu.append_item('Invert halfbars', () => { - pref.waveformBarInvertHalfbars = !pref.waveformBarInvertHalfbars; - waveformBar.updateConfig({ preset: { invertHalfbars: pref.waveformBarInvertHalfbars } }); - }, { is_checked: pref.waveformBarInvertHalfbars }); - waveformBarDisplayMenu.append_separator(); - - waveformBarDisplayMenu.append_item('Show indicator', () => { - pref.waveformBarIndicator = !pref.waveformBarIndicator; - waveformBar.updateConfig({ preset: { indicator: pref.waveformBarIndicator } }); - }, { is_checked: pref.waveformBarIndicator }); - cmac.append(waveformBarDisplayMenu); - - const waveformBarRefreshMenuDisabled = !(pref.waveformBarPaint === 'partial' && pref.waveformBarPrepaint || pref.waveformBarMode === 'visualizer'); - const waveformBarRefreshMenu = new ContextMenu(`Refresh rate${pref.waveformBarPaint === 'full' && pref.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`, { is_grayed_out: waveformBarRefreshMenuDisabled }); - const waveformBarRefresh = [['1000 ms (very slow CPU)', 1000], [' 500 ms', 500], [' 200 ms', 200], [' 100 ms (default)', 100], [' 80 ms', 80], [' 60 ms', 60], [' 30 ms (very fast CPU)', 30]]; - for (const rate of waveformBarRefresh) { - waveformBarRefreshMenu.append_item(rate[0], () => { - pref.waveformBarRefreshRate = rate[1]; - waveformBar.updateConfig({ ui: { refreshRate: rate[1] } }); - }, { - is_grayed_out: waveformBarRefreshMenuDisabled, - is_radio_checked: rate[1] === pref.waveformBarRefreshRate - } - ); - } - waveformBarRefreshMenu.append_separator(); - waveformBarRefreshMenu.append_item(' Variable refresh rate', () => { - pref.waveformBarRefreshRateVar = !pref.waveformBarRefreshRateVar; - waveformBar.updateConfig({ ui: { refreshRateVar: pref.waveformBarRefreshRateVar } }); - }, { is_checked: pref.waveformBarRefreshRateVar }); - cmac.append(waveformBarRefreshMenu); - } - } -}); diff --git a/profile/georgia-reborn/scripts/Base/gr-defaults.js b/profile/georgia-reborn/scripts/Base/gr-defaults.js deleted file mode 100644 index fc567909..00000000 --- a/profile/georgia-reborn/scripts/Base/gr-defaults.js +++ /dev/null @@ -1,2570 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Defaults * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-11 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -//////////////////////////////////////// -// ! GEORGIA-REBORN-CONFIG DEFAULTS ! // -//////////////////////////////////////// -// * This file contains the various definitions, default values, and schemas for the objects that will be written to the configuration file. -// * Any value defined here will be written to the config ( although some of these objects will be modified in gr-settings.js with values that are not saved ). -// ! DO NOT EDIT: Editing these values will likely not provide you with the results you expect as they will probably not be stored in the configs. -// ! NOTE: If you wish to make changes to this, edit it in your georgia-reborn-config.jsonc and georgia-reborn-custom.jsonc file and NOT here. - - -////////////////////////// -// * TITLE FORMATTING * // -////////////////////////// -/** @type {*} Title formatting strings used throughout the UI */ -let tf = {}; -tf.album_subtitle = '%albumsubtitle%'; -tf.album_translation = '%albumtranslation%'; -tf.artist_country = '%artistcountry%'; -tf.artist = '$if3($meta(artist),%composer%,%performer%,%album artist%)'; -tf.date = '$if3(%original release date%,%originaldate%,%date%,%fy_upload_date%,)'; -tf.disc_subtitle = '%discsubtitle%'; -tf.disc = '$ifgreater(%totaldiscs%,1,CD %discnumber%/%totaldiscs%,)'; -tf.edition = '[$if2($if(%original release date%,$ifequal($year(%original release date%),$year(%date%),,$year(%date%) ))$if2(%edition%,\'release\'),$if(%originaldate%,$ifequal($year(%originaldate%),$year(%date%),,$year(%date%) ))$if2(%edition%,\'release\'))]'; -tf.last_played = '[$if2(%last_played_enhanced%,%last_played%)]'; -tf.lyrics = '[$if3(%synced lyrics%,%syncedlyrics%,%lyrics%,%lyric%,%unsyncedlyrics%,%unsynced lyrics%,)]'; -tf.original_artist = '[ \'(\'%original artist%\' cover)\']'; -tf.composer = '[\' -\' %composer% \' \']'; -tf.releaseCountry = '$replace($if3(%releasecountry%,%discogs_country%,),AF,XW)'; -tf.title = '%title%[ \'[\'%translation%\']\']'; -tf.tracknum = '[%tracknumber%.]'; -tf.vinyl_side = '%vinyl side%'; -tf.vinyl_tracknum = '%vinyl tracknumber%'; -tf.year = '[$year($if3(%original release date%,%originaldate%,%date%,%fy_upload_date%,))]'; -tf.playing_playlist = 'Do not change this value as it is handled by the theme itself'; - -/** @type {Object} Assigning the title formatting strings. */ -const defaultTitleFormatStrings = Object.assign({}, tf); - -/** @type {Object} Title formatting config name description. */ -const titleFormatComments = { - artist_country: 'Only used for displaying artist flags.', - date: 'The full date stored for the track', - lyrics: 'gr-lyrics.js will check these fields in order if no local lyrics file is found.', - releaseCountry: 'Releases tagged from Musicbrainz with a release country of AF (Afghanistan) are almost always whole world releases that have each country listed individually, so replace with \'XW\' (Worldwide) tag.', - title: 'Track title shown above the progress bar', - vinyl_side: 'Used for determining what side a song appears on for vinyl releases - i.e. song A1 has a %vinyl side% of "A"', - vinyl_tracknum: 'Used for determining the track number on vinyl releases - i.e. song A1 has %vinyl tracknumber% set to "1"', - year: 'Just the year portion of any stored date.' -}; - -/** @type {Object} Title formatting config header description. */ -const titleFormatSchema = new ConfigurationObjectSchema('title_format_strings', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* TITLE FORMATTING STRINGS: ' + - '* Used throughout the display. Do NOT change the key names or add new ones. ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////////////////// -// * ARTWORK IMAGE PATHS * // -///////////////////////////// -/** @type {array} Artwork image paths load order - add, change or re-order entries as needed. */ -const imgPathDefaults = [ - // * File names with formats - '$replace(%path%,%filename_ext%,)folder*', - '$replace(%path%,%filename_ext%,)cover*', - '$replace(%path%,%filename_ext%,)front*', - '$replace(%path%,%filename_ext%,)*.*', - - // * All folder images in parent directory - '$replace(%path%,%directoryname%\\%filename_ext%,)folder*', - '$replace(%path%,%directoryname%\\%filename_ext%,)cover*', - '$replace(%path%,%directoryname%\\%filename_ext%,)front*', - '$replace(%path%,%directoryname%\\%filename_ext%,)*.*', - - // * Artwork, Images, Scans in root directory ( 1 Disc ) - '$replace(%path%\\..\\Artwork\\,%filename_ext%,)folder*', - '$replace(%path%\\..\\Artwork\\,%filename_ext%,)cover*', - '$replace(%path%\\..\\Artwork\\,%filename_ext%,)front*', - '$replace(%path%\\..\\Artwork\\,%filename_ext%,)*.*', - '$replace(%path%\\..\\Images\\,%filename_ext%,)folder*', - '$replace(%path%\\..\\Images\\,%filename_ext%,)cover*', - '$replace(%path%\\..\\Images\\,%filename_ext%,)front*', - '$replace(%path%\\..\\Images\\,%filename_ext%,)*.*', - '$replace(%path%\\..\\Scans\\,%filename_ext%,)folder*', - '$replace(%path%\\..\\Scans\\,%filename_ext%,)cover*', - '$replace(%path%\\..\\Scans\\,%filename_ext%,)front*', - '$replace(%path%\\..\\Scans\\,%filename_ext%,)*.*', - - // * Artwork, Images, Scans in other subfolders ( Multi Discs ) - '$replace(%path%\\Artwork\\,%filename_ext%,)folder*', - '$replace(%path%\\Artwork\\,%filename_ext%,)cover*', - '$replace(%path%\\Artwork\\,%filename_ext%,)front*', - '$replace(%path%\\Artwork\\,%filename_ext%,)*.*', - '$replace(%path%\\Images\\,%filename_ext%,)folder*', - '$replace(%path%\\Images\\,%filename_ext%,)cover*', - '$replace(%path%\\Images\\,%filename_ext%,)front*', - '$replace(%path%\\Images\\,%filename_ext%,)*.*', - '$replace(%path%\\Scans\\,%filename_ext%,)folder*', - '$replace(%path%\\Scans\\,%filename_ext%,)cover*', - '$replace(%path%\\Scans\\,%filename_ext%,)front*', - '$replace(%path%\\Scans\\,%filename_ext%,)*.*' -]; - -/** @type {Object} Artwork image paths config header description. */ -const imgPathSchema = new ConfigurationObjectSchema('imgPaths', ConfigurationObjectType.Array, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* ARTWORK IMAGE PATHS: ' + - '* The title formatting defined paths for artwork to be displayed. The first image matched will be shown first. ' + - '* Re-arrange, add, or remove as needed. Folder delimiters must be double-slashes. ' + - '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -/////////////// -// * THEME * // -/////////////// -/** @type {Object} Options > Theme settings with default value. */ -const themeDefaults = { - theme: 'reborn', - theme_day: 'white', - theme_night: 'black' -}; - -/** @type {Object} Options > Theme settings config name description. */ -const themesComments = { - theme: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold", "custom01-custom10" - Options > Theme', - theme_day: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold", "custom01-custom10" - Options > Theme - daytime theme', - theme_night: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold", "custom01-custom10" - Options > Theme - nighttime theme' -}; - -/** @type {Object} Options > Theme settings config header description. */ -const themesSchema = new ConfigurationObjectSchema('theme', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* THEME: ' + - '* Top menu Options > Theme ' + - '* You can set and select between the 12 available themes that will be used. ' + - '* If you choose to enable harmonic mode, your selected theme will be overriden once harmonic mode is deactivated again. ' + - '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -/////////////// -// * STYLE * // -/////////////// -/** @type {Object} Options > Style settings with default values */ -const stylesDefaults = { - default: true, - nighttime: false, - bevel: false, - blend: false, - blend2: false, - gradient: false, - gradient2: false, - alternative: false, - alternative2: false, - blackAndWhite: false, - blackAndWhite2: false, - blackAndWhiteReborn: false, - blackReborn: false, - rebornWhite: false, - rebornBlack: false, - rebornFusion: false, - rebornFusion2: false, - rebornFusionAccent: false, - randomPastel: false, - randomDark: false, - randomAutoColor: 'off', - topMenuButtons: 'default', - transportButtons: 'default', - progressBarDesign: 'default', - progressBar: 'default', - progressBarFill: 'default', - volumeBarDesign: 'default', - volumeBar: 'default', - volumeBarFill: 'default', - nighttime_day: false, - bevel_day: false, - blend_day: false, - blend2_day: false, - gradient_day: false, - gradient2_day: false, - alternative_day: false, - alternative2_day: false, - blackAndWhite_day: false, - blackAndWhite2_day: false, - blackAndWhiteReborn_day: false, - blackReborn_day: false, - rebornWhite_day: false, - rebornBlack_day: false, - rebornFusion_day: false, - rebornFusion2_day: false, - rebornFusionAccent_day: false, - randomPastel_day: false, - randomDark_day: false, - randomAutoColor_day: 'off', - topMenuButtons_day: 'default', - transportButtons_day: 'default', - progressBarDesign_day: 'default', - progressBar_day: 'default', - progressBarFill_day: 'default', - volumeBarDesign_day: 'default', - volumeBar_day: 'default', - volumeBarFill_day: 'default', - nighttime_night: false, - bevel_night: false, - blend_night: false, - blend2_night: false, - gradient_night: false, - gradient2_night: false, - alternative_night: false, - alternative2_night: false, - blackAndWhite_night: false, - blackAndWhite2_night: false, - blackAndWhiteReborn_night: false, - blackReborn_night: false, - rebornWhite_night: false, - rebornBlack_night: false, - rebornFusion_night: false, - rebornFusion2_night: false, - rebornFusionAccent_night: false, - randomPastel_night: false, - randomDark_night: false, - randomAutoColor_night: 'off', - topMenuButtons_night: 'default', - transportButtons_night: 'default', - progressBarDesign_night: 'default', - progressBar_night: 'default', - progressBarFill_night: 'default', - volumeBarDesign_night: 'default', - volumeBar_night: 'default', - volumeBarFill_night: 'default' -}; - -/** @type {Object} Options > Style settings config name description. */ -const stylesComments = { - default: 'Values: true, false - can be used in all themes', - nighttime: 'Values: true, false - special style can only be used with reborn, random, custom theme', - bevel: 'Values: true, false - can be used in all themes', - blend: 'Values: true, false - can be used in all themes', - blend2: 'Values: true, false - can be used in all themes', - gradient: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', - gradient2: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', - alternative: 'Values: true, false - can be used in all themes but not with special styles', - alternative2: 'Values: true, false - can be used in all themes but not with special styles', - blackAndWhite: 'Values: true, false - special white style can only be used with white theme', - blackAndWhite2: 'Values: true, false - special white style can only be used with white theme', - blackAndWhiteReborn: 'Values: true, false - special white style can only be used with white theme', - blackReborn: 'Values: true, false - special black style can only be used with black theme', - rebornWhite: 'Values: true, false - special reborn style can only be used with reborn theme', - rebornBlack: 'Values: true, false - special reborn style can only be used with reborn theme', - rebornFusion: 'Values: true, false - special reborn style can only be used with reborn theme', - rebornFusion2: 'Values: true, false - special reborn style can only be used with reborn theme', - rebornFusionAccent: 'Values: true, false - special reborn style can only be used with reborn theme', - randomPastel: 'Values: true, false - special random style can only be used with random theme', - randomDark: 'Values: true, false - special random style can only be used with random theme', - randomAutoColor: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track" - can only be used with random theme', - topMenuButtons: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal"', - transportButtons: 'Values: "default", "bevel", "inner", "emboss", "minimal"', - progressBarDesign: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin"', - progressBar: 'Values: "default", "bevel", "inner"', - progressBarFill: 'Values: "default", "bevel", "inner", "blend"', - volumeBarDesign: 'Values: "default", "rounded"', - volumeBar: 'Values: "default", "bevel", "inner"', - volumeBarFill: 'Values: "default", "bevel", "inner"', - nighttime_day: 'Values: true, false - daytime theme - special style can only be used with reborn, random, custom theme', - bevel_day: 'Values: true, false - daytime theme - can be used in all themes', - blend_day: 'Values: true, false - daytime theme - can be used in all themes', - blend2_day: 'Values: true, false - daytime theme - can be used in all themes', - gradient_day: 'Values: true, false - daytime theme - can only be used with reborn, random, blue, darkblue, red theme', - gradient2_day: 'Values: true, false - daytime theme - can only be used with reborn, random, blue, darkblue, red theme', - alternative_day: 'Values: true, false - daytime theme - can be used in all themes but not with special styles', - alternative2_day: 'Values: true, false - daytime theme - can be used in all themes but not with special styles', - blackAndWhite_day: 'Values: true, false - daytime theme - special white style can only be used with white theme', - blackAndWhite2_day: 'Values: true, false - daytime theme - special white style can only be used with white theme', - blackAndWhiteReborn_day: 'Values: true, false - daytime theme - special white style can only be used with white theme', - blackReborn_day: 'Values: true, false - daytime theme - special black style can only be used with black theme', - rebornWhite_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', - rebornBlack_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', - rebornFusion_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', - rebornFusion2_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', - rebornFusionAccent_day: 'Values: true, false - daytime theme - special reborn style can only be used with reborn theme', - randomPastel_day: 'Values: true, false - daytime theme - special random style can only be used with random theme', - randomDark_day: 'Values: true, false - daytime theme - special random style can only be used with random theme', - randomAutoColor_day: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track" - daytime theme - can only be used with random theme', - topMenuButtons_day: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal" - daytime theme', - transportButtons_day: 'Values: "default", "bevel", "inner", "emboss", "minimal" - daytime theme', - progressBarDesign_day: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin" - daytime theme', - progressBar_day: 'Values: "default", "bevel", "inner" - daytime theme', - progressBarFill_day: 'Values: "default", "bevel", "inner", "blend" - daytime theme', - volumeBarDesign_day: 'Values: "default", "rounded" - daytime theme', - volumeBar_day: 'Values: "default", "bevel", "inner" - daytime theme', - volumeBarFill_day: 'Values: "default", "bevel", "inner" - daytime theme', - nighttime_night: 'Values: true, false - nighttime theme - special style can only be used with reborn, random, custom theme', - bevel_night: 'Values: true, false - nighttime theme - can be used in all themes', - blend_night: 'Values: true, false - nighttime theme - can be used in all themes', - blend2_night: 'Values: true, false - nighttime theme - can be used in all themes', - gradient_night: 'Values: true, false - nighttime theme - can only be used with reborn, random, blue, darkblue, red theme', - gradient2_night: 'Values: true, false - nighttime theme - can only be used with reborn, random, blue, darkblue, red theme', - alternative_night: 'Values: true, false - nighttime theme - can be used in all themes but not with special styles', - alternative2_night: 'Values: true, false - nighttime theme - can be used in all themes but not with special styles', - blackAndWhite_night: 'Values: true, false - nighttime theme - special white style can only be used with white theme', - blackAndWhite2_night: 'Values: true, false - nighttime theme - special white style can only be used with white theme', - blackAndWhiteReborn_night: 'Values: true, false - nighttime theme - special white style can only be used with white theme', - blackReborn_night: 'Values: true, false - nighttime theme - special black style can only be used with black theme', - rebornWhite_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', - rebornBlack_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', - rebornFusion_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', - rebornFusion2_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', - rebornFusionAccent_night: 'Values: true, false - nighttime theme - special reborn style can only be used with reborn theme', - randomPastel_night: 'Values: true, false - nighttime theme - special random style can only be used with random theme', - randomDark_night: 'Values: true, false - nighttime theme - special random style can only be used with random theme', - randomAutoColor_night: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track" - nighttime theme - can only be used with random theme', - topMenuButtons_night: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal" - nighttime theme', - transportButtons_night: 'Values: "default", "bevel", "inner", "emboss", "minimal" - nighttime theme', - progressBarDesign_night: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin" - nighttime theme', - progressBar_night: 'Values: "default", "bevel", "inner" - nighttime theme', - progressBarFill_night: 'Values: "default", "bevel", "inner", "blend" - nighttime theme', - volumeBarDesign_night: 'Values: "default", "rounded" - nighttime theme', - volumeBar_night: 'Values: "default", "bevel", "inner" - nighttime theme', - volumeBarFill_night: 'Values: "default", "bevel", "inner" - nighttime theme' -}; - -/** @type {Object} Options > Style settings config header description. */ -const stylesSchema = new ConfigurationObjectSchema('style', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* STYLES: ' + - '* Top menu Options > Style ' + - '* If you set a style, you need to set default: false, ' + - '* Basic style "gradient" and "gradient2" is only supported and can be used with reborn, random, blue, darkblue, red theme. ' + - '* Basic style "alternative" and "alternative2" can not be used with special styles except "nighttime". ' + - '* Special style "nighttime" can only be used with "reborn", "random", "custom" theme. ' + - '* Special style "blackAndWhite", "blackAndWhite2" and "blackAndWhiteReborn" can only be used with "white" theme. ' + - '* Special style "blackReborn" can only be used with "black" theme. ' + - '* Special style "rebornWhite", "rebornBlack", "rebornFusion", "rebornFusion2" and "rebornFusionAccent" can only be used with "reborn" theme. ' + - '* Special style "randomPastel", "randomDark" and "randomAutoColor" can only be used with "random" theme. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////// -// * PRESET * // -//////////////// -/** @type {Object} Options > Preset settings with default values. */ -const presetDefaults = { - selectMode: 'default', - selectWhitePresets: true, - selectBlackPresets: true, - selectRebornPresets: true, - selectRandomPresets: true, - selectBluePresets: true, - selectDarkbluePresets: true, - selectRedPresets: true, - selectCreamPresets: true, - selectNbluePresets: true, - selectNgreenPresets: true, - selectNredPresets: true, - selectNgoldPresets: true, - selectCustomPresets: true, - autoRandomMode: 'dblclick', - indicator: true -}; - -/** @type {Object} Options > Preset settings config name description. */ -const presetComments = { - selectMode: 'Values: "default", "harmonic", "theme" - Options > Preset > Select mode', - selectWhitePresets: 'Values: true, false - Options > Preset > Select presets > White', - selectBlackPresets: 'Values: true, false - Options > Preset > Select presets > Black', - selectRebornPresets: 'Values: true, false - Options > Preset > Select presets > Reborn', - selectRandomPresets: 'Values: true, false - Options > Preset > Select presets > Random', - selectBluePresets: 'Values: true, false - Options > Preset > Select presets > Blue', - selectDarkbluePresets: 'Values: true, false - Options > Preset > Select presets > Dark blue', - selectRedPresets: 'Values: true, false - Options > Preset > Select presets > Red', - selectCreamPresets: 'Values: true, false - Options > Preset > Select presets > Cream', - selectNbluePresets: 'Values: true, false - Options > Preset > Select presets > Neon blue', - selectNgreenPresets: 'Values: true, false - Options > Preset > Select presets > Neon green', - selectNredPresets: 'Values: true, false - Options > Preset > Select presets > Neon red', - selectNgoldPresets: 'Values: true, false - Options > Preset > Select presets > Neon gold', - selectCustomPresets: 'Values: true, false - Options > Preset > Select presets > Custom theme', - autoRandomMode: 'Values: "off", 5000, 10000, 15000, 30000, 60000, 300000, 600000, 900000, 1800000, 3600000, "track", "album", "dblclick" - Options > Preset > Auto random', - indicator: 'Values: true, false - Options > Preset > Indicator' -}; - -/** @type {Object} Options > Preset settings config header description. */ -const presetSchema = new ConfigurationObjectSchema('preset', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* PRESET: ' + - '* Top menu Options > Preset ' + - '* You can set the preset select mode and preset auto-random mode and deactivate theme presets to be excluded in the preset selection when preset select mode or preset auto-random mode is being used. ' + - '* If you choose to change the preset select mode, your active theme will be overriden once preset select mode is not set to "default". ' + - '* This behavior also applies to the preset auto-random mode when "dblclick" has not been set. ' + - '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////////// -// * PLAYER SIZE * // -///////////////////// -/** @type {Object} Options > Player size settings with default value. */ -const themePlayerSizeDefaults = { - playerSize: 'small', - savedWidth_default: 1140, - savedHeight_default: 730, - savedWidth_artwork: 526, - savedHeight_artwork: 686, - savedWidth_compact: 484, - savedHeight_compact: 730 -}; - -/** @type {Object} Options > Player size settings config name description. */ -const themePlayerSizeComments = { - playerSize: 'Values: "small", "normal", "large" - Options > Player size', - savedWidth_default: 'The saved player width for the Default layout - Options > Layout > Default', - savedHeight_default: 'The saved player height for the Default layout - Options > Layout > Default', - savedWidth_artwork: 'The saved player width for the Artwork layout - Options > Layout > Artwork', - savedHeight_artwork: 'The saved player height for the Artwork layout - Options > Layout > Artwork', - savedWidth_compact: 'The saved player width for the Compact layout - Options > Layout > Compact', - savedHeight_compact: 'The saved player height for the Compact layout - Options > Layout > Compact' -}; - -/** @type {Object} Options > Player size settings config header description. */ -const themePlayerSizeSchema = new ConfigurationObjectSchema('themePlayerSize', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* PLAYER SIZE: ' + - '* Top menu Options > Player size ' + - '* You can set and select between the 3 available player sizes that will be used. ' + - '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////// -// * LAYOUT * // -//////////////// -/** @type {Object} Options > Layout settings with default value. */ -const themeLayoutDefaults = { - layout: 'default' -}; - -/** @type {Object} Options > Layout settings config name description. */ -const themeLayoutComments = { - layout: 'Values: "default", "artwork", "compact" - Options > Layout' -}; - -/** @type {Object} Options > Layout settings config header description. */ -const themeLayoutSchema = new ConfigurationObjectSchema('themeLayout', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* LAYOUT: ' + - '* Top menu Options > Layout ' + - '* You can set and select between the 3 available layouts that will be used. ' + - '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////// -// * DISPLAY * // -///////////////// -/** @type {Object} Options > Display settings with default value. */ -const themeDisplayDefaults = { - resolution: 'HD' -}; - -/** @type {Object} Options > Display settings config name description. */ -const themeDisplayComments = { - resolution: 'Values: "4K", "QHD", "HD" - Options > Display' -}; - -/** @type {Object} Options > Display settings config header description. */ -const themeDisplaySchema = new ConfigurationObjectSchema('themeDisplay', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* DISPLAY: ' + - '* Top menu Options > Display ' + - '* You can set and select between the 3 available resolution that will be used. ' + - '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////////// -// * BRIGHTNESS * // -//////////////////// -/** @type {Object} Options > Brightness settings with default value. */ -const themeBrightnessDefaults = { - themeBrightness: 'default', - themeBrightness_day: 'default', - themeBrightness_night: 'default' -}; - -/** @type {Object} Options > Brightness settings config name description. */ -const themeBrightnessComments = { - themeBrightness: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50 - Options > Brightness', - themeBrightness_day: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50 - Options > Brightness - daytime theme', - themeBrightness_night: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50 - Options > Brightness - nighttime theme' -}; - -/** @type {Object} Options > Brightness settings config header description. */ -const themeBrightnessSchema = new ConfigurationObjectSchema('themeBrightness', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* THEME BRIGHTNESS: ' + - '* Top menu Options > Brightness ' + - '* You can set and select between the 11 available brightness settings that will be used. ' + - '* Note: This setting will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -/////////////////// -// * FONT SIZE * // -/////////////////// -/** @type {Object} Options > Font size settings with default values. */ -const themeFontSizesDefaults = { - menuFontSize_default: RES_QHD ? 14 : 12, - menuFontSize_artwork: RES_QHD ? 14 : 12, - menuFontSize_compact: RES_QHD ? 14 : 12, - lowerBarFontSize_default: RES_QHD ? 20 : 18, - lowerBarFontSize_artwork: RES_QHD ? 18 : 16, - lowerBarFontSize_compact: RES_QHD ? 18 : 16, - notificationFontSize_default: RES_QHD ? 20 : 18, - notificationFontSize_artwork: RES_QHD ? 18 : 16, - notificationFontSize_compact: RES_QHD ? 18 : 16, - popupFontSize_default: RES_QHD ? 18 : 16, - popupFontSize_artwork: RES_QHD ? 16 : 14, - popupFontSize_compact: RES_QHD ? 16 : 14, - tooltipFontSize_default: RES_QHD ? 18 : 16, - tooltipFontSize_artwork: RES_QHD ? 16 : 14, - tooltipFontSize_compact: RES_QHD ? 16 : 14, - gridArtistFontSize_default: RES_QHD ? 20 : 18, - gridArtistFontSize_artwork: RES_QHD ? 20 : 18, - gridTrackNumFontSize_default: RES_QHD ? 20 : 18, - gridTrackNumFontSize_artwork: RES_QHD ? 20 : 18, - gridTitleFontSize_default: RES_QHD ? 20 : 18, - gridTitleFontSize_artwork: RES_QHD ? 20 : 18, - gridAlbumFontSize_default: RES_QHD ? 20 : 18, - gridAlbumFontSize_artwork: RES_QHD ? 20 : 18, - gridKeyFontSize_default: RES_QHD ? 19 : 17, - gridKeyFontSize_artwork: RES_QHD ? 19 : 17, - gridValueFontSize_default: RES_QHD ? 19 : 17, - gridValueFontSize_artwork: RES_QHD ? 19 : 17, - playlistHeaderFontSize_default: RES_QHD ? 17 : 15, - playlistHeaderFontSize_artwork: RES_QHD ? 17 : 15, - playlistHeaderFontSize_compact: RES_QHD ? 17 : 15, - playlistFontSize_default: RES_QHD ? 14 : 12, - playlistFontSize_artwork: RES_QHD ? 14 : 12, - playlistFontSize_compact: RES_QHD ? 14 : 12, - libraryFontSize_default: RES_4K ? 24 : RES_QHD ? 14 : 12, - libraryFontSize_artwork: RES_4K ? 24 : RES_QHD ? 14 : 12, - biographyFontSize_default: RES_4K ? 24 : RES_QHD ? 14 : 12, - biographyFontSize_artwork: RES_4K ? 24 : RES_QHD ? 14 : 12, - lyricsFontSize_default: RES_QHD ? 22 : 20, - lyricsFontSize_artwork: RES_QHD ? 22 : 20 -}; - -/** @type {Object} Options > Font size settings config name description. */ -const themeFontSizesComments = { - menuFontSize_default: 'Values: 8, 10, 11, 12, 13, 14, 16 - Options > Font size > Main > Top menu - when Default layout is active', - menuFontSize_artwork: 'Values: 8, 10, 11, 12, 13, 14, 16 - Options > Font size > Main > Top menu - when Artwork layout is active', - menuFontSize_compact: 'Values: 8, 10, 11, 12, 13, 14, 16 - Options > Font size > Main > Top menu - when Compact layout is active', - lowerBarFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26 - Options > Font size > Main > Lower bar - when Default layout is active', - lowerBarFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26 - Options > Font size > Main > Lower bar - when Artwork layout is active', - lowerBarFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26 - Options > Font size > Main > Lower bar - when Compact layout is active', - notificationFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Notification - when Default layout is active', - notificationFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Notification - when Artwork layout is active', - notificationFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Notification - when Compact layout is active', - popupFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Popup - when Default layout is active', - popupFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Popup - when Artwork layout is active', - popupFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Popup - when Compact layout is active', - tooltipFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Tooltip - when Default layout is active', - tooltipFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Tooltip - when Artwork layout is active', - tooltipFontSize_compact: 'Values: 10, 12, 14, 16, 18, 20, 22, 24 - Options > Font size > Main > Tooltip - when Compact layout is active', - gridArtistFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Artist - when Default layout is active', - gridArtistFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Artist - when Artwork layout is active', - gridTrackNumFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - not in Options, track number before song title in grid - when Default layout is active', - gridTrackNumFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - not in Options, track number before song title in grid - when Artwork layout is active', - gridTitleFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Title - when Default layout is active', - gridTitleFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Title - when Artwork layout is active', - gridAlbumFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Album - when Default layout is active', - gridAlbumFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Album - when Artwork layout is active', - gridKeyFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag name - when Default layout is active', - gridKeyFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag name - when Artwork layout is active', - gridValueFontSize_default: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag value - when Default layout is active', - gridValueFontSize_artwork: 'Values: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24 - Options > Font size > Details > Tag value - when Artwork layout is active', - playlistHeaderFontSize_default: 'Values: 10, 12, 13, 14, 15, 16, 17, 18, 20, 22 - Options > Font size > Playlist - when Default layout is active', - playlistHeaderFontSize_artwork: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Artwork layout is active', - playlistHeaderFontSize_compact: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Compact layout is active', - playlistFontSize_default: 'Values: 10, 12, 13, 14, 15, 16, 17, 18, 20, 22 - Options > Font size > Playlist - when Default layout is active', - playlistFontSize_artwork: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Artwork layout is active', - playlistFontSize_compact: 'Values: 10, 12, 13, 14, 15, 16, 17, 18 - Options > Font size > Playlist - when Compact layout is active', - libraryFontSize_default: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Library - when Default layout is active', - libraryFontSize_artwork: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Library - when Artwork layout is active', - biographyFontSize_default: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Biography - when Default layout is active', - biographyFontSize_artwork: 'Values: 8, 10, 11, 12, 13, 14, 16, 18 - Options > Font size > Biography - when Artwork layout is active', - lyricsFontSize_default: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 - Options > Font size > Lyrics - when Default layout is active', - lyricsFontSize_artwork: 'Values: 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 - Options > Font size > Lyrics - when Artwork layout is active' -}; - -/** @type {Object} Options > Font size settings config header description. */ -const themeFontSizesSchema = new ConfigurationObjectSchema('themeFontSize', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* FONT SIZES: ' + - '* Top menu Options > Font size ' + - '* Default, Artwork and Compact font sizes can be independently customized and will be used when changing between layouts. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////////////// -// * PLAYER CONTROLS * // -///////////////////////// -/** @type {Object} Options > Player controls settings with default values. */ -const themePlayerControlsDefaults = { - showPanelDetails_default: true, - showPanelDetails_artwork: true, - showPanelLibrary_default: true, - showPanelLibrary_artwork: true, - showPanelBiography_default: true, - showPanelBiography_artwork: true, - showPanelLyrics_default: true, - showPanelLyrics_artwork: true, - showPanelRating_default: true, - showPanelRating_artwork: true, - topMenuAlignment: 'center', - topMenuCompact: true, - albumArtAlign: 'right', - albumArtBg: 'left', - albumArtScale: 'cropped', - albumArtAspectRatioLimit: 1.5, - cycleArt: false, - cycleArtMWheel: true, - loadEmbeddedAlbumArtFirst: false, - showHiResAudioBadge: false, - hiResAudioBadgeRound: false, - hiResAudioBadgeSize: 'normal', - hiResAudioBadgePos: 'bottomright', - showPause: true, - jumpSearchIncludeLibrary: true, - jumpSearchIncludePlaylist: true, - jumpSearchComposerOnly: false, - playlistWheelScrollSteps: 3, - playlistWheelScrollDuration: 300, - playlistAutoScrollNowPlaying: false, - playlistAutoHideScrollbar: true, - playlistSmoothScrolling: true, - scrollStepLib: 3, - durationScrollLib: 500, - libraryAutoScrollNowPlaying: false, - libraryAutoHideScrollbar: true, - smoothLib: true, - scrollStepBio: 3, - durationScrollBio: 500, - biographyAutoHideScrollbar: true, - smoothBio: true, - showTooltipTruncated: true, - showTooltipTimeline: true, - showTooltipVolume: false, - showTooltipVolumeInPercent: false, - showTooltipMain: false, - showTooltipLibrary: false, - showTooltipBiography: false, - showStyledTooltips: true, - panelWidthAuto: false, - showPanelOnStartup: 'playlist', - showPreloaderLogo: true, - returnToHomeOnPlaybackStop: true, - hideMiddlePanelShadow: false, - lockPlayerSize: false, - maximizeToFullscreen: true, - switchPlaybackTime: false, - transportButtonSize_default: 32, - transportButtonSize_artwork: 32, - transportButtonSize_compact: 32, - transportButtonSpacing_default: 5, - transportButtonSpacing_artwork: 5, - transportButtonSpacing_compact: 5, - showTransportControls_default: true, - showTransportControls_artwork: true, - showTransportControls_compact: true, - showPlaybackOrderBtn_default: true, - showPlaybackOrderBtn_artwork: true, - showPlaybackOrderBtn_compact: true, - showReloadBtn_default: false, - showReloadBtn_artwork: false, - showReloadBtn_compact: false, - showVolumeBtn_default: true, - showVolumeBtn_artwork: true, - showVolumeBtn_compact: true, - autoHideVolumeBar: true, - showPlaybackTime_default: true, - showPlaybackTime_artwork: true, - showPlaybackTime_compact: true, - showLowerBarArtist_default: true, - showLowerBarArtist_artwork: true, - showLowerBarArtist_compact: true, - showLowerBarTrackNum_default: true, - showLowerBarTrackNum_artwork: true, - showLowerBarTrackNum_compact: true, - showLowerBarTitle_default: true, - showLowerBarTitle_artwork: true, - showLowerBarTitle_compact: true, - showLowerBarComposer_default: false, - showLowerBarComposer_artwork: false, - showLowerBarComposer_compact: false, - showLowerBarArtistFlags_default: true, - showLowerBarArtistFlags_artwork: true, - showLowerBarArtistFlags_compact: true, - showLowerBarVersion_default: true, - showLowerBarVersion_artwork: true, - showLowerBarVersion_compact: true, - showProgressBar_default: true, - showProgressBar_artwork: true, - showProgressBar_compact: true, - showPeakmeterBar_default: true, - showPeakmeterBar_artwork: true, - showPeakmeterBar_compact: true, - showWaveformBar_default: true, - showWaveformBar_artwork: true, - showWaveformBar_compact: true, - seekbar: 'progressbar', - progressBarWheelSeekSpeed: 5, - progressBarRefreshRate: 'variable', - peakmeterBarDesign: 'horizontal', - peakmeterBarVertSize: 20, - peakmeterBarVertDbRange: 220, - peakmeterBarOverBars: true, - peakmeterBarOuterBars: true, - peakmeterBarOuterPeaks: true, - peakmeterBarMainBars: true, - peakmeterBarMainPeaks: true, - peakmeterBarMiddleBars: true, - peakmeterBarProgBar: true, - peakmeterBarGaps: false, - peakmeterBarGrid: false, - peakmeterBarInfo: false, - peakmeterBarVertPeaks: true, - peakmeterBarVertBaseline: true, - peakmeterBarRefreshRate: 80, - waveformBarMode: 'audiowaveform', - waveformBarAnalysis: 'rms_level', - waveformBarDesign: 'halfbars', - waveformBarSizeWave: 3, - waveformBarSizeBars: 1, - waveformBarSizeDots: 2, - waveformBarSizeHalf: 4, - waveformBarSizeNormalize: false, - waveformBarPaint: 'partial', - waveformBarPrepaint: true, - waveformBarPrepaintFront: 'Infinity', - waveformBarAnimate: true, - waveformBarBPM: true, - waveformBarInvertHalfbars: true, - waveformBarIndicator: false, - waveformBarRefreshRate: 200, - waveformBarRefreshRateVar: false, - waveformBarAutoDelete: false, - playbackOrder: 'default' -}; - -/** @type {Object} Options > Player controls settings config name description. */ -const themePlayerControlsComments = { - showPanelDetails_default: 'Values: true, false - Options > Player controls > Top menu > Default > Details', - showPanelDetails_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Details', - showPanelLibrary_default: 'Values: true, false - Options > Player controls > Top menu > Default > Library', - showPanelLibrary_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Library', - showPanelBiography_default: 'Values: true, false - Options > Player controls > Top menu > Default > Biography', - showPanelBiography_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Biography', - showPanelLyrics_default: 'Values: true, false - Options > Player controls > Top menu > Default > Lyrics', - showPanelLyrics_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Lyrics', - showPanelRating_default: 'Values: true, false - Options > Player controls > Top menu > Default > Rating', - showPanelRating_artwork: 'Values: true, false - Options > Player controls > Top menu > Artwork > Rating', - topMenuAlignment: 'Values: left, center - Options > Player controls > Top menu', - topMenuCompact: 'Values: true, false - Options > Player controls > Top menu > Compact top menu', - albumArtAlign: 'Values: "left", "leftMargin", "center", "right" - Options > Player controls > Album art > When player size is not proportional', - albumArtBg: 'Values: "left", "full", "none" - Options > Player controls > Album art > When player size is not proportional', - albumArtScale: 'Values: "cropped", "stretched", "proportional" - Options > Player controls > Album art > When player size is maximized/fullscreen', - albumArtAspectRatioLimit: 'Values: 1, 1.25, 1.5, 1.75, 2 - Options > Player controls > Album art > When player size is maximized/fullscreen > Keep wide and tall artworks proportional', - cycleArt: 'Values: min: 5, max: 120 in seconds - Options > Player controls > Album art > Cycle album artwork', - cycleArtMWheel: 'Values: true, false - Options > Player controls > Album art > Cycle album artwork with mouse wheel', - loadEmbeddedAlbumArtFirst: 'Values: true, false - Options > Player controls > Album art > Load embedded album art first', - showHiResAudioBadge: 'Values: true, false - Options > Player controls > Album art > Show hi-res audio badge on album cover > Enabled', - hiResAudioBadgeRound: 'Values: true, false - Options > Player controls > Album art > Show hi-res audio badge on album cover > Round', - hiResAudioBadgeSize: 'Values: "small", "normal", "large" - Options > Player controls > Album art > Show hi-res audio badge on album cover', - hiResAudioBadgePos: 'Values: "topleft", "topright", "bottomleft", "bottomright" - Options > Player controls > Album art > Show hi-res audio badge on album cover', - showPause: 'Values: true, false - Options > Player controls > Album art > Show pause on album cover', - jumpSearchIncludeLibrary: 'Values: true, false - Options > Player controls > Jump search > Include library in playlist search query', - jumpSearchIncludePlaylist: 'Values: true, false - Options > Player controls > Jump search > Include playlist in library search query', - jumpSearchComposerOnly: 'Values: true, false - Options > Player controls > Jump search > Composer only in jump search query', - playlistWheelScrollSteps: 'Values: 0.5, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Scrollbar > Playlist > Mouse wheel scroll steps', - playlistWheelScrollDuration: 'Values: 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 - Options > Player controls > Scrollbar > Playlist > Mouse wheel scroll smooth duration', - playlistAutoScrollNowPlaying: 'Values: true, false - Options > Player controls > Scrollbar > Playlist > Auto-scroll to current playing song', - playlistAutoHideScrollbar: 'Values: true, false - Options > Player controls > Scrollbar > Playlist > Auto-hide', - playlistSmoothScrolling: 'Values: true, false - Options > Player controls > Scrollbar > Playlist > Smooth scroll', - scrollStepLib: 'Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Scrollbar > Library > Mouse wheel scroll steps', - durationScrollLib: 'Values: 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 - Options > Player controls > Scrollbar > Library > Mouse wheel scroll smooth duration', - libraryAutoScrollNowPlaying: 'Values: true, false - Options > Player controls > Scrollbar > Library > Auto-scroll to current playing song', - libraryAutoHideScrollbar: 'Values: true, false - Options > Player controls > Scrollbar > Library > Auto-hide', - smoothLib: 'Values: true, false - Options > Player controls > Scrollbar > Library > Smooth scroll', - scrollStepBio: 'Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Scrollbar > Biography > Mouse wheel scroll steps', - durationScrollBio: 'Values: 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 - Options > Player controls > Scrollbar > Biography > Mouse wheel scroll smooth duration', - biographyAutoHideScrollbar: 'Values: true, false - Options > Player controls > Scrollbar > Biography > Auto-hide', - smoothBio: 'Values: true, false - Options > Player controls > Scrollbar > Biography > Smooth scroll', - showTooltipTruncated: 'Values: true, false - Options > Player controls > Tooltip > Show tooltips only on truncated text', - showTooltipTimeline: 'Values: true, false - Options > Player controls > Tooltip > Show timeline tooltips', - showTooltipVolume: 'Values: true, false - Options > Player controls > Tooltip > Show volume tooltips', - showTooltipVolumeInPercent: 'Values: true, false - Options > Player controls > Tooltip > Show volume tooltips in percent', - showTooltipMain: 'Values: true, false - Options > Player controls > Tooltip > Show main tooltips', - showTooltipLibrary: 'Values: true, false - Options > Player controls > Tooltip > Show library tooltips', - showTooltipBiography: 'Values: true, false - Options > Player controls > Tooltip > Show biography tooltips', - showStyledTooltips: 'Values: true, false - Options > Player controls > Tooltip > Show styled tooltips', - panelWidthAuto: 'Values: true, false - Options > Player controls > Panel > Width > Use auto panel width', - showPanelOnStartup: 'Values: "cover", "playlist", "details", "library", "biography", "lyrics" - Options > Player controls > Panel > Show panel on startup', - showPreloaderLogo: 'Values: true, false - Options > Player controls > Panel > Show logo on preloader', - returnToHomeOnPlaybackStop: 'Values: true, false - Options > Player controls > Panel > Return to home on playback stop', - hideMiddlePanelShadow: 'Values: true, false - Options > Player controls > Panel > Hide middle panel shadow', - lockPlayerSize: 'Values: true, false - Options > Player controls > Panel > Lock player size', - maximizeToFullscreen: 'Values: true, false - not in Options - enable or disable the maximize to fullscreen function', - switchPlaybackTime: 'Values: true, false - not in Options - switch to playback time remaining when clicking on the playback time in the lower bar', - transportButtonSize_default: 'Values: 28, 30, 32, 34, 36, 38, 40, 42 - Options > Player controls > Transport button size > Default', - transportButtonSize_artwork: 'Values: 28, 30, 32, 34, 36 - Options > Player controls > Lower bar > Transport button size > Artwork', - transportButtonSize_compact: 'Values: 28, 30, 32, 34, 36 - Options > Player controls > Lower bar > Transport button size > Compact', - transportButtonSpacing_default: 'Values: 3, 5, 7, 10, 15 - Options > Player controls > Lower bar > Transport button spacing > Default', - transportButtonSpacing_artwork: 'Values: 3, 5, 7, 10, 15 - Options > Player controls > Lower bar > Transport button spacing > Artwork', - transportButtonSpacing_compact: 'Values: 3, 5, 7, 10, 15 - Options > Player controls > Lower bar > Transport button spacing > Compact', - showTransportControls_default: 'Values: true, false - Options > Player controls > Lower bar > Show transport controls > Default', - showTransportControls_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show transport controls > Artwork', - showTransportControls_compact: 'Values: true, false - Options > Player controls > Lower bar > Show transport controls > Compact', - showPlaybackOrderBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show playback order button > Default', - showPlaybackOrderBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show playback order button > Artwork', - showPlaybackOrderBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show playback order button > Compact', - showReloadBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show reload button > Default', - showReloadBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show reload button > Artwork', - showReloadBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show reload button > Compact', - showVolumeBtn_default: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Default', - showVolumeBtn_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Artwork', - showVolumeBtn_compact: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Compact', - autoHideVolumeBar: 'Values: true, false - Options > Player controls > Lower bar > Show volume button > Auto-hide bar', - showPlaybackTime_default: 'Values: true, false - Options > Player controls > Lower bar > Show playback time > Default', - showPlaybackTime_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show playback time > Artwork', - showPlaybackTime_compact: 'Values: true, false - Options > Player controls > Lower bar > Show playback time > Compact', - showLowerBarArtist_default: 'Values: true, false - Options > Player controls > Lower bar > Show artist > Default', - showLowerBarArtist_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show artist > Artwork', - showLowerBarArtist_compact: 'Values: true, false - Options > Player controls > Lower bar > Show artist > Compact', - showLowerBarTrackNum_default: 'Values: true, false - Options > Player controls > Lower bar > Show track number > Default', - showLowerBarTrackNum_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show track number > Artwork', - showLowerBarTrackNum_compact: 'Values: true, false - Options > Player controls > Lower bar > Show track number > Compact', - showLowerBarTitle_default: 'Values: true, false - Options > Player controls > Lower bar > Show song title > Default', - showLowerBarTitle_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show song title > Artwork', - showLowerBarTitle_compact: 'Values: true, false - Options > Player controls > Lower bar > Show song title > Compact', - showLowerBarComposer_default: 'Values: true, false - Options > Player controls > Lower bar > Show composer > Default', - showLowerBarComposer_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show composer > Artwork', - showLowerBarComposer_compact: 'Values: true, false - Options > Player controls > Lower bar > Show composer > Compact', - showLowerBarArtistFlags_default: 'Values: true, false - Options > Player controls > Lower bar > Show artist country flags > Default', - showLowerBarArtistFlags_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show artist country flags > Artwork', - showLowerBarArtistFlags_compact: 'Values: true, false - Options > Player controls > Lower bar > Show artist country flags > Compact', - showLowerBarVersion_default: 'Values: true, false - Options > Player controls > Lower bar > Show software version > Default', - showLowerBarVersion_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show software version > Artwork', - showLowerBarVersion_compact: 'Values: true, false - Options > Player controls > Lower bar > Show software version > Compact', - showProgressBar_default: 'Values: true, false - Options > Player controls > Lower bar > Show progress bar > Default', - showProgressBar_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show progress bar > Artwork', - showProgressBar_compact: 'Values: true, false - Options > Player controls > Lower bar > Show progress bar > Compact', - showPeakmeterBar_default: 'Values: true, false - Options > Player controls > Lower bar > Show peakmeter bar > Default', - showPeakmeterBar_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show peakmeter bar > Artwork', - showPeakmeterBar_compact: 'Values: true, false - Options > Player controls > Lower bar > Show peakmeter bar > Compact', - showWaveformBar_default: 'Values: true, false - Options > Player controls > Lower bar > Show waveform bar > Default', - showWaveformBar_artwork: 'Values: true, false - Options > Player controls > Lower bar > Show waveform bar > Artwork', - showWaveformBar_compact: 'Values: true, false - Options > Player controls > Lower bar > Show waveform bar > Compact', - seekbar: 'Values: "progressbar", "peakmeterBar", "waveformbar", - Options > Player controls > Seekbar > Type', - progressBarWheelSeekSpeed: 'Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Player controls > Seekbar > Progress bar > Mouse wheel seek speed', - progressBarRefreshRate: 'Values: 1000, 500, 333, "variable", 100, 60, 30 - Options > Player controls > Seekbar > Progress bar > Refresh rate', - peakmeterBarDesign: 'Values: "horizontal", "horizontal_center", "vertical" - Options > Player controls > Seekbar > Peakmeter bar > Style', - peakmeterBarVertSize: 'Values: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, "min" - Options > Player controls > Seekbar > Peakmeter bar > Size', - peakmeterBarVertDbRange: 'Values: 220, 215, 210, 320, 315, 310, 520, 515, 510 - Options > Player controls > Seekbar > Peakmeter bar > Decibel range', - peakmeterBarOverBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show over bars', - peakmeterBarOuterBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show outer bars', - peakmeterBarOuterPeaks: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show outer peaks', - peakmeterBarMainBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show main bars', - peakmeterBarMainPeaks: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show main peaks', - peakmeterBarMiddleBars: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show middle bars', - peakmeterBarProgBar: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show progress bar', - peakmeterBarGaps: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show gaps', - peakmeterBarGrid: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show grid', - peakmeterBarInfo: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show info', - peakmeterBarVertPeaks: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show peaks', - peakmeterBarVertBaseline: 'Values: true, false - Options > Player controls > Seekbar > Peakmeter bar > Display > Show baseline', - peakmeterBarRefreshRate: 'Values: 200, 150, 120, 100, 80, 60, 30 - Options > Player controls > Seekbar > Peakmeter bar > Refresh rate', - waveformBarMode: 'Values: "ffprobe", "audiowaveform", "visualizer" - Options > Player controls > Seekbar > Waveform bar > Mode', - waveformBarAnalysis: 'Values: "rms_level", "peak_level", "rms_peak" - Options > Player controls > Seekbar > Waveform bar > Analysis', - waveformBarDesign: 'Values: "waveform", "bars", "dots", "halfbars" - Options > Player controls > Seekbar > Waveform bar > Shape', - waveformBarSizeWave: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Waveform', - waveformBarSizeBars: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Bars', - waveformBarSizeDots: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Dots', - waveformBarSizeHalf: 'Values: 1, 2, 3, 4, 5 - Options > Player controls > Seekbar > Waveform bar > Size > Halfbars', - waveformBarSizeNormalize: 'Values:true, false - Options > Player controls > Seekbar > Waveform bar > Size > Normalize width', - waveformBarPaint: 'Values: "full", "partial" - Options > Player controls > Seekbar > Waveform bar > Display', - waveformBarPrepaint: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Prepaint', - waveformBarPrepaintFront: 'Values: "Infinity", 2, 5, 10, - Options > Player controls > Seekbar > Waveform bar > Display > Prepaint front', - waveformBarAnimate: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Animate', - waveformBarBPM: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Use BPM', - waveformBarInvertHalfbars: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Invert halfbars', - waveformBarIndicator: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Display > Show indicator', - waveformBarRefreshRate: 'Values: 1000, 500, 200, 100, 80, 60, 30, - Options > Player controls > Seekbar > Waveform bar > Refresh rate', - waveformBarRefreshRateVar: 'Values: true, false - Options > Player controls > Seekbar > Waveform bar > Refresh rate > Variable', - playbackOrder: 'Values: "default", "repeatPlaylist", "repeatTrack", "shuffle" - not in Options - playback order state button' -}; - -/** @type {Object} Options > Player controls settings config header description. */ -const themePlayerControlsSchema = new ConfigurationObjectSchema('themeControls', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* PLAYER CONTROLS: ' + - '* Top menu Options > Player controls ' + - '* You can set and select between various player control settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -////////////////// -// * PLAYLIST * // -////////////////// -/** @type {Object} Options > Playlist settings with default values. */ -const themePlaylistDefaults = { - playlistLayout: 'normal', - showPlaylistManager_default: true, - showPlaylistManager_artwork: false, - showPlaylistManager_compact: false, - showPlaylistHistory: true, - autoHidePlman: true, - show_album_art: true, - auto_album_art: false, - show_header: true, - show_rating_header: true, - show_PLR_header: false, - use_compact_header: false, - auto_collapse: false, - hyperlinksCtrlClick: false, - show_disc_header: true, - show_group_info: true, - showWeblinks: true, - showPlaylistFullDate: false, - show_row_stripes: false, - show_playcount: true, - show_queue_position: true, - show_rating: true, - use_rating_from_tags: false, - showPlaylistRatingGrid: false, - show_PLR: false, - showPlaylistTrackNumbers: true, - showPlaylistIndexNumbers: false, - showDifferentArtist: false, - showArtistPlaylistRows: false, - showAlbumPlaylistRows: false, - playlistTimeRemaining: false, - showVinylNums: true, - lastFmScrobblesFallback: true, - playlistRowHover: true, - playlistSortOrderAuto: false, - playlistSortOrder: '', - playlistSortOrderDirection: '_asc', - playlist_stats_include_artist: true, - playlist_stats_include_album : true, - playlist_stats_include_track: true, - playlist_stats_include_year: false, - playlist_stats_include_genre: false, - playlist_stats_include_label: false, - playlist_stats_include_country: false, - playlist_stats_include_stats: true, - playlist_stats_sort_by: '', - playlist_stats_sort_direction: '_dsc' -}; - -/** @type {Object} Options > Playlist settings config name description. */ -const themePlaylistComments = { - playlistLayout: 'Values: "normal", "full" - Options > Playlist > Layout', - showPlaylistManager_default: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist manager > Default', - showPlaylistManager_artwork: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist manager > Artwork', - showPlaylistManager_compact: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist manager > Compact', - showPlaylistHistory: 'Values: true, false - Options > Playlist > Playlist manager > Show playlist history', - autoHidePlman: 'Values: true, false - Options > Playlist > Playlist manager > Auto-hide', - show_album_art: 'Values: true, false - Options > Playlist > Album header > Album art > Show', - auto_album_art: 'Values: true, false - Options > Playlist > Album header > Album art > Auto-hide when no cover', - show_header: 'Values: true, false - Options > Playlist > Album header > Album header', - show_rating_header: 'Values: true, false - Options > Playlist > Album Header > Show rating', - show_PLR_header: 'Values: true, false - Options > Playlist > Album Header > Show PLR value', - use_compact_header: 'Values: true, false - Options > Playlist > Album header > Compact header', - auto_collapse: 'Values: true, false - Options > Playlist > Album header > Auto collapse and expand', - hyperlinksCtrlClick: 'Values: true, false - Options > Playlist > Album header > Ctrl+click to follow hyperlinks', - show_disc_header: 'Values: true, false - Options > Playlist > Album header > Show disc sub-header', - show_group_info: 'Values: true, false - Options > Playlist > Album header > Show group info', - showWeblinks: 'Values: true, false - Options > Playlist > Album header > Show weblinks in context menu', - showPlaylistFullDate: 'Values: true, false - Options > Playlist > Album header > Show long release date (YYYY-MM-DD)', - show_row_stripes: 'Values: true, false - Options > Playlist > Track row > Show row stripes', - show_playcount: 'Values: true, false - Options > Playlist > Track row > Show play count', - show_queue_position: 'Values: true, false - Options > Playlist > Track row > Show queue position', - show_rating: 'Values: true, false - Options > Playlist > Track row > Show rating', - use_rating_from_tags: 'Values: true, false - Options > Playlist > Track row > Show rating from tags', - showPlaylistRatingGrid: 'Values: true, false - Options > Playlist > Track row > Show rating grid', - show_PLR: 'Values: true, false - Options > Playlist > Track row > Show PLR value', - showPlaylistTrackNumbers: 'Values: true, false - Options > Playlist > Track row > Show track numbers', - showPlaylistIndexNumbers: 'Values: true, false - Options > Playlist > Track row > Show index numbers', - showDifferentArtist: 'Values: true, false - Options > Playlist > Track row > Show artist name on difference', - showArtistPlaylistRows: 'Values: true, false - Options > Playlist > Track row > Show artist name in all rows', - showAlbumPlaylistRows: 'Values: true, false - Options > Playlist > Track row > Show album title in all rows', - playlistTimeRemaining: 'Values: true, false - Options > Playlist > Track row > Show time remaining on playing track', - showVinylNums: 'Values: true, false - Options > Playlist > Track row > Show vinyl style numbering if available', - lastFmScrobblesFallback: 'Values: true, false - Options > Playlist > Track row > Show last.fm scrobbles on no local plays', - playlistRowHover: 'Values: true, false - Options > Playlist > Track row > Row mouse hover', - playlistSortOrderAuto: 'Values: true, false - Options > Playlist > Sort order > Always auto-sort', - playlistSortOrder: 'Values: "", "default", "artistDate_asc", "artistDate_dsc", "album", "title", "trackNumber", "year_asc", "year_dsc", "filePath", "custom" - Options > Playlist > Sort order', - playlistSortOrderDirection: 'Values: "_asc", "_dsc" - Options > Playlist > Sort order', - playlist_stats_include_artist: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include artist', - playlist_stats_include_album: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include album', - playlist_stats_include_track: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include track', - playlist_stats_include_year: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include year', - playlist_stats_include_genre: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include genre', - playlist_stats_include_label: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include label', - playlist_stats_include_country: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include country', - playlist_stats_include_stats: 'Values: true, false - Playlist context menu > Write playlist statistics to list > Include stats', - playlist_stats_sort_by: 'Values: "", "artist", "albumTitle", "year", "genre" - playlist context menu - Write playlist statistics to text', - playlist_stats_sort_direction: 'Values: "_asc", "_dsc" - playlist context menu - Write playlist statistics to text' -}; - -/** @type {Object} Options > Playlist settings config header description. */ -const themePlaylistSchema = new ConfigurationObjectSchema('themePlaylist', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* PLAYLIST: ' + - '* Top menu Options > Playlist ' + - '* You can set and select between various playlist settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {PlaylistGroupingPresets[]} Playlist grouping presets with default entries and values. */ -const themePlaylistGroupingPresets = [ - { - name: 'artist', - description: 'by artist', - group_query: '%album artist%', - title_query: '[%album artist%]', - sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", - show_date: false, - show_disc: false - }, - { - name: 'artist_album', - description: 'by artist / album', - group_query: '%album artist%%album%', - title_query: '[%album artist%]', - sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", - show_date: true, - show_disc: false - }, - { - name: 'artist_album_disc', - description: 'by artist / album / disc number', - group_query: '%album artist%%album%%discnumber%', - title_query: '[%album artist%]', - sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", - show_date: true, - show_disc: true - }, - { - name: 'artist_album_disc_edition', - description: 'by artist / album / disc number / edition / codec', - group_query: '%album artist%%album%%discnumber%%edition%%codec%', - title_query: '[%album artist%]', - sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", - show_date: true, - show_disc: true - }, - { - name: 'path', - description: 'by path', - group_query: '$directory_path(%path%)', - title_query: '[%album artist%]', - sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", - show_date: true, - show_disc: false - }, - { - name: 'date', - description: 'by date', - group_query: '%date%', - title_query: '[%album artist%]', - sub_title_query: "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", - show_date: true, - show_disc: false - } -]; - -/** @type {Object} Playlist grouping presets config header description. */ -const themePlaylistGroupingPresetsSchema = new ConfigurationObjectSchema('themePlaylistGroupingPresets', ConfigurationObjectType.Array, [ - { name: 'name' }, - { name: 'description' }, - { name: 'group_query' }, - { name: 'title_query' }, - { name: 'sub_title_query' }, - { name: 'show_date' }, - { name: 'show_disc' }], - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* PLAYLIST GROUPING PRESETS: ' + - '* You can add new entries with grouping patterns or reorder entries that will be displayed in the Playlist grouping manager. ' + - '* The playlist grouping manager can be accessed via right clicking in the Playlist > Grouping > Manage presets. ' + - '* Changes will be saved to the config file after reload when creating new grouping presets or modifications in the Playlist grouping manager. ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////// -// * DETAILS * // -///////////////// -/** @type {Object} Options > Details settings with default values. */ -const themeDetailsDefaults = { - showDiscArtStub: true, - noDiscArtStub: false, - discArtStub: 'cdAlbumCover', - displayDiscArt: true, - discArtOnTop: false, - filterDiscJpgsFromAlbumArt: true, - spinDiscArt: false, - spinDiscArtImageCount: 72, - spinDiscArtRedrawInterval: 75, - rotateDiscArt: true, - rotationAmt: 3, - artRotateDelay: 30, - discArtDisplayAmount: 0.5, - detailsAlbumArtOpacity: 255, - detailsAlbumArtDiscAreaOpacity: 255, - showGridArtist_default: false, - showGridArtist_artwork: false, - showGridTitle_default: false, - showGridTrackNum_default: false, - showGridTrackNum_artwork: false, - showGridTitle_artwork: false, - showGridPlayingPlaylist: false, - showGridTimeline_default: true, - showGridTimeline_artwork: true, - showGridArtistFlags_default: true, - showGridArtistFlags_artwork: true, - showGridReleaseFlags_default: 'logo', - showGridReleaseFlags_artwork: 'logo', - showGridCodecLogo_default: 'logo', - showGridCodecLogo_artwork: 'logo', - showGridChannelLogo_default: 'logo', - showGridChannelLogo_artwork: 'logo', - autoHideGridMetadata: true, - noDiscArtBg: true, - labelArtOnBg: false -}; - -/** @type {Object} Options > Details settings config name description. */ -const themeDetailsComments = { - showDiscArtStub: 'Values: true, false - Options > Details > Disc art > Disc art placeholder > Show placeholder if no disc art found', - noDiscArtStub: 'Values: true, false - Options > Details > Disc art > Disc art placeholder > No placeholder', - discArtStub: 'Values: "cdAlbumCover", "cdWhite", "cdBlack", "cdBlank", "cdTrans", "vinylAlbumCover", "vinylWhite", "vinylVoid", "vinylColdFusion", "vinylRingOfFire", "vinylMaple", "vinylBlack", "vinylBlackHole", "vinylEbony", "vinylTrans" - Options > Details > Disc art > Disc art placeholder', - displayDiscArt: 'Values: true, false - Options > Details > Disc art > Display disc art if found', - discArtOnTop: 'Values: true, false - Options > Details > Disc art > Display disc art above cover', - filterDiscJpgsFromAlbumArt: 'Values: true, false - Options > Details > Disc art > Filter cd/disc/vinyl .jpgs from artwork', - spinDiscArt: 'Values: true, false - Options > Details > Disc art > Spin disc art while songs play (increases memory and CPU)', - spinDiscArtImageCount: 'Values: 36, 45, 60, 72, 90, 120, 180 - Options > Details > Disc art > # Rotation images (memory usage/rotational speed)', - spinDiscArtRedrawInterval: 'Values: 250, 200, 150, 125, 100, 75, 50, 40, 30, 20, 10 - Options > Details > Disc art > Spinning disc art redraw speed', - rotateDiscArt: 'Values: true, false - Options > Details > Disc art > Rotate disc art as tracks change', - rotationAmt: 'Values: 2, 3, 4, 5 - Options > Details > Disc art > Disc art rotation amount', - artRotateDelay: 'Values: free to choose - not in Options - seconds to display each art', - discArtDisplayAmount: 'Values: 1, 0.5, 0.455, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1 - Options > Details > Disc art > Disc art display amount', - detailsAlbumArtOpacity: 'Values: 255, 230, 204, 178, 153, 128, 102, 76, 51, 25 - from 100% - 10% - Options > Details > Album art > Full artwork opacity', - detailsAlbumArtDiscAreaOpacity: 'Values: 255, 230, 204, 178, 153, 128, 102, 76, 51, 25 - from 100% - 10% - Options > Details > Album art > Disc area opacity', - showGridArtist_default: 'Values: true, false - Options > Details > Metadata grid > Show artist - when Default layout is active', - showGridArtist_artwork: 'Values: true, false - Options > Details > Metadata grid > Show artist - when Artwork layout is active', - showGridTrackNum_default: 'Values: true, false - Options > Details > Metadata grid > Show track number - when Default layout is active', - showGridTrackNum_artwork: 'Values: true, false - Options > Details > Metadata grid > Show track number - when Artwork layout is active', - showGridTitle_default: 'Values: true, false - Options > Details > Metadata grid > Show song title - when Default layout is active', - showGridTitle_artwork: 'Values: true, false - Options > Details > Metadata grid > Show song title - when Artwork layout is active', - showGridPlayingPlaylist: 'Values: true, false - Options > Details > Metadata grid > Show playing playlist', - showGridTimeline_default: 'Values: true, false - Options > Details > Metadata grid > Show timeline - when Default layout is active', - showGridTimeline_artwork: 'Values: true, false - Options > Details > Metadata grid > Show timeline - when Artwork layout is active', - showGridArtistFlags_default: 'Values: true, false - Options > Details > Metadata grid > Show artist country flags - when Default layout is active', - showGridArtistFlags_artwork: 'Values: true, false - Options > Details > Metadata grid > Show artist country flags - when Artwork layout is active', - showGridReleaseFlags_default: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show release country flags - when Default layout is active', - showGridReleaseFlags_artwork: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show release country flags - when Artwork layout is active', - showGridCodecLogo_default: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show codec logo - when Default layout is active', - showGridCodecLogo_artwork: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show codec logo - when Artwork layout is active', - showGridChannelLogo_default: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show channel logo - when Default layout is active', - showGridChannelLogo_artwork: 'Values: false, logo, textlogo - Options > Details > Metadata grid > Show channel logo - when Artwork layout is active', - autoHideGridMetadata: 'Values: true, false - Options > Details > Metadata grid > Auto-hide full metadata on small player', - noDiscArtBg: 'Values: true, false - Options > Details > Background > Show full background when no disc art', - labelArtOnBg: 'Values: true, false - Options > Details > Background > Show label art on background' -}; - -/** @type {Object} Options > Details settings config header description. */ -const themeDetailsSchema = new ConfigurationObjectSchema('themeDetails', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* DETAILS: ' + - '* Top menu Options > Details ' + - '* You can set and select between various details settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** - * @typedef {Object} MetadataGridEntry - * @property {string} label Text that shows in the left column of the metadata grid. - * @property {string} val Evaluated text in the right column. If this evaluates to an empty string, the entry is not shown. - * @property {boolean=} age If True, appends the "(1y 10, 23d)" style text to the evaluated val. Only valid for date strings. - * @property {string=} comment Optional comment for the .jsonc file. - */ - -/** @type {MetadataGridEntry[]} Metadata grid default entries and values. */ -const defaultMetadataGrid = [ - { label: 'Disc', val: `$if(${tf.disc_subtitle},[Disc %discnumber% \u2013 ]${tf.disc_subtitle})` }, - { label: 'Rel. Type', val: '$if($stricmp(%releasetype%,Album),,[%releasetype%])' }, - { label: 'Year', val: `$puts(d,${tf.date})$if($strcmp($year($get(d)),$get(d)),$get(d),)`, comment: '\'Year\' is shown if the date format is YYYY' }, - { label: 'Rel. Date', val: `$puts(d,${tf.date})$if($strcmp($year($get(d)),$get(d)),,$get(d))`, age: true, comment: '\'Release Date\' is shown if the date format is YYYY-MM-DD' }, - { label: 'Edition', val: tf.edition }, - { label: 'Label', val: '[$if($meta(label),$meta_sep(label, \u00B7 ),$if3(%publisher%,%discogs_label%,))]', comment: 'The label(s) or publisher(s) that released the album.' }, - { label: 'Catalog', val: `$puts(cn,$if3(%catalognumber%,%discogs_catalog%,))[$if($get(cn),$get(cn)[ / ${tf.releaseCountry}],)]` }, - { label: 'Rel. Country', val: `$puts(cn,$if3(%catalognumber%,%discogs_catalog%,))[$if($get(cn),,$replace(${tf.releaseCountry},XW,))]`, comment: 'Only shown if %catalognumber% or %discogs_catalog% is not present. If release country is entire world (\'XW\') value is hidden.' }, - { label: 'Track', val: '$if(%tracknumber%,$num(%tracknumber%,1)$if(%totaltracks%,/$num(%totaltracks%,1))$ifgreater(%totaldiscs%,1, CD %discnumber%/$num(%totaldiscs%,1),)' }, - { label: 'Genre', val: '[$meta_sep(genre, \u00B7 )]' }, - { label: 'Style', val: '[$meta_sep(style, \u00B7 )]' }, - { label: 'Release', val: '[%release%]' }, - { label: 'Codec', val: '[%codec%]' }, - { label: 'Channels', val: '[%channels%]' }, - { label: 'Source', val: '[%codec_profile%$if(%__bitspersample%, \u00B7 )]$if($strcmp(%__encoding%,lossless),%__bitspersample% bit)$ifgreater(%samplerate%,44100,$if($if2(%codec_profile%,%__bitspersample%), \u00B7 )$div(%samplerate%,1000)$replace($insert($right($div(%samplerate%,100),1),.,0),.0,) kHz,)[ \u00B7 $if3(%media%,%mediatype%,%media type%)]' }, - { label: 'Data', val: '%__bitrate% kbps \u00B7 $div(%filesize%,1048576).$num($div($mul($mod(%filesize%,1048576),10),1048576),0) MB' }, - { label: 'Added', val: '[$if2(%added_enhanced%,%added%)]', age: true }, - { label: 'Last Played', val: `[${tf.last_played}]`, age: true }, - { label: 'Hotness', val: "$puts(X,5)$puts(Y,$div(%_dynamic_rating%,400))$repeat($repeat(I,$get(X)) ,$div($get(Y),$get(X)))$repeat(I,$mod($get(Y),$get(X)))$ifgreater(%_dynamic_rating%,0, $replace($div(%_dynamic_rating%,1000)'.'$mod($div(%_dynamic_rating%,100),10),0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9),)" }, - { label: 'View Count', val: '[%fy_view_count%]' }, - { label: 'Likes', val: '[$if(%fy_like_count%,%fy_like_count% \u25B2 / %fy_dislike_count% \u25BC,)]' }, - { label: 'Play Count', val: '$if($or(%play_count%,%lastfm_play_count%),$puts(X,5)$puts(Y,$max(%play_count%,%lastfm_play_count%))$ifgreater($get(Y),30,,$repeat($repeat(I,$get(X)) ,$div($get(Y),$get(X)))$repeat(I,$mod($get(Y),$get(X))) )$get(Y))' }, - { label: 'Rating', val: '$if(%rating%,$repeat(\u2605 ,%rating%))' }, - { label: 'Mood', val: '$if(%mood%,$puts(X,5)$puts(Y,$mul(5,%mood%))$repeat($repeat(I,$get(X)) ,$div($get(Y),$get(X)))$repeat(I,$mod($get(Y),$get(X)))$replace(%mood%,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9))' }, - { label: 'Playing List', val: tf.playing_playlist }, - { label: 'Blank 01', val: '' }, - { label: 'Blank 02', val: '' }, - { label: 'Blank 03', val: '' }, - { label: 'Blank 04', val: '' }, - { label: 'Blank 05', val: '' }, - { label: 'Blank 06', val: '' }, - { label: 'Blank 07', val: '' }, - { label: 'Blank 08', val: '' } -]; - -/** @type {Object} Metadata grid config header description. */ -const gridSchema = new ConfigurationObjectSchema('metadataGrid', ConfigurationObjectType.Array, [ - { name: 'label' }, - { name: 'val' }, - { name: 'age', optional: true }], - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* DETAILS METADATA GRID: ' + - '* You can add new tags or reorder entries that will be displayed in the metadata grid in top menu Details. ' + - '* If there are too many entries and no space available in Details, tags will be hidden. You can change to a larger player size. ' + - '* Entries that evaluate to an empty string will not be shown in the grid. ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////// -// * LIBRARY * // -///////////////// -/** @type {Object} Options > Library settings with default values. */ -const themeLibraryDefaults = { - libraryLayout: 'normal', - libraryLayoutFullPreset: true, - libraryLayoutSplitPreset: true, - libraryLayoutSplitPreset2: false, - libraryLayoutSplitPreset3: false, - libraryLayoutSplitPreset4: false, - libraryDesign: 'reborn', - libraryTheme: 0, - libraryThumbnailSize: 'auto', - libraryThumbnailBorder: 'border', - albumArtShow: false, - itemOverlayType: 0, - albumArtLetter: true, - albumArtLetterNo: 1, - artId: 0, - albumArtGrpLevel: 0, - imgStyleFront: 1, - imgStyleBack: 1, - imgStyleDisc: 1, - imgStyleIcon: 1, - imgStyleArtist: 1, - albumArtLabelType: 1, - albumArtFlipLabels: false, - actionMode: 0, - clickAction: 0, - dblClickAction: 1, - mbtnClickAction: 1, - altClickAction: 1, - autoPlay: true, - keyAction: 0, - rememberTree: false, - artTreeSameView: false, - presetLoadCurView: true, - libraryPlaylistSwitch: false, - rootNode: 3, - nodeCounts: 1, - countsRight: true, - autoCollapse: false, - itemShowStatistics: 0, - highLightNowplaying: true, - showTracks: true, - rowStripes: false, - fullLineSelection: true, - libraryRowHover: true, - filterBy: 0, - sortOrder: 'default', - yearBeforeAlbum: true, - albumArtViewBy: 0, - treeViewBy: 0, - librarySource: 1, - librarySourceFixedPlaylist: false, - librarySourceFixedPlaylistName: '' -}; - -/** @type {Object} Options > Library settings config name dscription. */ -const themeLibraryComments = { - libraryLayout: 'Values: "normal", "full", "split" - Options > Library > Layout', - libraryLayoutFullPreset: 'Values: true, false - Options > Library > Layout > Use full preset', - libraryLayoutSplitPreset: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (collapse)', - libraryLayoutSplitPreset2: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (text)', - libraryLayoutSplitPreset3: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (art grid)', - libraryLayoutSplitPreset4: 'Values: true (only one split preset can be active), false - Options > Library > Layout > Use split preset (art header)', - libraryDesign: 'Values: "reborn", "traditional", "modern", "ultraModern", "clean", "facet", "coversLabelsRight", "coversLabelsBottom", "coversLabelsBlend", "flowMode" - Options > Library > Design', - libraryTheme: 'Values: 0, 1, 2, 3, 4, 5 - Options > Library > Theme', - libraryThumbnailSize: 'Values: "auto", "playlist", 0, 1, 2, 3, 4, 5, 6, 7 - Options > Library > Album art > Thumbnail size', - libraryThumbnailBorder: 'Values: "none", "border", "shadow" - Options > Library > Album art > Thumbnail border', - albumArtShow: 'Values: true, false - Options > Library > Album art > Activate option or change design to album art', - itemOverlayType: 'Values: 0, 1, 2 - Options > Library > Album art > Overlay', - albumArtLetter: 'Values: true, false - Options > Library > Album art > Index > Show on scrollbar drag', - albumArtLetterNo: 'Values: 0, 1 - Options > Library > Album art > Index', - artId: 'Values: 0, 1, 2, 3, 4 - Options > Library > Album art > View', - albumArtGrpLevel: 'Values: 0, 1, 2 - Options > Library > Album art > View', - imgStyleFront: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Front', - imgStyleBack: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Back', - imgStyleDisc: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Disc', - imgStyleIcon: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Icon', - imgStyleArtist: 'Values: 0, 1, 2 - Options > Library > Album art > Image > Artist', - albumArtLabelType: 'Values: 1, 2, 3, 4, 0 - Options > Library > Album art > Labels', - albumArtFlipLabels: 'Values: true, false - Options > Library > Album art > Labels', - actionMode: 'Values: 0, 1, 2 - Options > Library > Controls > Action mode', - clickAction: 'Values: 0, 1, 2, 3 - Options > Library > Controls > Single-click action', - dblClickAction: 'Values: 0, 1, 2, 3 - Options > Library > Controls > Double-click action', - mbtnClickAction: 'Values: 0, 1, 2 - Options > Library > Controls > Middle-mouse click action', - altClickAction: 'Values: 0, 1, 2 - Options > Library > Controls > Alt + mouse click action', - autoPlay: 'Values: true, false - Options > Library > Controls > Keystroke action > Play on Enter or send from menu', - keyAction: 'Values: 0, 1 - Options > Library > Controls > Keystroke action', - rememberTree: 'Values: true, false - Options > Library > Controls > Always remember library state', - artTreeSameView: 'Values: true, false - Options > Library > Controls > Always load View by same as tree', - presetLoadCurView: 'Values: true, false - Options > Library > Controls > Always load preset with current view pattern', - libraryPlaylistSwitch: 'Values: true, false - Options > Library > Controls > Switch to playlist when adding songs', - rootNode: 'Values: 0, 1, 2, 3 - Options > Library > Track row > Node root type', - nodeCounts: 'Values: 0, 1, 2 - Options > Library > Track row > Node item counts', - countsRight: 'Values: true, false - Options > Library > Track row > Node item counts position', - autoCollapse: 'Values: true, false - Options > Library > Track row > Node auto collapse', - itemShowStatistics: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 - Options > Library > Track row > Statistics', - highLightNowplaying: 'Values: true, false - Options > Library > Track row > Show now playing', - showTracks: 'Values: true, false - Options > Library > Track row > Show tracks when expanding nodes', - rowStripes: 'Values: true, false - Options > Library > Track row > Show row stripes', - fullLineSelection: 'Values: true, false - Options > Library > Track row > Row fully clickable', - libraryRowHover: 'Values: true, false - Options > Library > Track row > Row mouse hover', - filterBy: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - Options > Library > Filter order', - sortOrder: 'Values: 0, 1, 2, 3, 4 - Options > Library > Sort order', - yearBeforeAlbum: 'Values: true, false - Options > Library > Sort order', - albumArtViewBy: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Library > View order', - treeViewBy: 'Values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - Options > Library > View order', - librarySource: 'Values: 0, 1 - Options > Library > Source', - librarySourceFixedPlaylist: 'Values: true, false - not in Options, managed by Library source menu', - librarySourceFixedPlaylistName: 'Values: "selected playlist name" - not in Options, managed by Library source menu' -}; - -/** @type {Object} Options > Library settings config header dscription. */ -const themeLibrarySchema = new ConfigurationObjectSchema('themeLibrary', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* LIBRARY: ' + - '* Top menu Options > Library ' + - '* You can set and select between various library settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -/////////////////// -// * BIOGRAPHY * // -/////////////////// -/** @type {Object} Options > Biography settings with default values. */ -const themeBiographyDefaults = { - biographyLayout: 'normal', - biographyLayoutFullPreset: true, - style: 0, - filmStripPos: 3, - filmStripOverlay: false, - biographyTheme: 0, - biographyDisplay: 'Image+text', - showFilmStrip: false, - imgSeekerShow: 0, - heading: 1, - summaryShow: true, - summaryCompact: true, - artistView: true, - focus: false, - lockBio: false, - sourceAll: false, - classicalMusicMode: false, - cycPhotoLocation: 0, - covType: 0, - loadCovAllFb: false, - loadCovFolder: false, - artStyleDual: 1, - artReflDual: false, - artShadowDual: false, - covStyleDual: 1, - covReflDual: false, - covShadowDual: false, - artStyleImgOnly: 1, - artReflImgOnly: false, - artShadowImgOnly: false, - covStyleImgOnly: 1, - covReflImgOnly: false, - covShadowImgOnly: false, - filmPhotoStyle: 1, - filmCoverStyle: 1, - photoNum: 10, - cycPic: true, - imgSmoothTrans: false, - cycTimePic: 15 -}; - -/** @type {Object} Options > Biography settings config name description. */ -const themeBiographyComments = { - biographyLayout: 'Values: "normal", "full" - Options > Biography > Layout', - biographyLayoutFullPreset: 'Values: true, false - Options > Biography > Layout > Use full preset', - style: 'Values: 0, 1, 2, 3, 4, 5 - Options > Biography > Layout', - filmStripPos: 'Values: 0, 1, 2, 3 - Options > Biography > Layout > Filmstrip', - filmStripOverlay: 'Values: true, false - Options > Biography > Layout > Filmstrip > Overlay image area', - biographyTheme: 'Values: 0, 1, 2, 3 - Options > Biography > Theme', - biographyDisplay: 'Values: "Image+text", "Image", "Text" - Options > Biography > Display', - showFilmStrip: 'Values: true, false - Options > Biography > Display', - imgSeekerShow: 'Values: 0, 1 - Options > Biography > Display', - heading: 'Values: 0, 1 - Options > Biography > Display', - summaryShow: 'Values: true, false - Options > Biography > Display', - summaryCompact: 'Values: true, false - Options > Biography > Display', - artistView: 'Values: true, false - Options > Biography > Display', - focus: 'Values: true, false - Options > Biography > Display', - lockBio: 'Values: true, false - Options > Biography > Source > Text', - sourceAll: 'Values: true, false - Options > Biography > Source > Text > Amalgamate', - classicalMusicMode: 'Values: true, false - Options > Biography > Source > Text > Prefer composition (allmusic && wikipedia review)', - cycPhotoLocation: 'Values: 0, 1, 2 - Options > Biography > Source > Photo', - covType: 'Values: 0, 1, 2, 3, 4 - Options > Biography > Source > Cover', - loadCovAllFb: 'Values: true, false - Options > Biography > Source > Cover > Cycle above', - loadCovFolder: 'Values: true, false - Options > Biography > Source > Cover > Cycle from download folder', - artStyleDual: 'Values: 0, 1, 2 - Options > Biography > Image > Image + text > Photo', - artReflDual: 'Values: true, false - Options > Biography > Image > Image + text > Reflection (Photo)', - artShadowDual: 'Values: true, false - Options > Biography > Image > Image + text > Shadow (Photo)', - covStyleDual: 'Values: 0, 1, 2 - Options > Biography > Image > Image + text > Cover', - covReflDual: 'Values: true, false - Options > Biography > Image > Image + text > Reflection (Cover)', - covShadowDual: 'Values: true, false - Options > Biography > Image > Image + text > Shadow (Cover)', - artStyleImgOnly: 'Values: 0, 1, 2 - Options > Biography > Image > Image only > Photo', - artReflImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Reflection (Photo)', - artShadowImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Shadow (Photo)', - covStyleImgOnly: 'Values: 0, 1, 2 - Options > Biography > Image > Image only > Cover', - covReflImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Reflection (Cover)', - covShadowImgOnly: 'Values: true, false - Options > Biography > Image > Image only > Shadow (Cover)', - filmPhotoStyle: 'Values: 0, 1, 2 - Options > Biography > Image > Filmstrip > Photo', - filmCoverStyle: 'Values: 0, 1, 2 - Options > Biography > Image > Filmstrip > Cover', - photoNum: 'Values: 5, 10, 15, 20 - Options > Biography > Image > Downloads', - cycPic: 'Values: true, false - Options > Biography > Image > Auto cycle > Auto cycle', - imgSmoothTrans: 'Values: true, false - Options > Biography > Image > Auto cycle > Smooth transition', - cycTimePic: 'Values: 5, 10, 15, 30, 60 - Options > Biography > Image > Auto cycle' -}; - -/** @type {Object} Options > Biography settings config header description. */ -const themeBiographySchema = new ConfigurationObjectSchema('themeBiography', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* BIOGRAPHY: ' + - '* Top menu Options > Biography ' + - '* You can set and select between various biography settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////// -// * LYRICS * // -//////////////// -/** @type {Object} Options > Lyrics settings with default values. */ -const themeLyricsDefaults = { - lyricsLayout: 'normal', - lyricsDropShadowLevel: 2, - lyricsFadeScroll: true, - lyricsLargerCurrentSync: true, - lyricsAlbumArt: true, - lyricsRememberActiveState: false, - lyricsRememberPanelState: false, - lyricsScrollSpeed: 'normal', - lyricsScrollRateAvg: 750, - lyricsScrollRateMax: 375 -}; - -/** @type {Object} Options > Lyrics settings config name dscription. */ -const themeLyricsComments = { - lyricsLayout: 'Values: "normal", "full" - Options > Lyrics > Layout', - lyricsDropShadowLevel: 'Values: 0, 1, 2, 3 - Options > Lyrics > Display > Show drop shadow', - lyricsFadeScroll: 'Values: true, false - Options > Lyrics > Display > Show fade scroll', - lyricsLargerCurrentSync: 'Values: true, false - Options > Lyrics > Display > Larger current sync', - lyricsAlbumArt: 'Values: true, false - Options > Lyrics > Display > Show lyrics on album art', - lyricsRememberActiveState: 'Values: true, false - Options > Lyrics > Display > Remember active lyrics state', - lyricsRememberPanelState: 'Values: true, false - Options > Lyrics > Display > Remember lyrics panel state', - lyricsScrollSpeed: 'Values: "fastest", "fast", "normal", "slow", "slowest" - Options > Lyrics > Scroll speed', - lyricsScrollRateAvg: 'Values: false, 300, 500, 750, 1000, 1500 - not in Options, set by lyricsScrollSpeed', - lyricsScrollRateMax: 'Values: false, 150, 250, 375, 500, 725 - not in Options, set by lyricsScrollSpeed' -}; - -/** @type {Object} Options > Lyrics settings config header dscription. */ -const themeLyricsSchema = new ConfigurationObjectSchema('themeLyrics', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* LYRICS: ' + - '* Top menu Options > Lyrics ' + - '* You can set and select between various lyrics settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {array} Lyrics filename defaults. */ -const lyricFilenamesDefaults = [ - '%title%', - '%artist% - %title%', - '%artist% -%title%', - '%tracknumber% - %title%', - '%tracknumber% - %artist% - %title%' -]; - -/** @type {Object} Lyrics filename config header description. */ -const lyricFilenamesSchema = new ConfigurationObjectSchema('lyricFilenamePatterns', ConfigurationObjectType.Array, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* LYRICS TITLE FORMATTING: ' + - '* The title formatting defined patterns for the names of lyrics files. Do not include file extensions. ' + - '* Special characters which are not allowed in filenames (i.e. / : " etc.) will be stripped from the filenames automatically and replaced with underscores. ' + - '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -////////////////// -// * SETTINGS * // -////////////////// -/** @type {Object} Options > Settings with default values. */ -const themeSettingsDefaults = { - themeDayNightMode: false, - customThemeFonts: false, - customPreloaderLogo: false, - customThemeImages: false, - albumArtDiskCache: true, - albumArtPreLoad: false, - customLibraryDir: false, - libraryAutoDelete: false, - customBiographyDir: false, - biographyAutoDelete: false, - customLyricsDir: false, - lyricsAutoDelete: false, - customWaveformBarDir: false, - waveformBarAutoDelete: false, - themePerformance: 'balanced', - devTools: false, - disableRightClick: true -}; - -/** @type {Object} Options > Settings config name description. */ -const themeSettingsComments = { - themeDayNightMode: 'Values: false, or a custom string value in 24 hour time format e.g "6-18" - Options > Settings > Theme day/night mode', - customThemeFonts: 'Values: true, false - Options > Settings > Theme fonts > Use custom theme fonts', - customPreloaderLogo: 'Values: true, false - Options > Settings > Theme images > Use custom preloader logo', - customThemeImages: 'Values: true, false - Options > Settings > Theme images > Use custom theme images', - albumArtDiskCache: 'Values: true, false - Options > Settings > Theme cache > Library > Image disk cache enabled', - albumArtPreLoad: 'Values: true, false - Options > Settings > Theme cache > Library > Preload images in disk cache', - customLibraryDir: 'Values: true, false - Options > Settings > Theme cache > Library > Use custom library directory', - libraryAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Library > Auto-delete library cache on startup', - customBiographyDir: 'Values: true, false - Options > Settings > Theme cache > Library > Use custom biography directory', - biographyAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Biography > Auto-delete biography cache on startup', - customLyricsDir: 'Values: true, false - Options > Settings > Theme cache > Library > Use custom lyrics directory', - lyricsAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Lyrics > Auto-delete lyrics cache on startup', - customWaveformBarDir: 'Values: true, false - Options > Settings > Theme cache > Waveform bar > Use custom waveform bar directory', - waveformBarAutoDelete: 'Values: true, false - Options > Settings > Theme cache > Waveform bar > Auto-delete waveform bar cache on startup', - themePerformance: 'Values: "lowestQuality", "lowQuality", "balanced", "highQuality", "highestQuality" - Options > Settings > Theme performance', - devTools: 'Values: true, false - Options > Settings > Developer tools', - disableRightClick: 'Values: true, false - Options > Settings > Disable right-click' -}; - -/** @type {Object} Options > Settings config header description. */ -const themeSettingsSchema = new ConfigurationObjectSchema('themeSettings', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* SETTINGS: ' + - '* Top menu Options > Settings ' + - '* You can set and select between various lyrics settings that will be used. ' + - '* Note: These settings will be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} General settings with default values. */ -const settingsDefaults = { - artworkDisplayTime: 30, - discArtBasename: 'cd', - playlistCustomHeaderInfo: '', - playlistCustomTitle: '', - playlistCustomTitleNoHeader: '', - playlistSortDefault: '$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortArtistDate_asc: '$if2(%artist sort order%,%album artist%) $if3(%album sort order%,%original release date%,%originaldate%,%date%) %album% %edition% %codec% %discnumber% %tracknumber%', - playlistSortArtistDate_dsc: '$if2(%artist sort order%,%album artist%) $if3(%album sort order%,$sub(99999,%original release date%),$sub(99999,%originaldate%),$sub(99999,%date%)) %album% %edition% %codec% %discnumber% %tracknumber%', - playlistSortAlbumTitle: '%album% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortAlbumRating_asc: '%albumrating% $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortAlbumRating_dsc: '$sub(99999,%albumrating%) $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortAlbumPlaycount_asc: '%albumplaycount% $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortAlbumPlaycount_dsc: '$sub(99999,%albumplaycount%) $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortTrackTitle: '%title% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortTrackNumber: '%tracknumber% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortTrackRating_asc: '%rating% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortTrackRating_dsc: '$sub(99999,%rating%) $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortTrackPlaycount_asc: '%play_count% $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortTrackPlaycount_dsc: '$sub(99999,%play_count%) $if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortYear_asc: '%year% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortYear_dsc: '$sub(99999,%year%) $if3($sub(99999,%original release date%),$sub(99999,%originaldate%),$sub(99999,%date%)) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortGenre: '%genre% %artist% %album% %edition% %codec% %discnumber% %tracknumber%', - playlistSortLabel: '%label% %artist% %album% %edition% %codec% %discnumber% %tracknumber%', - playlistSortCountry: '%artistcountry% %artist% %album% %edition% %codec% %discnumber% %tracknumber%', - playlistSortFilePath: '%path_sort% $if3(%original release date%,%originaldate%,%date%) $if2(%artist sort order%,%album artist%) %edition% %codec% %discnumber% %tracknumber%', - playlistSortCustom: '$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%', - playlistShowBitSampleAlways: false, - extraTrackInfo: '$ifequal(%samplerate%,44100,, |$ifgreater($info(bitspersample),16, $info(bitspersample)bit,) $div(%samplerate%,1000).$left($right(%samplerate%,3),1)kHz)[ | $replace(%replaygain_album_gain%, dB,dB)]', - hideCursor: false, - hidePanelBgWhenCollapsed: false, - doubleClickRefresh: false, - showDebugLog: false, - showDebugThemeLog: false, - showDebugThemeOverlay: false, - stoppedString1: 'foobar2000', - stoppedString1acr: 'fb2k', - stoppedString2: '$replace(%_foobar2000_version%,foobar2000 ,)' -}; - -/** @type {Object} General settings config name description. */ -const settingsComments = { - artworkDisplayTime: 'Number of seconds to show each image if more than one is found and "Cycle through all artwork" option is enabled. (Min: 5, Max: 120)', - discArtBasename: 'Do not include extension. Example: "discart", if the image provider uses that name for saving discArt and you want those filtered from showing up as albumArt. Would also filter out discart1.png, etc.', - playlistCustomHeaderInfo: 'You can use your own custom pattern for the playlist header info', - playlistCustomTitle: 'You can use your own custom title pattern for the playlist row', - playlistCustomTitleNoHeader: 'You can use your own custom title pattern for the playlist row - when playlist header is not being displayed', - playlistSortDefault: 'Options > Playlist > Sort order > Default - sort pattern to sort playlists generated from Library selections or clicking on hyperlinks in the Playlist', - playlistSortArtistDate_asc: 'Options > Playlist > Sort order > Artist | date ascending', - playlistSortArtistDate_dsc: 'Options > Playlist > Sort order > Artist | date descending', - playlistSortAlbumTitle: 'Options > Playlist > Sort order > Album', - playlistSortAlbumRating_asc: 'Options > Playlist > Sort order > Album rating | ascending', - playlistSortAlbumRating_dsc: 'Options > Playlist > Sort order > Album rating | descending', - playlistSortAlbumPlaycount_asc: 'Options > Playlist > Sort order > Album playcount | ascending', - playlistSortAlbumPlaycount_dsc: 'Options > Playlist > Sort order > Album playcount | descending', - playlistSortTrackTitle: 'Options > Playlist > Sort order > Track', - playlistSortTrackNumber: 'Options > Playlist > Sort order > Track number', - playlistSortTrackRating_asc: 'Options > Playlist > Sort order > Track rating | ascending', - playlistSortTrackRating_dsc: 'Options > Playlist > Sort order > Track rating | descending', - playlistSortTrackPlaycount_asc: 'Options > Playlist > Sort order > Track playcount | ascending', - playlistSortTrackPlaycount_dsc: 'Options > Playlist > Sort order > Track playcount | descending', - playlistSortYear_asc: 'Options > Playlist > Sort order > Year | ascending', - playlistSortYear_dsc: 'Options > Playlist > Sort order > Year | descending', - playlistSortGenre: 'Options > Playlist > Sort order > Genre', - playlistSortLabel: 'Options > Playlist > Sort order > Label', - playlistSortCountry: 'Options > Playlist > Sort order > Country', - playlistSortFilePath: 'Options > Playlist > Sort order > File path', - playlistSortCustom: 'Options > Playlist > Sort order > Custom', - playlistShowBitSampleAlways: 'Always show the bit depth and sample rate in the playlist header.', - extraTrackInfo: 'Portion of the trackInfo in the upper right, directly under the year. Only part of the info string is customizable', - hideCursor: 'Hides cursor when song is playing after 10 seconds of no mouse activity', - hidePanelBgWhenCollapsed: 'Hide panel background when playing an album and the playlist or library view is active', - doubleClickRefresh: 'Will refresh the theme when double clicking for example on the lower bar', - showDebugLog: 'Enables extra logging in the console. Probably not needed unless you encounter a problem or you\'re asked to enable it.', - showDebugThemeLog: 'Logs the output of the algorithm which determines the primary theme color.', - showDebugThemeOverlay: 'Displays various theme debug logs on the album art as an overlay.', - stoppedString1: 'The bolded portion of text shown above the progress bar when nothing is playing', - stoppedString2: 'The second (non-bold) portion of text shown above the progress bar when nothing is playing' -}; - -/** @type {Object} General settings config header description. */ -const settingsSchema = new ConfigurationObjectSchema('settings', ConfigurationObjectType.Object, /* Will display as key/val pairs with comments attached. */ undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* GENERAL SETTINGS: ' + - '* These settings are not in the top menu options. ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////////////////////////////// -// ! GEORGIA-REBORN-CUSTOM DEFAULTS ! // -//////////////////////////////////////// - - -////////////////////////////////// -// * CUSTOM LIBRARY DIRECTORY * // -////////////////////////////////// -/** @type {array} Custom library cache directory. */ -const customLibraryDirDefaults = [ - 'C:\\Replace_this_path_to_your_new_library\\Directory\\' -]; - -/** @type {Object} Custom library cache directory config header description. */ -const customLibraryDirSchema = new ConfigurationObjectSchema('customLibraryDir', ConfigurationObjectType.Array, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM LIBRARY DIRECTORY: ' + - '* You can set your own custom library directory. ' + - '* Replace the path below and activate in top menu Options > Settings > Theme cache > Library > Use custom library directory. Restart foobar to take effect. ' + - '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////////////////////////// -// * CUSTOM BIOGRAPHY DIRECTORY * // -//////////////////////////////////// -/** @type {array} Custom biography cache directory. */ -const customBiographyDirDefaults = [ - 'C:\\Replace_this_path_to_your_new_biography\\Directory\\' -]; - -/** @type {Object} Custom biography cache directory config header description. */ -const customBiographyDirSchema = new ConfigurationObjectSchema('customBiographyDir', ConfigurationObjectType.Array, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM BIOGRAPHY DIRECTORY: ' + - '* You can set your own custom biography directory. ' + - '* Replace the path below and activate in top menu Options > Settings > Theme cache > Library > Use custom biography directory. Restart foobar to take effect. ' + - '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////////////////////// -// * CUSTOM LYRICS DIRECTORY * // -///////////////////////////////// -/** @type {array} Custom lyrics cache directory. */ -const customLyricsDirDefaults = [ - 'C:\\Replace_this_path_to_your_new_lyrics\\Directory\\' -]; - -/** @type {Object} Custom lyrics cache directory config header description. */ -const customLyricsDirSchema = new ConfigurationObjectSchema('customLyricsDir', ConfigurationObjectType.Array, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM LYRICS DIRECTORY: ' + - '* You can set your own custom lyric directory. ' + - '* Change in foobar\'s Preferences > ESLyric > Lyric options > Location to your custom lyric folder and replace the path below. ' + - '* Activate in top menu Options > Settings > Theme cache > Lyrics > Use custom lyrics directory. Restart foobar to take effect. ' + - '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -/////////////////////////////////////// -// * CUSTOM WAVEFORM BAR DIRECTORY * // -/////////////////////////////////////// -/** @type {array} Custom waveform bar cache directory. */ -const customWaveformBarDirDefaults = [ - 'C:\\Replace_this_path_to_your_new_waveform\\Directory\\' -]; - -/** @type {Object} Custom waveform bar cache directory config header description. */ -const customWaveformBarDirSchema = new ConfigurationObjectSchema('customWaveformBarDir', ConfigurationObjectType.Array, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM WAVEFORM BAR DIRECTORY: ' + - '* You can set your own custom waveform bar directory. ' + - '* Replace the path below and activate in top menu Options > Settings > Theme cache > Waveform bar > Use custom waveform bar directory. Restart foobar to take effect. ' + - '* Note: This setting will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -//////////////////////////// -// * CUSTOM THEME FONTS * // -//////////////////////////// -/** @type {Object} Custom fonts with default Open Sans type. */ -const customFontsDefaults = { - fontDefault: 'Open Sans', - fontTopMenu: 'Open Sans SemiBold', - fontLowerBarArtist: 'Open Sans SemiBold', - fontLowerBarTitle: 'Open Sans', - fontLowerBarDisc: 'Open Sans', - fontLowerBarTime: 'Open Sans SemiBold', - fontLowerBarLength: 'Open Sans', - fontLowerBarWave: 'Open Sans SemiBold', - fontNotification: 'Open Sans', - fontPopup: 'Open Sans', - fontTooltip: 'Open Sans', - fontGridArtist: 'Open Sans', - fontGridTitle: 'Open Sans', - fontGridTitleBold: 'Open Sans SemiBold', - fontGridAlbum: 'Open Sans', - fontGridKey: 'Open Sans SemiBold', - fontGridValue: 'Open Sans', - playlistTitleNormal: 'Open Sans', - playlistTitleSelected: 'Open Sans', - playlistTitlePlaying: 'Open Sans SemiBold', - playlistArtistNormal: 'Open Sans', - playlistArtistPlaying: 'Open Sans', - playlistArtistNormalCompact: 'Open Sans SemiBold', - playlistArtistPlayingCompact: 'Open Sans SemiBold', - playlistAlbum: 'Open Sans SemiBold', - playlistDate: 'Open Sans SemiBold', - playlistDateCompact: 'Open Sans', - playlistInfo: 'Open Sans', - playlistCover: 'Open Sans', - playlistPlaycount: 'Open Sans', - fontLibrary: 'Open Sans', - fontBiography: 'Open Sans', - fontLyrics: 'Open Sans' -}; - -/** @type {Object} Custom fonts config name description. */ -const customFontsComments = { - fontDefault: 'Default font: Segoe UI - panel font used as default and panel related elements in Playlist, Library, Biography', - fontTopMenu: 'Default font: Segoe UI Semibold - theme font used for top menu buttons', - fontLowerBarArtist: 'Default font: HelveticaNeueLT Pro 65 Md - theme artist font used in lower bar', - fontLowerBarTitle: 'Default font: HelveticaNeueLT Pro 45 Lt - theme title font used in lower bar', - fontLowerBarDisc: 'Default font: HelveticaNeueLT Pro 45 Lt - theme disc font used in lower bar', - fontLowerBarTime: 'Default font: HelveticaNeueLT Pro 65 Md - theme time font used in lower bar', - fontLowerBarLength: 'Default font: HelveticaNeueLT Pro 45 Lt - theme length font used in lower bar', - fontLowerBarWave: 'Default font: HelveticaNeueLT Pro 65 Md - theme waveform bar font used in lower bar', - fontNotification: 'Default font: HelveticaNeueLT Pro 65 Md - theme notification font used in jump search and style indicator', - fontPopup: 'Default font: Segoe UI - theme popup font used in custom theme menu and theme log overlay', - fontTooltip: 'Default font: HelveticaNeueLT Pro 65 Md - theme tooltip font used in styled tooltips', - fontGridArtist: 'Default font: HelveticaNeueLT Pro 65 Md - theme artist font used in metadata grid ( Details )', - fontGridTitle: 'Default font: HelveticaNeueLT Pro 45 Lt - theme title font used in metadata grid ( Details )', - fontGridTitleBold: 'Default font: HelveticaNeueLT Pro 65 Md - theme title bold font used in metadata grid ( Details )', - fontGridAlbum: 'Default font: HelveticaNeueLT Pro 65 Md - theme album font used in metadata grid ( Details )', - fontGridKey: 'Default font: HelveticaNeueLT Pro 55 Roman - theme key font used in metadata grid ( Details )', - fontGridValue: 'Default font: HelveticaNeueLT Pro 45 Lt - theme value font used in metadata grid ( Details )', - playlistTitleNormal: 'Default font: Segoe UI - playlist title font used in rows, normal state', - playlistTitleSelected: 'Default font: Segoe UI - playlist title font used in rows, selected state', - playlistTitlePlaying: 'Default font: Segoe UI - playlist title font used in rows, playing state', - playlistArtistNormal: 'Default font: Segoe UI Semibold - playlist artist font used in headers, normal state', - playlistArtistPlaying: 'Default font: Segoe UI Semibold - playlist artist font used in headers, playing state', - playlistArtistNormalCompact: 'Default font: Segoe UI Semibold - playlist artist font for compact mode used in headers, normal state', - playlistArtistPlayingCompact: 'Default font: Segoe UI Semibold - playlist artist font for compact mode used in headers, playing state', - playlistAlbum: 'Default font: Segoe UI Semibold - playlist album font used in headers', - playlistDate: 'Default font: Segoe UI Semibold - playlist date font used in headers', - playlistDateCompact: 'Default font: Segoe UI Semibold - playlist date font for compact mode used in headers', - playlistInfo: 'Default font: Segoe UI - playlist info font used in headers', - playlistCover: 'Default font: Segoe UI Semibold - playlist cover font used in thumbnails', - playlistPlaycount: 'Default font: Segoe UI - playlist playcount font used in rows', - fontLibrary: 'Default font: Segoe UI - library font', - fontBiography: 'Default font: Segoe UI - biography font', - fontLyrics: 'Default font: Segoe UI - lyrics font' -}; - -/** @type {Object} Custom fonts config header description. */ -const customFontsSchema = new ConfigurationObjectSchema('customFont', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME FONTS: ' + - '* Top menu Options > Settings > Theme fonts > Use custom theme fonts ' + - '* Here you can set your own custom fonts if top menu Options > Settings > Theme fonts > Use custom theme fonts was activated. ' + - '* If you change the Open Sans placeholder, it needs to be the exact name of the font name/family. ' + - '* Note: If you mix different fonts all together, there will be issues with Y-alignment because fonts have different line heights than others. ' + - '* For example, if you want to use more than one font in the lower bar, the fonts need to have the same line height. ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////////////////// -// * CUSTOM STYLE PRESET * // -///////////////////////////// -/** @type {Object} Custom style preset with default values. */ -const customStylePresetDefaults = { - theme: 'white', - nighttime: false, - bevel: false, - blend: false, - blend2: false, - gradient: false, - gradient2: false, - alternative: false, - alternative2: false, - blackAndWhite: false, - blackAndWhite2: false, - blackAndWhiteReborn: false, - blackReborn: false, - rebornWhite: false, - rebornBlack: false, - randomPastel: false, - randomDark: false, - randomAutoColor: 'off', - topMenuButtons: 'default', - transportButtons: 'default', - progressBarDesign: 'default', - progressBar: 'default', - progressBarFill: 'default', - volumeBarDesign: 'default', - volumeBar: 'default', - volumeBarFill: 'default', - themeBrightness: 'default' -}; - -/** @type {Object} Custom style preset config name description. */ -const customStylePresetComments = { - theme: 'Values: "white", "black", "reborn", "random", "blue", "darkblue", "red", "cream", "nblue", "ngreen", "nred", "ngold"', - nighttime: 'Values: true, false - special style can only be used with reborn, random, custom theme', - bevel: 'Values: true, false - can be used in all themes', - blend: 'Values: true, false - can be used in all themes', - blend2: 'Values: true, false - can be used in all themes', - gradient: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', - gradient2: 'Values: true, false - can only be used with reborn, random, blue, darkblue, red theme', - alternative: 'Values: true, false - can be used in all themes but not with special styles', - alternative2: 'Values: true, false - can be used in all themes but not with special styles', - blackAndWhite: 'Values: true, false - special white style can only be used with white theme', - blackAndWhite2: 'Values: true, false - special white style can only be used with white theme', - blackAndWhiteReborn: 'Values: true, false - special white style can only be used with white theme', - blackReborn: 'Values: true, false - special black style can only be used with black theme', - rebornWhite: 'Values: true, false - special reborn style can only be used with reborn theme', - rebornBlack: 'Values: true, false - special reborn style can only be used with reborn theme', - randomPastel: 'Values: true, false - special random style can only be used with random theme', - randomDark: 'Values: true, false - special random style can only be used with random theme', - randomAutoColor: 'Values: "off", 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, "track", - can only be used with random theme', - topMenuButtons: 'Values: "default", "filled", "bevel", "inner", "emboss", "minimal"', - transportButtons: 'Values: "default", "bevel", "inner", "emboss", "minimal"', - progressBarDesign: 'Values: "default", "rounded", "lines", "blocks", "dots", "thin"', - progressBar: 'Values: "default", "bevel", "inner"', - progressBarFill: 'Values: "default", "bevel", "inner", "blend"', - volumeBarDesign: 'Values: "default", "rounded"', - volumeBar: 'Values: "default", "bevel", "inner"', - volumeBarFill: 'Values: "default", "bevel", "inner"', - themeBrightness: 'Values: -50, -40, -30, -25, -20, -15, -10, -5, "default", 5, 10, 15, 20, 25, 30, 40, 50' -}; - -/** @type {Object} Custom style preset config header description. */ -const customStylePresetSchema = new ConfigurationObjectSchema('customStylePreset', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM STYLE PRESET: ' + - '* Top menu Options > Preset > User preset > User settings ' + - '* You can set your own custom style preset if top menu Options > Preset > User preset > User settings is selected. ' + - '* First you need to set only one theme in theme: and set the theme to true, all other themes need to be set to false. ' + - '* Second, only one style per column can be set, see top menu Options > Style. For example, only Blend or Blend 2 can be active at the same time. ' + - '* It is the same for Alternative or Alternative 2 or if this particular column has an additional special style, see comment in styles section. ' + - '* Best practice would be to first set each style in top menu Options > Style, note these styles and modify them in the config here. ' + - '* If something goes wrong, you can reset your theme settings in top menu Options > Settings > Theme configuration > Reset all ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -///////////////////////////////////// -// * CUSTOM DISC ART PLACEHOLDER * // -///////////////////////////////////// -/** @type {Object} Custom disc art placeholder with default values. */ -const customDiscArtStubDefaults = { - cdName01: 'Custom CD - Name 01', - cdStub01: 'cd-custom01.png', - cdName02: 'Custom CD - Name 02', - cdStub02: '', - cdName03: 'Custom CD - Name 03', - cdStub03: '', - cdName04: 'Custom CD - Name 04', - cdStub04: '', - cdName05: 'Custom CD - Name 05', - cdStub05: '', - vinylName01: 'Custom Vinyl - Name 01', - vinylStub01: 'vinyl-custom01.png', - vinylName02: 'Custom Vinyl - Name 02', - vinylStub02: '', - vinylName03: 'Custom Vinyl - Name 03', - vinylStub03: '', - vinylName04: 'Custom Vinyl - Name 04', - vinylStub04: '', - vinylName05: 'Custom Vinyl - Name 05', - vinylStub05: '' -}; - -/** @type {Object} Custom disc art placeholder config name description. */ -const customDiscArtStubComments = { - cdName01: 'Value: the name of the first CD placeholder, e.g. Custom CD - Name 01', - cdStub01: 'Value: the filename of the first CD placeholder, e.g. cd-custom01.png', - cdName02: 'Value: the name of the second CD placeholder, e.g. Custom CD - Name 02', - cdStub02: 'Value: the filename of the second CD placeholder, e.g. cd-custom02.png', - cdName03: 'Value: the name of the third CD placeholder, e.g. Custom CD - Name 03', - cdStub03: 'Value: the filename of the third CD placeholder, e.g. cd-custom03.png', - cdName04: 'Value: the name of the fourth CD placeholder, e.g. Custom CD - Name 04', - cdStub04: 'Value: the filename of the fourth CD placeholder, e.g. cd-custom04.png', - cdName05: 'Value: the name of the fifth CD placeholder, e.g. Custom CD - Name 05', - cdStub05: 'Value: the filename of the fifth CD placeholder, e.g. cd-custom05.png', - vinylName01: 'Value: the name of the first vinyl placeholder, e.g. Custom vinyl - Name 01', - vinylStub01: 'Value: the filename of the first vinyl placeholder, e.g. vinyl-custom01.png', - vinylName02: 'Value: the name of the second vinyl placeholder, e.g. Custom vinyl - Name 02', - vinylStub02: 'Value: the filename of the second vinyl placeholder, e.g. vinyl-custom02.png', - vinylName03: 'Value: the name of the third vinyl placeholder, e.g. Custom vinyl - Name 03', - vinylStub03: 'Value: the filename of the third vinyl placeholder, e.g. vinyl-custom03.png', - vinylName04: 'Value: the name of the fourth vinyl placeholder, e.g. Custom vinyl - Name 04', - vinylStub04: 'Value: the filename of the fourth vinyl placeholder, e.g. vinyl-custom04.png', - vinylName05: 'Value: the name of the fifth vinyl placeholder, e.g. Custom vinyl - Name 05', - vinylStub05: 'Value: the filename of the fifth vinyl placeholder, e.g. vinyl-custom05.png' -}; - -/** @type {Object} Custom disc art placeholder config header description. */ -const customDiscArtStubSchema = new ConfigurationObjectSchema('customDiscArtStub', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM DISC ART PLACEHOLDER: ' + - '* Top menu Options > Details > Disc art > Disc art placeholder ' + - '* You can add your own custom CD and Vinyl placeholders that will be dynamically appended to the disc art placeholder menu. ' + - '* It is recommended to use the predefined prefix naming convention for CD and Vinyl placeholders, such as "Custom CD - YourName" or "Custom Vinyl - YourName". ' + - '* The filenames must end with .png, for example, "cd-custom01.png" or "vinyl-custom01.png". ' + - '* If the placeholders cdStub02, cdStub03, cdStub04, cdStub05, vinylStub02, vinylStub03, vinylStub04, vinylStub05 etc, do not have any filename values, they will not be added to the menu. ' + - '* The custom disc art placeholders are located in the directory "georgia-reborn/images/custom/discart". ' + - '* Note: These settings will NOT be automatically set if you use top menu Options > Settings > Theme configuration > Save settings to config file ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - - -////////////////////// -// * CUSTOM THEME * // -////////////////////// -/** @type {Object} Custom theme with default HEX color values. */ -const customThemeDefaults = { - name: '', - - ////////////////////////// - // * PRELOADER COLORS * // - ////////////////////////// - - preloaderBg: '372355', - preloaderLogo: 'violet-play-logo.png', - preloaderLowerBarTitle: 'ffffff', - preloaderProgressBar: '412d64', - preloaderProgressBarFill: 'ebc841', - preloaderProgressBarFrame: '372355', - preloaderUIHacksFrame: '372355', - - ///////////////////////// - // * PLAYLIST COLORS * // - ///////////////////////// - - // * MAIN COLORS * // - g_pl_colors_bg: '321946', - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors_plman_text_normal: 'd2d2d2', - g_pl_colors_plman_text_hovered: 'ffffff', - g_pl_colors_plman_text_pressed: '3cfaa0', - - // * HEADER COLORS * // - g_pl_colors_header_nowplaying_bg: '372355', - g_pl_colors_header_sideMarker: '3cfaa0', - g_pl_colors_header_artist_normal: 'd2d2d2', - g_pl_colors_header_artist_playing: '3cfaa0', - g_pl_colors_header_album_normal: 'd2d2d2', - g_pl_colors_header_album_playing: 'ffffff', - g_pl_colors_header_info_normal: 'd2d2d2', - g_pl_colors_header_info_playing: 'ffffff', - g_pl_colors_header_date_normal: 'd2d2d2', - g_pl_colors_header_date_playing: '3cfaa0', - g_pl_colors_header_line_normal: '503782', - g_pl_colors_header_line_playing: '6450b4', - - // * ROW COLORS * // - g_pl_colors_row_nowplaying_bg: '372355', - g_pl_colors_row_stripes_bg: '372355', - g_pl_colors_row_selection_frame: '503782', - g_pl_colors_row_sideMarker: '3cfaa0', - g_pl_colors_row_title_normal: 'd2d2d2', - g_pl_colors_row_title_playing: 'ffffff', - g_pl_colors_row_title_selected: '3cfaa0', - g_pl_colors_row_title_hovered: 'ffffff', - g_pl_colors_row_rating_color: 'ebc841', - g_pl_colors_row_disc_subheader_line: '503782', - g_pl_colors_row_drag_line: '6450b4', - g_pl_colors_row_drag_line_reached: '3cfaa0', - - // * SCROLLBAR COLORS * // - g_pl_colors_sbar_btn_normal: 'd2d2d2', - g_pl_colors_sbar_btn_hovered: 'ebc841', - g_pl_colors_sbar_thumb_normal: '412d64', - g_pl_colors_sbar_thumb_hovered: 'ebc841', - g_pl_colors_sbar_thumb_drag: '3cfaa0', - - //////////////////////// - // * LIBRARY COLORS * // - //////////////////////// - - // * MAIN COLORS * // - ui_col_bg: '321946', - ui_col_rowStripes: '372355', - - // * ROW COLORS * // - ui_col_nowPlayingBg: '372350', - ui_col_sideMarker: '3cfaa0', - ui_col_selectionFrame: '503782', - ui_col_selectionFrame2: '503782', - ui_col_hoverFrame: '503782', - - // * NODE COLORS * // - ui_col_iconPlus: 'ebc841', - ui_col_iconPlus_h: '3cfaa0', - ui_col_iconPlus_sel: '3cfaa0', - ui_col_iconPlusBg: '372355', - ui_col_iconMinus_e: 'ebc841', - ui_col_iconMinus_c: 'ebc841', - ui_col_iconMinus_h: 'ebc841', - - // * TEXT COLORS * // - ui_col_text: 'd2d2d2', - ui_col_text_h: 'ffffff', - ui_col_text_nowp: 'ffffff', - ui_col_textSel: '3cfaa0', - ui_col_txt: 'd2d2d2', - ui_col_txt_h: 'ffffff', - ui_col_txt_box: 'ffffff', - ui_col_search: 'ffffff', - - // * BUTTON COLORS * // - ui_col_searchBtn: 'ffffff', - ui_col_crossBtn: 'ffffff', - ui_col_filterBtn: 'ffffff', - ui_col_settingsBtn: 'ffffff', - ui_col_line: '503782', - ui_col_s_line: '503782', - - // * SCROLLBAR COLORS * // - ui_col_sbarBtns: 'd2d2d2', - ui_col_sbarNormal: '504673', - ui_col_sbarHovered: 'ebc841', - ui_col_sbarDrag: '3cfaa0', - - ////////////////////////// - // * BIOGRAPHY COLORS * // - ////////////////////////// - - // * MAIN COLORS * // - uiBio_col_bg: '321946', - uiBio_col_rowStripes: '372350', - - // * HEADER COLORS * // - uiBio_col_headingText: 'ffffff', - uiBio_col_bottomLine: '503782', - uiBio_col_centerLine: '503782', - uiBio_col_sectionLine: '503782', - - // * TEXT COLORS * // - uiBio_col_accent: 'ebc841', - uiBio_col_source: 'd2d2d2', - uiBio_col_summary: 'd2d2d2', - uiBio_col_text: 'd2d2d2', - - // * MISC COLORS * // - uiBio_col_lyricsNormal: 'd2d2d2', - uiBio_col_lyricsHighlight: 'ebc841', - uiBio_col_noPhotoStubBg: '372355', - uiBio_col_noPhotoStubText: '3cfaa0', - - // * SCROLLBAR COLORS * // - uiBio_col_sbarBtns: 'd2d2d2', - uiBio_col_sbarNormal: '504673', - uiBio_col_sbarHovered: 'ebc841', - uiBio_col_sbarDrag: '3cfaa0', - - ///////////////////// - // * MAIN COLORS * // - ///////////////////// - - // * MAIN COLORS * // - col_bg: '372355', - col_shadow: '000000', - col_discArtShadow: '000000', - col_noAlbumArtStub: '3cfaa0', - col_lowerBarArtist: '3cfaa0', - col_lowerBarTitle: 'ffffff', - col_lowerBarTime: 'ffffff', - col_lowerBarLength: 'ffffff', - col_lyricsNormal: 'ffffff', - col_lyricsHighlight: 'ebc841', - col_lyricsShadow: '000000', - - // * DETAILS * // - col_detailsBg: '321946', - col_detailsText: 'd2d2d2', - col_detailsRating: 'ebc841', - col_timelineAdded: '3cfaa0', - col_timelinePlayed: '32d287', - col_timelineUnplayed: '28aa6e', - col_timelineFrame: '321946', - - // * POPUP COLORS * // - col_popupBg: '372355', - col_popupText: 'd2d2d2', - - // * TOP MENU BUTTON COLORS * // - col_menuBgColor: '503782', - col_menuStyleBg: '372355', - col_menuRectStyleEmbossTop: '6450b4', - col_menuRectStyleEmbossBottom: '151515', - col_menuRectNormal: '6450b4', - col_menuRectHovered: '6450b4', - col_menuRectDown: '6450b4', - col_menuTextNormal: 'ffffff', - col_menuTextHovered: 'ebc841', - col_menuTextDown: '3cfaa0', - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col_transportEllipseBg: '503782', - col_transportEllipseNormal: '503c8c', - col_transportEllipseHovered: '6450b4', - col_transportEllipseDown: '6450b4', - col_transportStyleBg: '372355', - col_transportStyleTop: '503c8c', - col_transportStyleBottom: '151515', - col_transportIconNormal: '3cfaa0', - col_transportIconHovered: 'ebc841', - col_transportIconDown: '3cfaa0', - - // * PROGRESS BAR COLORS * // - col_progressBar: '412d64', - col_progressBarStreaming: 'cf0005', - col_progressBarFrame: '372355', - col_progressBarFill: 'ebc841', - - // * PEAKMETER BAR COLORS * // - col_peakmeterBarProg: '412d64', - col_peakmeterBarProgFill: 'ebc841', - col_peakmeterBarFillTop: '3cfaa0', - col_peakmeterBarFillMiddle: '32d287', - col_peakmeterBarFillBack: '28aa6e', - col_peakmeterBarVertProgFill: 'ebc841', - col_peakmeterBarVertFill: 'ebc841', - col_peakmeterBarVertFillPeaks: 'fff0af', - - // * WAVEFORM BAR COLORS * // - col_waveformBarFillFront: 'ebc841', - col_waveformBarFillBack: 'b99b32', - col_waveformBarFillPreFront: '7d55cd', - col_waveformBarFillPreBack: '5f419b', - col_waveformBarIndicator: '3cfaa0', - - // * VOLUME BAR COLORS * // - col_volumeBar: '412d64', - col_volumeBarFrame: '372355', - col_volumeBarFill: '3cfaa0', - - // * STYLE COLORS * // - col_styleBevel: '1e0f2d', - col_styleGradient: '1e1428', - col_styleGradient2: '5a003c', - col_styleProgressBar: '372355', - col_styleProgressBarLineTop: '321946', - col_styleProgressBarLineBottom: '503782', - col_styleProgressBarFill: '8c6914', - col_styleVolumeBar: '372355', - col_styleVolumeBarFill: '19826e' -}; - -/** @type {Object} Custom theme config name description. */ -const customThemeComments = { - name: 'Custom theme name will be displayed in the options theme menu', - - ////////////////////////// - // * PRELOADER COLORS * // - ////////////////////////// - - preloaderBg: 'Preloader background color. If this value is empty, the default color will be used', - preloaderLogo: 'Preloader logo must end with .png, for example, logo.png. If this value is empty, the default logo will be used', - preloaderLowerBarTitle: 'Preloader lower bar title color. If this value is empty, the default color will be used', - preloaderProgressBar: 'Preloader progress bar color. If this value is empty, the default color will be used', - preloaderProgressBarFill: 'Preloader progress bar fill color. If this value is empty, the default color will be used', - preloaderProgressBarFrame: 'Preloader progress bar frame color. If this value is empty, the default color will be used', - preloaderUIHacksFrame: 'Preloader UIHacks frame color. Should have the same color as preloaderBg. If this value is empty, the default color will be used', - - ///////////////////////// - // * PLAYLIST COLORS * // - ///////////////////////// - - // * MAIN COLORS * // - g_pl_colors_bg: 'Playlist main background color', - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors_plman_text_normal: 'Playlist manager text color in normal state', - g_pl_colors_plman_text_hovered: 'Playlist manager text color in hovered state', - g_pl_colors_plman_text_pressed: 'Playlist manager text color in pressed state', - - // * HEADER COLORS * // - g_pl_colors_header_nowplaying_bg: 'Playlist header nowplaying bg color', - g_pl_colors_header_sideMarker: 'Playlist header side marker color', - g_pl_colors_header_artist_normal: 'Playlist header artist text color in normal state', - g_pl_colors_header_artist_playing: 'Playlist header artist text color in playing state', - g_pl_colors_header_album_normal: 'Playlist header album text color in normal state', - g_pl_colors_header_album_playing: 'Playlist header album text color in playing state', - g_pl_colors_header_info_normal: 'Playlist header info text color in normal state', - g_pl_colors_header_info_playing: 'Playlist header info text color in playing state', - g_pl_colors_header_date_normal: 'Playlist header date text color in normal state', - g_pl_colors_header_date_playing: 'Playlist header date text color in playing state', - g_pl_colors_header_line_normal: 'Playlist header line color in normal state', - g_pl_colors_header_line_playing: 'Playlist header line color in playing state', - - // * ROW COLORS * // - g_pl_colors_row_nowplaying_bg: 'Playlist row nowplaying bg color', - g_pl_colors_row_stripes_bg: 'Playlist row alternate bg color', - g_pl_colors_row_selection_frame: 'Playlist row selection frame color', - g_pl_colors_row_sideMarker: 'Playlist row side marker color', - g_pl_colors_row_title_normal: 'Playlist row title text color in normal state', - g_pl_colors_row_title_playing: 'Playlist row title text color in playing state', - g_pl_colors_row_title_selected: 'Playlist row title text color in selected state', - g_pl_colors_row_title_hovered: 'Playlist row title text color in hovered state', - g_pl_colors_row_rating_color: 'Playlist row rating color', - g_pl_colors_row_disc_subheader_line: 'Playlist row disc sub header line color', - g_pl_colors_row_drag_line: 'Playlist track reorder drag line color', - g_pl_colors_row_drag_line_reached: 'Playlist track reorder reached drag line color', - - // * SCROLLBAR COLORS * // - g_pl_colors_sbar_btn_normal: 'Playlist scrollbar button color in normal state', - g_pl_colors_sbar_btn_hovered: 'Playlist scrollbar button color in hovered state', - g_pl_colors_sbar_thumb_normal: 'Playlist scrollbar thumb color in normal state', - g_pl_colors_sbar_thumb_hovered: 'Playlist scrollbar thumb color in hovered state', - g_pl_colors_sbar_thumb_drag: 'Playlist scrollbar thumb color in dragged state', - - //////////////////////// - // * LIBRARY COLORS * // - //////////////////////// - - // * MAIN COLORS * // - ui_col_bg: 'Library main background color', - ui_col_rowStripes: 'Library row stripes background color', - - // * ROW COLORS * // - ui_col_nowPlayingBg: 'Library row now playing bg color', - ui_col_sideMarker: 'Library row side marker color', - ui_col_selectionFrame: 'Library row selection frame color', - ui_col_selectionFrame2: 'Library row selection frame color 2', - ui_col_hoverFrame: 'Library row hover frame color', - - // * NODE COLORS * // - ui_col_iconPlus: 'Library tree plus node icon color', - ui_col_iconPlus_h: 'Library tree plus node icon color in hovered state', - ui_col_iconPlus_sel: 'Library tree plus node icon color in selected state', - ui_col_iconPlusBg: 'Library tree plus node icon background color for traditional tree', - ui_col_iconMinus_e: 'Library tree minus node icon color in expanded state for traditional tree', - ui_col_iconMinus_c: 'Library tree minus node icon color in collapsed state for traditional tree', - ui_col_iconMinus_h: 'Library tree minus node icon color in hovered state for traditional tree', - - // * TEXT COLORS * // - ui_col_text: 'Library row text color', - ui_col_text_h: 'Library row text color in hovered state', - ui_col_text_nowp: 'Library row now playing text color', - ui_col_textSel: 'Library row text color in selected state', - ui_col_txt: 'Library user interface text color', - ui_col_txt_h: 'Library user interface text color in hovered state', - ui_col_txt_box: 'Library user interface text box color', - ui_col_search: 'Library search text color', - - // * BUTTON COLORS * // - ui_col_searchBtn: 'Library search button color', - ui_col_crossBtn: 'Library cross button color', - ui_col_filterBtn: 'Library filter button color', - ui_col_settingsBtn: 'Library settings button color', - ui_col_line: 'Library line color', - ui_col_s_line: 'Library line color when selected', - - // * SCROLLBAR COLORS, ALSO LINKED WITH BIOGRAPHY SCROLLBAR COLORS * // - ui_col_sbarBtns: 'Library scrollbar button color', - ui_col_sbarNormal: 'Library scrollbar normal color', - ui_col_sbarHovered: 'Library scrollbar hovered color', - ui_col_sbarDrag: 'Library scrollbar drag color', - - ////////////////////////// - // * BIOGRAPHY COLORS * // - ////////////////////////// - - // * MAIN COLORS * // - uiBio_col_bg: 'Biography main background color', - uiBio_col_rowStripes: 'Biography row stripes background color', - - // * HEADER COLORS * // - uiBio_col_headingText: 'Biography header text color', - uiBio_col_bottomLine: 'Biography header bottom line color', - uiBio_col_centerLine: 'Biography header center line color', - uiBio_col_sectionLine: 'Biography header section line color', - - // * TEXT COLORS * // - uiBio_col_source: 'Biography source text color', - uiBio_col_accent: 'Biography accent text color (used in item properties)', - uiBio_col_summary: 'Biography summary text color', - uiBio_col_text: 'Biography text color', - - // * MISC COLORS * // - uiBio_col_lyricsNormal: 'Biography lyrics normal color', - uiBio_col_lyricsHighlight: 'Biography lyrics highlight color', - uiBio_col_noPhotoStubBg: 'Biography no photo stub background color', - uiBio_col_noPhotoStubText: 'Biography no photo stub text color', - - // * SCROLLBAR COLORS * // - uiBio_col_sbarBtns: 'Biography scrollbar button color', - uiBio_col_sbarNormal: 'Biography scrollbar thumb normal color', - uiBio_col_sbarHovered: 'Biography scrollbar thumb hovered color', - uiBio_col_sbarDrag: 'Biography scrollbar thumb drag color', - - ///////////////////// - // * MAIN COLORS * // - ///////////////////// - - // * MAIN COLORS * // - col_bg: 'Main background color', - col_shadow: 'Panel shadow color', - col_discArtShadow: 'Disc art shadow color', - col_noAlbumArtStub: 'No album art stub color', - col_lowerBarArtist: 'Lower bar artist text color', - col_lowerBarTitle: 'Lower bar title text color', - col_lowerBarTime: 'Lower bar playback time text color', - col_lowerBarLength: 'Lower bar playback length text color', - col_lyricsNormal: 'Lyrics normal color', - col_lyricsHighlight: 'Lyrics highlight color', - col_lyricsShadow: 'Lyrics shadow color', - - // * DETAILS * // - col_detailsBg: 'Details background color', - col_detailsText: 'Details text color', - col_detailsRating: 'Details rating color', - col_timelineAdded: 'Details timeline added color', - col_timelinePlayed: 'Details timeline played color', - col_timelineUnplayed: 'Details timeline unplayed color', - col_timelineFrame: 'Details timeline frame color', - - // * POPUP COLORS * // - col_popupBg: 'Popup background color', - col_popupText: 'Popup text color', - - // * TOP MENU BUTTON COLORS * // - col_menuBgColor: 'Top menu background color', - col_menuStyleBg: 'Top menu style background color', - col_menuRectStyleEmbossTop: 'Top menu rectangle style emboss top color', - col_menuRectStyleEmbossBottom: 'Top menu rectangle style emboss bottom color', - col_menuRectNormal: 'Top menu rectangle color', - col_menuRectHovered: 'Top menu rectangle color when hovered', - col_menuRectDown: 'Top menu rectangle color when down', - col_menuTextNormal: 'Top menu text color', - col_menuTextHovered: 'Top menu text color when hovered', - col_menuTextDown: 'Top menu text color when down', - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col_transportEllipseBg: 'Lower bar transport ellipse background color', - col_transportEllipseNormal: 'Lower bar transport ellipse color', - col_transportEllipseHovered: 'Lower bar transport ellipse color when hovered', - col_transportEllipseDown: 'Lower bar transport ellipse color when down', - col_transportStyleBg: 'Lower bar transport style background color', - col_transportStyleTop: 'Lower bar transport style top color', - col_transportStyleBottom: 'Lower bar transport style bottom color', - col_transportIconNormal: 'Lower bar transport icon color', - col_transportIconHovered: 'Lower bar transport icon color when hovered', - col_transportIconDown: 'Lower bar transport icon color when down', - - // * PROGRESS BAR COLORS * // - col_progressBar: 'Progress bar color', - col_progressBarStreaming: 'Progress bar when streaming color', - col_progressBarFrame: 'Progress bar frame color', - col_progressBarFill: 'Progress bar fill color', - - // * PEAKMETER BAR COLORS * // - col_peakmeterBarProg: 'Peakmeter bar progress bg color', - col_peakmeterBarProgFill: 'Peakmeter bar progress fill color', - col_peakmeterBarFillTop: 'Peakmeter bar top fill color', - col_peakmeterBarFillMiddle: 'Peakmeter bar middle fill color', - col_peakmeterBarFillBack: 'Peakmeter bar back fill color', - col_peakmeterBarVertProgFill: 'Peakmeter bar vertical progress fill color', - col_peakmeterBarVertFill: 'Peakmeter bar vertical fill color', - col_peakmeterBarVertFillPeaks: 'Peakmeter bar vertical peaks fill color', - - // * WAVEFORM BAR COLORS * // - col_waveformBarFillFront: 'Waveform bar front fill color', - col_waveformBarFillBack: 'Waveform bar back fill color', - col_waveformBarFillPreFront: 'Waveform bar prepaint front fill color', - col_waveformBarFillPreBack: 'Waveform bar prepaint back fill color', - col_waveformBarIndicator: 'Waveform bar progress indicator color', - - // * VOLUME BAR COLORS * // - col_volumeBar: 'Volume bar color', - col_volumeBarFrame: 'Volume bar frame color', - col_volumeBarFill: 'Volume bar fill color', - - // * STYLE COLORS * // - col_styleBevel: 'Style bevel color', - col_styleGradient: 'Style gradient color', - col_styleGradient2: 'Style gradient 2 color', - col_styleProgressBar: 'Style progress bar color', - col_styleProgressBarLineTop: 'Style progress bar line top color', - col_styleProgressBarLineBottom: 'Style progress bar line bottom color', - col_styleProgressBarFill: 'Style progress bar fill color', - col_styleVolumeBar: 'Style volume bar color', - col_styleVolumeBarFill: 'Style volume bar fill color' -}; - -/** @type {Object} Custom theme 01 config header description. */ -const customTheme01Schema = new ConfigurationObjectSchema('customTheme01', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 01: ' + - '* Top menu Options > Theme > Custom > Theme 01 ' + - '* This is the first custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 02 config header description. */ -const customTheme02Schema = new ConfigurationObjectSchema('customTheme02', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 02: ' + - '* Top menu Options > Theme > Custom > Theme 02 ' + - '* This is the second custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 03 config header description. */ -const customTheme03Schema = new ConfigurationObjectSchema('customTheme03', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 03: ' + - '* Top menu Options > Theme > Custom > Theme 03 ' + - '* This is the third custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 04 config header description. */ -const customTheme04Schema = new ConfigurationObjectSchema('customTheme04', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 04: ' + - '* Top menu Options > Theme > Custom > Theme 04 ' + - '* This is the fourth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 05 config header description. */ -const customTheme05Schema = new ConfigurationObjectSchema('customTheme05', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 05: ' + - '* Top menu Options > Theme > Custom > Theme 05 ' + - '* This is the fifth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 06 config header description. */ -const customTheme06Schema = new ConfigurationObjectSchema('customTheme06', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 06: ' + - '* Top menu Options > Theme > Custom > Theme 06 ' + - '* This is the sixth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 07 config header description. */ -const customTheme07Schema = new ConfigurationObjectSchema('customTheme07', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 07: ' + - '* Top menu Options > Theme > Custom > Theme 07 ' + - '* This is the seventh custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 08 config header description. */ -const customTheme08Schema = new ConfigurationObjectSchema('customTheme08', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 08: ' + - '* Top menu Options > Theme > Custom > Theme 08 ' + - '* This is the eighth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 09 config header description. */ -const customTheme09Schema = new ConfigurationObjectSchema('customTheme09', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 09: ' + - '* Top menu Options > Theme > Custom > Theme 09 ' + - '* This is the ninth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); - -/** @type {Object} Custom theme 10 config header description. */ -const customTheme10Schema = new ConfigurationObjectSchema('customTheme10', ConfigurationObjectType.Object, undefined, - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' + - '* CUSTOM THEME 10: ' + - '* Top menu Options > Theme > Custom > Theme 10 ' + - '* This is the tenth custom theme that contains all colors for Main, Playlist, Library, Biography. ' + - '* Note: All colors will be automatically saved here if you do live editing with top menu Options > Theme > Custom > Edit custom theme ' + - '///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '); diff --git a/profile/georgia-reborn/scripts/Base/gr-display.js b/profile/georgia-reborn/scripts/Base/gr-display.js index cd20ee4d..298fbc77 100644 --- a/profile/georgia-reborn/scripts/Base/gr-display.js +++ b/profile/georgia-reborn/scripts/Base/gr-display.js @@ -1,77 +1,59 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Display * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Display * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////// -// * VARIABLES * // -/////////////////// -/** @type {number} Default display dots per inch setting. */ -let DPI = 96; -/** @type {boolean} State variable for 4K resolution. */ -let RES_4K = false; -/** @type {boolean} State variable for QHD resolution. */ -let RES_QHD = false; -/** @type {boolean} Setup variable for 4K check. */ -let sizeInitialized = false; -/** @type {boolean} Setup variable for 4K check. */ -let lastSize; - -// * Check and set the actual display DPI number via reading the Windows registry value. -try { DPI = WshShell.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI'); } catch (e) {} - - -///////////////// -// * HELPERS * // -///////////////// -/** - * NOT USED AT THE MOMENT. - * Converts a size value from points to pixels using the DPI value. - * @param {number} size The size in pixels. - * @returns {number} The size in points. - */ -function Scale(size) { - return Math.round(size * DPI / 72); -} - - -/** - * Scales the value based on 4K mode or not. - * @param {number} val The value that needs to be scaled for 4K resolution. - * @returns {number} The value doubled. - */ -function SCALE(val) { - return RES_4K ? val * 2 : val; -} - - ///////////////// // * DISPLAY * // ///////////////// /** - * Manages and sets the window size based on active display resolution, layout setting and player size. + * A class that manages and sets the window size based on active display resolution, layout setting and player size. */ class Display { + /** + * Creates the `Display` instance. + * Initializes variables to check for 4K display and to save the last used window dimensions. + * Sets up predefined player sizes based on active layout and display settings. + */ constructor() { - /** - * Saves last used active window width and height. - */ - this.fbHandle = ''; + /** @public @type {boolean} Setup variable for 4K check. */ + this.sizeInitialized = false; + /** @private @type {boolean} Setup variable for 4K check. */ + this.lastSize = undefined; + /** @private @type {boolean} Saves last used active window width and height. */ + this.fbHandle = undefined; + + // * UIHACKS * // + /** @public @type {boolean} UIHacks pseudo caption state, used in setWindowDrag. */ + this.pseudoCaption = false; + /** @public @type {number} UIHacks sets pseudo caption width when dragging foobar, used in setWindowDrag. */ + this.pseudoCaptionWidth = 0; + /** Preferences > Display > Main Window > Frame style: No border. */ + UIHacks.FrameStyle = 3; + /** Preferences > Display > Main Window > Move with: Any method. */ + UIHacks.MoveStyle = 3; + /** Preferences > Display > Main Window > Aero effects: Glass frame. */ + UIHacks.Aero.Effect = 2; + /** Preferences > Display > Main Window > Aero top: 1 px fix for window drop shadow. */ + UIHacks.Aero.Top = 1; + /** Preferences > Display > Main Window > Constraints: Disable window maximization. */ + UIHacks.BlockMaximize = false; /** * Contains all available predefined player sizes in Georgia-ReBORN. - * Based on active `Options > Layout` and `Options > Display` settings. - * @type {Object} + * These sizes are based on active `Options > Layout` and `Options > Display` settings. + * @type {{ [resolution: string]: { [size: string]: { name: string, size: string, width: number, height: number } } }} + * @public */ this.playerSize = { default: { @@ -126,66 +108,69 @@ class Display { } } }; - } - // * INITIALIZATION * // + // * INITIALIZE 4K PROPERTY * // + RES._4K = grSet.displayRes === '4K' || window.Width > 2560 && window.Height > 1600; + } + // * PUBLIC METHODS - INITIALIZATION * // + // #region PUBLIC METHODS - INITIALIZATION /** * Display auto-detection used in Options > Display or for the very first foobar startup and when factory resetting the theme. */ async autoDetectRes() { const resetSize = async () => { - RES_4K = false; - RES_QHD = false; - pref.displayRes = 'HD'; - pref.playerSize_4K_small = false; - pref.playerSize_QHD_small = false; - pref.playerSize_HD_small = true; + RES._4K = false; + RES._QHD = false; + grSet.displayRes = 'HD'; + grSet.playerSize_4K_small = false; + grSet.playerSize_QHD_small = false; + grSet.playerSize_HD_small = true; this.setSizesFor4KorHD(); }; const check4k = async () => { this.setWindowSize(2800, 1720); // Check if player size 'Normal' for 4K can be attained - if (ww > 2560 && wh > 1600) { - RES_4K = true; - RES_QHD = false; - pref.displayRes = '4K'; - pref.playerSize_4K_normal = true; - pref.playerSize_QHD_small = false; - pref.playerSize_HD_small = false; + if (grm.ui.ww > 2560 && grm.ui.wh > 1600) { + RES._4K = true; + RES._QHD = false; + grSet.displayRes = '4K'; + grSet.playerSize_4K_normal = true; + grSet.playerSize_QHD_small = false; + grSet.playerSize_HD_small = false; this.setSizesFor4KorHD(); } }; const checkQHD = async () => { - if (ww < 2800 && wh < 1720) { + if (grm.ui.ww < 2800 && grm.ui.wh < 1720) { this.setWindowSize(2500, 1400); // Check if this resolution for QHD can be attained - if (ww < 2500 && wh < 1400) { // If not, set to HD mode + if (grm.ui.ww < 2500 && grm.ui.wh < 1400) { // If not, set to HD mode resetSize(); } else { // Set to QHD mode - RES_4K = true; - RES_QHD = true; - pref.displayRes = 'QHD'; - pref.playerSize_4K_normal = false; - pref.playerSize_QHD_small = true; - pref.playerSize_HD_small = false; + RES._4K = true; + RES._QHD = true; + grSet.displayRes = 'QHD'; + grSet.playerSize_4K_normal = false; + grSet.playerSize_QHD_small = true; + grSet.playerSize_HD_small = false; this.setSizesForQHD(); } } }; const updatePanels = async () => { - if (pref.layout === 'default') { - display.layoutDefault(); + if (grSet.layout === 'default') { + grm.display.layoutDefault(); } - else if (pref.layout === 'artwork') { - display.layoutArtwork(); + else if (grSet.layout === 'artwork') { + grm.display.layoutArtwork(); } - else if (pref.layout === 'compact') { - display.layoutCompact(); + else if (grSet.layout === 'compact') { + grm.display.layoutCompact(); } - initPanels(); + grm.ui.initPanels(); }; // * Start detection @@ -197,31 +182,31 @@ class Display { /** * Checks and sets the player size and display resolution mode. - * Also marks Options > Player size to 'Small', 'Normal', 'Large' if current window size matches a predfined size. + * Also marks Options > Player size to 'Small', 'Normal', 'Large' if current window size matches a predefined size. * Called from on_size() when window size changes. */ checkRes() { // * Set and update the player size based on active layout - pref[`savedWidth_${pref.layout}`] = ww; - pref[`savedHeight_${pref.layout}`] = wh; + grSet[`savedWidth_${grSet.layout}`] = grm.ui.ww; + grSet[`savedHeight_${grSet.layout}`] = grm.ui.wh; // * Check current player size - const res = `${ww}x${wh}`; - const size = this.playerSize[pref.layout][pref.displayRes]; + const res = `${grm.ui.ww}x${grm.ui.wh}`; + const size = this.playerSize[grSet.layout][grSet.displayRes]; if (size) { const sizes = Object.values(size); const matchingSize = sizes.find(size => size.size === res); - pref.playerSize = matchingSize ? matchingSize.name : 'custom'; + grSet.playerSize = matchingSize ? matchingSize.name : 'custom'; } // * Check and set display monitor resolution mode for 4K or QHD - RES_4K = pref.displayRes === '4K' || (ww > 2560 && wh > 1600); - RES_QHD = pref.displayRes === 'QHD' || (ww > 2500 && ww <= 2560 && wh > 1400 && wh <= 1600); + RES._4K = grSet.displayRes === '4K' || (grm.ui.ww > 2560 && grm.ui.wh > 1600); + RES._QHD = grSet.displayRes === 'QHD' || (grm.ui.ww > 2500 && grm.ui.ww <= 2560 && grm.ui.wh > 1400 && grm.ui.wh <= 1600); - if (lastSize !== RES_4K) { - sizeInitialized = false; - lastSize = RES_4K; + if (this.lastSize !== RES._4K) { + this.sizeInitialized = false; + this.lastSize = RES._4K; } this.setWindowSizeFix(); @@ -231,33 +216,34 @@ class Display { * Initializes the current player size and called from setThemeSettings(), used to save player size in the config file. */ initPlayerSize() { - if (pref.layout === 'default') { - pref.savedWidth_default = window.GetProperty('Georgia-ReBORN - 16. System: Saved width (Default)'); - pref.savedHeight_default = window.GetProperty('Georgia-ReBORN - 16. System: Saved height (Default)'); - this.setWindowSize(pref.savedWidth_default, pref.savedHeight_default); - } else if (pref.layout === 'artwork') { - pref.savedWidth_artwork = window.GetProperty('Georgia-ReBORN - 16. System: Saved width (Artwork)'); - pref.savedHeight_artwork = window.GetProperty('Georgia-ReBORN - 16. System: Saved height (Artwork)'); - this.setWindowSize(pref.savedWidth_artwork, pref.savedHeight_artwork); - } else if (pref.layout === 'compact') { - pref.savedWidth_compact = window.GetProperty('Georgia-ReBORN - 16. System: Saved width (Compact)'); - pref.savedHeight_compact = window.GetProperty('Georgia-ReBORN - 16. System: Saved height (Compact)'); - this.setWindowSize(pref.savedWidth_compact, pref.savedHeight_compact); + if (grSet.layout === 'default') { + grSet.savedWidth_default = window.GetProperty('Georgia-ReBORN - 16. System: Saved width (Default)'); + grSet.savedHeight_default = window.GetProperty('Georgia-ReBORN - 16. System: Saved height (Default)'); + this.setWindowSize(grSet.savedWidth_default, grSet.savedHeight_default); + } else if (grSet.layout === 'artwork') { + grSet.savedWidth_artwork = window.GetProperty('Georgia-ReBORN - 16. System: Saved width (Artwork)'); + grSet.savedHeight_artwork = window.GetProperty('Georgia-ReBORN - 16. System: Saved height (Artwork)'); + this.setWindowSize(grSet.savedWidth_artwork, grSet.savedHeight_artwork); + } else if (grSet.layout === 'compact') { + grSet.savedWidth_compact = window.GetProperty('Georgia-ReBORN - 16. System: Saved width (Compact)'); + grSet.savedHeight_compact = window.GetProperty('Georgia-ReBORN - 16. System: Saved height (Compact)'); + this.setWindowSize(grSet.savedWidth_compact, grSet.savedHeight_compact); } } + // #endregion - // * PLAYER SIZE * // - + // * PUBLIC METHODS - PLAYER SIZE * // + // #region PUBLIC METHODS - PLAYER SIZE /** * Sets the player size based on active display resolution mode and layout. - * @param {object} size The playerSizes object containing width and height. + * @param {object} size - The playerSizes object containing width and height. */ setPlayerSize(size) { if (UIHacks.FullScreen) { UIHacks.FullScreen = false; } else if (this.fbHandle) { - pref.savedWidth_default = this.fbHandle.Width; - pref.savedHeight_default = this.fbHandle.Height; + grSet.savedWidth_default = this.fbHandle.Width; + grSet.savedHeight_default = this.fbHandle.Height; } if (size) this.setWindowSize(size.width, size.height); @@ -267,230 +253,231 @@ class Display { * Sets the Default layout -> Options > Layout > Default. */ layoutDefault() { - this.setPlayerSize(this.playerSize[pref.layout][pref.displayRes].small); + this.setPlayerSize(this.playerSize[grSet.layout][grSet.displayRes].small); } /** * Sets the Artwork layout -> Options > Layout > Artwork. */ layoutArtwork() { - this.setPlayerSize(this.playerSize[pref.layout][pref.displayRes].small); + this.setPlayerSize(this.playerSize[grSet.layout][grSet.displayRes].small); } /** * Sets the Compact layout -> Options > Layout > Compact. */ layoutCompact() { - this.setPlayerSize(this.playerSize[pref.layout][pref.displayRes].small); + this.setPlayerSize(this.playerSize[grSet.layout][grSet.displayRes].small); } /** * Sets the player size Small for 4K res -> Options > Player size > Small. */ playerSize_4K_small() { - this.setPlayerSize(this.playerSize[pref.layout]['4K'].small); + this.setPlayerSize(this.playerSize[grSet.layout]['4K'].small); } /** * Sets the player size Normal for 4K res -> Options > Player size > Normal. */ playerSize_4K_normal() { - this.setPlayerSize(this.playerSize[pref.layout]['4K'].normal); + this.setPlayerSize(this.playerSize[grSet.layout]['4K'].normal); } /** * Sets the player size Large for 4K res -> Options > Player size > Large. */ playerSize_4K_large() { - this.setPlayerSize(this.playerSize[pref.layout]['4K'].large); + this.setPlayerSize(this.playerSize[grSet.layout]['4K'].large); } /** * Sets the player size Small for QHD res -> Options > Player size > Small. */ playerSize_QHD_small() { - this.setPlayerSize(this.playerSize[pref.layout].QHD.small); + this.setPlayerSize(this.playerSize[grSet.layout].QHD.small); } /** * Sets the player size Normal for QHD res -> Options > Player size > Normal. */ playerSize_QHD_normal() { - this.setPlayerSize(this.playerSize[pref.layout].QHD.normal); + this.setPlayerSize(this.playerSize[grSet.layout].QHD.normal); } /** * Sets the player size Large for QHD res -> Options > Player size > Large. */ playerSize_QHD_large() { - this.setPlayerSize(this.playerSize[pref.layout].QHD.large); + this.setPlayerSize(this.playerSize[grSet.layout].QHD.large); } /** * Sets the player size Small for HD res -> Options > Player size > Small. */ playerSize_HD_small() { - this.setPlayerSize(this.playerSize[pref.layout].HD.small); + this.setPlayerSize(this.playerSize[grSet.layout].HD.small); } /** * Sets the player size Normal for HD res -> Options > Player size > Normal. */ playerSize_HD_normal() { - this.setPlayerSize(this.playerSize[pref.layout].HD.normal); + this.setPlayerSize(this.playerSize[grSet.layout].HD.normal); } /** * Sets the player size Large for HD res -> Options > Player size > Large. */ playerSize_HD_large() { - this.setPlayerSize(this.playerSize[pref.layout].HD.large); + this.setPlayerSize(this.playerSize[grSet.layout].HD.large); } + // #endregion - // * RESOLUTION SETTINGS * // - + // * PUBLIC METHODS - RESOLUTION SETTINGS * // + // #region PUBLIC METHODS - RESOLUTION SETTINGS /** * Sets the font and button sizes for 4K and HD display resolution. */ setSizesFor4KorHD() { - RES_QHD = false; + RES._QHD = false; // * Main - pref.menuFontSize_default = 12; - pref.menuFontSize_artwork = 12; - pref.menuFontSize_compact = 12; - pref.lowerBarFontSize_default = 18; - pref.lowerBarFontSize_artwork = 16; - pref.lowerBarFontSize_compact = 16; - pref.transportButtonSize_default = 32; - pref.transportButtonSize_artwork = 32; - pref.transportButtonSize_compact = 32; + grSet.menuFontSize_default = 12; + grSet.menuFontSize_artwork = 12; + grSet.menuFontSize_compact = 12; + grSet.lowerBarFontSize_default = 18; + grSet.lowerBarFontSize_artwork = 16; + grSet.lowerBarFontSize_compact = 16; + grSet.transportButtonSize_default = 32; + grSet.transportButtonSize_artwork = 32; + grSet.transportButtonSize_compact = 32; // * Details - pref.gridArtistFontSize_default = 18; - pref.gridArtistFontSize_artwork = 18; - pref.gridTrackNumFontSize_default = 18; - pref.gridTrackNumFontSize_artwork = 18; - pref.gridTitleFontSize_default = 18; - pref.gridTitleFontSize_artwork = 18; - pref.gridAlbumFontSize_default = 18; - pref.gridAlbumFontSize_artwork = 18; - pref.gridKeyFontSize_default = 17; - pref.gridKeyFontSize_artwork = 17; - pref.gridValueFontSize_default = 17; - pref.gridValueFontSize_artwork = 17; + grSet.gridArtistFontSize_default = 18; + grSet.gridArtistFontSize_artwork = 18; + grSet.gridTrackNumFontSize_default = 18; + grSet.gridTrackNumFontSize_artwork = 18; + grSet.gridTitleFontSize_default = 18; + grSet.gridTitleFontSize_artwork = 18; + grSet.gridAlbumFontSize_default = 18; + grSet.gridAlbumFontSize_artwork = 18; + grSet.gridKeyFontSize_default = 17; + grSet.gridKeyFontSize_artwork = 17; + grSet.gridValueFontSize_default = 17; + grSet.gridValueFontSize_artwork = 17; // * Playlist - pref.playlistHeaderFontSize_default = 15; - pref.playlistHeaderFontSize_artwork = 15; - pref.playlistHeaderFontSize_compact = 15; - pref.playlistFontSize_default = 12; - pref.playlistFontSize_artwork = 12; - pref.playlistFontSize_compact = 12; - - if (RES_4K) { - ppt.baseFontSize_default = 24; // * Library - ppt.baseFontSize_artwork = 24; // * Library - pptBio.baseFontSizeBio_default = 24; // * Biography - pptBio.baseFontSizeBio_artwork = 24; // * Biography + grSet.playlistHeaderFontSize_default = 15; + grSet.playlistHeaderFontSize_artwork = 15; + grSet.playlistHeaderFontSize_compact = 15; + grSet.playlistFontSize_default = 12; + grSet.playlistFontSize_artwork = 12; + grSet.playlistFontSize_compact = 12; + + if (RES._4K) { + libSet.baseFontSize_default = 24; // * Library + libSet.baseFontSize_artwork = 24; // * Library + bioSet.baseFontSizeBio_default = 24; // * Biography + bioSet.baseFontSizeBio_artwork = 24; // * Biography } else { - ppt.baseFontSize_default = 12; // * Library - ppt.baseFontSize_artwork = 12; // * Library - pptBio.baseFontSizeBio_default = 12; // * Biography - pptBio.baseFontSizeBio_artwork = 12; // * Biography + libSet.baseFontSize_default = 12; // * Library + libSet.baseFontSize_artwork = 12; // * Library + bioSet.baseFontSizeBio_default = 12; // * Biography + bioSet.baseFontSizeBio_artwork = 12; // * Biography } // * Lyrics - pref.lyricsFontSize_default = 20; - pref.lyricsFontSize_artwork = 20; + grSet.lyricsFontSize_default = 20; + grSet.lyricsFontSize_artwork = 20; } /** * Sets the font and button sizes for QHD display resolution. */ setSizesForQHD() { - RES_4K = false; - RES_QHD = true; - pref.playerSize_4K_normal = false; - pref.playerSize_QHD_small = true; - pref.playerSize_HD_small = false; + RES._4K = false; + RES._QHD = true; + grSet.playerSize_4K_normal = false; + grSet.playerSize_QHD_small = true; + grSet.playerSize_HD_small = false; // * Main - pref.menuFontSize_default = 14; - pref.menuFontSize_artwork = 14; - pref.menuFontSize_compact = 14; - pref.lowerBarFontSize_default = 20; - pref.lowerBarFontSize_artwork = 18; - pref.lowerBarFontSize_compact = 18; - pref.transportButtonSize_default = 34; - pref.transportButtonSize_artwork = 34; - pref.transportButtonSize_compact = 34; + grSet.menuFontSize_default = 14; + grSet.menuFontSize_artwork = 14; + grSet.menuFontSize_compact = 14; + grSet.lowerBarFontSize_default = 20; + grSet.lowerBarFontSize_artwork = 18; + grSet.lowerBarFontSize_compact = 18; + grSet.transportButtonSize_default = 34; + grSet.transportButtonSize_artwork = 34; + grSet.transportButtonSize_compact = 34; // * Details - pref.gridArtistFontSize_default = 20; - pref.gridArtistFontSize_artwork = 20; - pref.gridTrackNumFontSize_default = 20; - pref.gridTrackNumFontSize_artwork = 20; - pref.gridTitleFontSize_default = 20; - pref.gridTitleFontSize_artwork = 20; - pref.gridAlbumFontSize_default = 20; - pref.gridAlbumFontSize_artwork = 20; - pref.gridKeyFontSize_default = 19; - pref.gridKeyFontSize_artwork = 19; - pref.gridValueFontSize_default = 19; - pref.gridValueFontSize_artwork = 19; + grSet.gridArtistFontSize_default = 20; + grSet.gridArtistFontSize_artwork = 20; + grSet.gridTrackNumFontSize_default = 20; + grSet.gridTrackNumFontSize_artwork = 20; + grSet.gridTitleFontSize_default = 20; + grSet.gridTitleFontSize_artwork = 20; + grSet.gridAlbumFontSize_default = 20; + grSet.gridAlbumFontSize_artwork = 20; + grSet.gridKeyFontSize_default = 19; + grSet.gridKeyFontSize_artwork = 19; + grSet.gridValueFontSize_default = 19; + grSet.gridValueFontSize_artwork = 19; // * Playlist - pref.playlistHeaderFontSize_default = 17; - pref.playlistHeaderFontSize_artwork = 17; - pref.playlistHeaderFontSize_compact = 17; - pref.playlistFontSize_default = 14; - pref.playlistFontSize_artwork = 14; - pref.playlistFontSize_compact = 14; + grSet.playlistHeaderFontSize_default = 17; + grSet.playlistHeaderFontSize_artwork = 17; + grSet.playlistHeaderFontSize_compact = 17; + grSet.playlistFontSize_default = 14; + grSet.playlistFontSize_artwork = 14; + grSet.playlistFontSize_compact = 14; // * Library - ppt.baseFontSize_default = 14; - ppt.baseFontSize_artwork = 14; + libSet.baseFontSize_default = 14; + libSet.baseFontSize_artwork = 14; // * Biography - pptBio.baseFontSizeBio_default = 14; - pptBio.baseFontSizeBio_artwork = 14; + bioSet.baseFontSizeBio_default = 14; + bioSet.baseFontSizeBio_artwork = 14; // * Lyrics - pref.lyricsFontSize_default = 22; - pref.lyricsFontSize_artwork = 22; + grSet.lyricsFontSize_default = 22; + grSet.lyricsFontSize_artwork = 22; } + // #endregion - // * WINDOW CONTROL * // - + // * PUBLIC METHODS - WINDOW CONTROL * // + // #region PUBLIC METHODS - WINDOW CONTROL /** * Sets temporarily top menu caption to be able to drag foobar around. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ setWindowDrag(x, y) { - if (!componentUIHacks) return; // * Disable mouse middle btn (wheel) to be able to use Library & Biography mouse middle actions - UIHacks.MoveStyle = displayLibrary && mouseInLibrary(x, y) || displayBiography && mouseInBiography(x, y) ? 0 : 3; + UIHacks.MoveStyle = grm.ui.displayLibrary && mouseInLibrary(x, y) || grm.ui.displayBiography && mouseInBiography(x, y) ? 0 : 3; try { - if (mouseInControl || downButton) { + if (grm.button.mouseInControl || grm.button.downButton) { UIHacks.SetPseudoCaption(0, 0, 0, 0); if (UIHacks.FrameStyle === 3) UIHacks.DisableSizing = true; - pseudoCaption = false; + this.pseudoCaption = false; } - else if (!pseudoCaption || pseudoCaptionWidth !== ww) { - UIHacks.SetPseudoCaption(0, 0, ww, pref.layout !== 'default' ? geo.topMenuHeight + SCALE(5) : geo.topMenuHeight); - if (UIHacks.FrameStyle === 3 && !pref.lockPlayerSize) UIHacks.DisableSizing = false; - pseudoCaption = true; - pseudoCaptionWidth = ww; + else if (!this.pseudoCaption || this.pseudoCaptionWidth !== grm.ui.ww) { + UIHacks.SetPseudoCaption(0, 0, grm.ui.ww, grSet.layout !== 'default' ? grm.ui.topMenuHeight + SCALE(5) : grm.ui.topMenuHeight); + if (UIHacks.FrameStyle === 3 && !grSet.lockPlayerSize) UIHacks.DisableSizing = false; + this.pseudoCaption = true; + this.pseudoCaptionWidth = grm.ui.ww; } } catch (e) {} } /** * Sets the window size via UIHacks. - * @param {number} width The window width. - * @param {number} height The window height. + * @param {number} width - The window width. + * @param {number} height - The window height. */ setWindowSize(width, height) { // * Avoid resizing bugs, when the window is bigger\smaller than the saved one. @@ -509,24 +496,24 @@ class Display { /** * Sets and forces window size limits based on layout and display resolution, also fixes window size for messed up settings or properties. - * @return {boolean} Whether the window size needs to be fixed. + * @returns {boolean} Whether the window size needs to be fixed. */ setWindowSizeFix() { - this.setWindowSizeLimitsForLayouts(pref.layout); + this.setWindowSizeLimitsForLayouts(grSet.layout); const lastW = this.fbHandle ? this.fbHandle.Width : ''; const lastH = this.fbHandle ? this.fbHandle.Height : ''; - const systemFirstLaunch = !this.fbHandle ? pref.systemFirstLaunch : ''; + const systemFirstLaunch = !this.fbHandle ? grSet.systemFirstLaunch : ''; return this.fbHandle ? lastW !== this.fbHandle.Width || lastH !== this.fbHandle.Height : systemFirstLaunch; } /** * Sets the minimum and maximum sizes of the window based on different screen resolutions. - * @param {number} min_w The minimum width. - * @param {number} max_w The maximum width. - * @param {number} min_h The minimum height. - * @param {number} max_h The maximum height. + * @param {number} min_w - The minimum width. + * @param {number} max_w - The maximum width. + * @param {number} min_h - The minimum height. + * @param {number} max_h - The maximum height. */ setWindowSizeLimits(min_w, max_w, min_h, max_h) { UIHacks.MinSize.Enabled = !!min_w; @@ -548,14 +535,12 @@ class Display { setWindowSizeLimitsForLayouts() { let min_w = 0; const max_w = 0; let min_h = 0; const max_h = 0; - const size = this.playerSize[pref.layout][pref.displayRes]; + const size = this.playerSize[grSet.layout][grSet.displayRes]; min_w = size && size.small.width min_h = size && size.small.height; this.setWindowSizeLimits(min_w, max_w, min_h, max_h); } + // #endregion } - -/** @type {Object} Creates the Display handler object. */ -const display = new Display(); diff --git a/profile/georgia-reborn/scripts/Base/gr-helpers.js b/profile/georgia-reborn/scripts/Base/gr-helpers.js index bbe0c6ef..a633dd39 100644 --- a/profile/georgia-reborn/scripts/Base/gr-helpers.js +++ b/profile/georgia-reborn/scripts/Base/gr-helpers.js @@ -1,56 +1,25 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Main Helpers * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Main Helpers * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -///////////////////////// -// * ACTIVEX OBJECTS * // -///////////////////////// -/** @type {*} */ -const app = new ActiveXObject('Shell.Application'); -/** @type {*} */ -const doc = new ActiveXObject('htmlfile'); -/** @type {*} */ -const fso = new ActiveXObject('Scripting.FileSystemObject'); -/** @type {*} */ -const UIHacks = !utils.CheckComponent('foo_ui_hacks') || new ActiveXObject('UIHacks'); -/** @type {*} */ -const vb = new ActiveXObject('ScriptControl'); -/** @type {*} */ -const WshShell = new ActiveXObject('WScript.Shell'); - - -//////////////////// -// * COMPONENTS * // -//////////////////// -/** @type {*} Checks if the foo_chronflow component is installed. */ -const componentChronFlow = utils.CheckComponent('foo_chronflow') || utils.CheckComponent('foo_chronflow_mod'); -/** @type {*} Checks if the foo_enhanced_playcount component is installed. */ -const componentEnhancedPlaycount = utils.CheckComponent('foo_enhanced_playcount'); -/** @type {*} Checks if the foo_uie_eslyric component is installed. */ -const componentESLyric = utils.CheckComponent('foo_uie_eslyric'); -/** @type {*} Checks if the foo_ui_hacks component is installed. */ -const componentUIHacks = utils.CheckComponent('foo_ui_hacks'); -/** @type {*} Checks if the the foo_vis_vumeter component is installed. */ -const componentVUMeter = utils.CheckComponent('foo_vis_vumeter'); - - /////////////////////// // * COMPATIBILITY * // /////////////////////// /** * Detects if Internet Explorer is installed on the user's system by searching for specific file paths * in the Program Files directories of all available disk drives. Needed to render HTML popups. + * @global * @returns {boolean} True or false. */ function DetectIE() { @@ -67,6 +36,7 @@ function DetectIE() { /** * Detects if the user's system is running on Win x64 by looking for specific folders * in the Program Files (x86) directory on all available disk drives. Actually used only for x86 detection. + * @global * @returns {boolean} True or false. */ function DetectWin64() { @@ -83,7 +53,8 @@ function DetectWin64() { /** * Detects if the user's system is running Wine on Linux or MacOs by looking for specific folder * with the name 'bin' and files with the names 'bash', 'ls', 'sh', and 'fstab' on any partitions from A to Z. - * Default Wine mount point is Z:\ + * Default Wine mount point is Z:\. + * @global * @returns {boolean} True or false. */ function DetectWine() { @@ -103,8 +74,9 @@ function DetectWine() { /** * Compares two versions to determine if a version has changed. * Release must be in form of 2.0.0-beta1, or 2.0.1. - * @param {number|string=} oldVer The old version of the config file. - * @param {number|string=} newVer The new version of the config file. + * @global + * @param {number|string} oldVer - The old version of the config file. + * @param {number|string} newVer - The new version of the config file. * @returns {boolean} True if the `newVer` is newer. */ function IsNewerVersion(oldVer, newVer) { @@ -132,13 +104,13 @@ function IsNewerVersion(oldVer, newVer) { /** * Makes an HTTP request to a URL and calls a callback when the response is received. - * @param {string} type The type of request to make. Can be one of 'GET' 'POST' 'PUT' 'DELETE'. - * @param {string} url The URL to make the request. - * @param {function} successCB The callback function to call when the request is successful. - * @returns {boolean} True if source is valid. + * @global + * @param {string} type - The type of request to make. Can be one of 'GET' 'POST' 'PUT' 'DELETE'. + * @param {string} url - The URL to make the request. + * @param {Function} successCB - The callback function to call when the request is successful. + * @returns {void} */ function MakeHttpRequest(type, url, successCB) { - /** @type {*} */ const xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); xmlhttp.open(type, url, true); xmlhttp.setRequestHeader('User-Agent', 'foo_spider_monkey_georgia'); @@ -156,9 +128,11 @@ function MakeHttpRequest(type, url, successCB) { /////////////// /** * Asserts that a condition is true and throws an error if it is not. - * @param {boolean} predicate The condition to evaluate. - * @param {new (...args: any[]) => Error} ExceptionType The error constructor to instantiate if the condition is false. - * @param {...any} args Additional arguments to pass to the error constructor. + * @global + * @param {boolean} predicate - The condition to evaluate. + * @param {new (...args: any[]) => Error} ExceptionType - The error constructor to instantiate if the condition is false. + * @param {...any} args - Additional arguments to pass to the error constructor. + * @returns {void} * @throws {Error} Throws an error of the type specified by `ExceptionType` if `predicate` is false. */ function Assert(predicate, ExceptionType, ...args) { @@ -169,18 +143,20 @@ function Assert(predicate, ExceptionType, ...args) { /** * Prints exclusive theme debug logs and avoids cluttering the console constantly. * @type {function(...*):void} var_args - * @returns {string} The theme debug exclusive logs to console. + * @global + * @returns {void} */ function DebugLog() { - if (arguments.length && settings.showDebugLog) console.log(...arguments); + if (arguments.length && grCfg.settings.showDebugLog) console.log(...arguments); } /** * Prints a color object to the console. * This is primarily for debugging and for the benefit of other tools that rely on color objects. - * @param {Object} obj The object to print. - * @returns {string} The RGB and HEX values. + * @global + * @param {object} obj - The object to print. + * @returns {void} */ function PrintColorObj(obj) { console.log('\tname: \'\',\n\tcolors: {'); @@ -199,8 +175,9 @@ function PrintColorObj(obj) { /** * Gets the meta values of a specified metadata field from a given metadb object. * Will strip leading and trailing %'s from name. - * @param {string} name The name of the meta field. - * @param {FbMetadbHandle=} metadb The handle to evaluate string with. + * @global + * @param {string} name - The name of the meta field. + * @param {FbMetadbHandle} metadb - The handle to evaluate string with. * @returns {Array} An array of values of the meta field. */ function GetMetaValues(name, metadb = undefined) { @@ -224,7 +201,8 @@ function GetMetaValues(name, metadb = undefined) { /** * Parses a JSON string into a JavaScript object. - * @param {} value The string to parse. + * @global + * @param {string} value - The string to parse. * @returns {*} The parsed value or null if there was an error. */ function _JsonParse(value) { @@ -238,9 +216,10 @@ function _JsonParse(value) { /** * Parses a JSON file and returns a JavaScript object. - * @param {} file The file to parse. - * @param {number} codePage The code page of the JSON document. - * @returns {Object} The JSON object parsed from the file. + * @global + * @param {string} file - The file to parse. + * @param {number} codePage - The code page of the JSON document. + * @returns {object} The JSON object parsed from the file. */ function _JsonParseFile(file, codePage = 0) { return _JsonParse(Open(file, codePage)); @@ -249,10 +228,11 @@ function _JsonParseFile(file, codePage = 0) { /** * Parses JSON and returns an array of objects. If there is an error, the error is logged to the console. - * @param {} json The JSON to parse as JSON. - * @param {string} label A label to print before parsing the JSON. - * @param {boolean} log Whether to log the parsing or not. - * @returns {Object} The parsed JSON in an array of objects and returns an array of objects. + * @global + * @param {string} json - The JSON to parse as JSON. + * @param {string} label - A label to print before parsing the JSON. + * @param {boolean} log - Whether to log the parsing or not. + * @returns {object} The parsed JSON in an array of objects and returns an array of objects. */ function ParseJson(json, label, log) { let parsed = []; @@ -273,7 +253,8 @@ function ParseJson(json, label, log) { /** * Sanitizes a string to be safe for insertion into a JSON config. * Removes double quotes, backslashes, newlines and trailing spaces. - * @param {string} str The string to sanitize. + * @global + * @param {string} str - The string to sanitize. * @returns {string} The sanitized string. */ function SanitizeJsonString(str) { @@ -289,10 +270,12 @@ function SanitizeJsonString(str) { /** * Strips comments from a JSON string. - * https://github.com/sindresorhus/strip-json-comments/blob/master/index.js - * @param {string} jsonString The JSON string to strip comments from. - * @param {boolean} options Options to control how whitespace is stripped. + * Https://github.com/sindresorhus/strip-json-comments/blob/master/index.js. + * @global + * @param {string} jsonString - The JSON string to strip comments from. + * @param {object} options - The options to control how whitespace is stripped. * @returns {string} The stripped string. Note that the result may be empty. + * @throws {TypeError} If `jsonString` is not a string. */ function StripJsonComments(jsonString, options = { whitespace: false }) { if (typeof jsonString !== 'string') { @@ -380,24 +363,27 @@ function StripJsonComments(jsonString, options = { whitespace: false }) { ///////////////////////// /** * Creates a folder if it doesn't exist. - * @param {string} folder The folder to create. - * @param {boolean} is_recursive Whether to create the folder recursively. - * @returns {boolean} True if the folder was created. + * @global + * @param {string} folder - The folder to create. + * @param {boolean} is_recursive - Whether to create the folder recursively. + * @returns {boolean} - True if the folder was created or already exists, false on error. */ function CreateFolder(folder, is_recursive) { if (utils.IsDirectory(folder)) { - return; + return true; } + // If recursive creation is needed and the parent directory does not exist, create it if (is_recursive) { const parentPath = fso.GetParentFolderName(folder); - if (!utils.IsDirectory(parentPath)) { - CreateFolder(parentPath, true); + if (!utils.IsDirectory(parentPath) && !CreateFolder(parentPath, true)) { + return false; } } try { - return fso.CreateFolder(folder); + fso.CreateFolder(folder); + return true; } catch (e) { return false; } @@ -406,7 +392,8 @@ function CreateFolder(folder, is_recursive) { /** * Creates complete dir tree if needed up to the final folder. - * @param {string} folder The folder to create. + * @global + * @param {string} folder - The folder to create. * @returns {boolean} True if the folder was created. */ function _CreateFolder(folder) { @@ -429,8 +416,9 @@ function _CreateFolder(folder) { /** * Deletes a file from the file system. - * @param {string} file The file to delete. - * @param {boolean} force Whether to force the deletion even if the file doesn't exist. + * @global + * @param {string} file - The file to delete. + * @param {boolean} force - Whether to force the deletion even if the file doesn't exist. * @returns {boolean} True if the file was deleted. */ function _DeleteFile(file, force = true) { @@ -449,24 +437,28 @@ function _DeleteFile(file, force = true) { /** * Deletes a file. - * @param {string} file The file to delete. - * @returns {boolean} True if the file was successfully deleted. + * @global + * @param {string} file - The file to delete. + * @returns {boolean} True if the file was successfully deleted, false otherwise. */ function DeleteFile(file) { if (utils.IsFile(file)) { try { - return fso.DeleteFile(file); + fso.DeleteFile(file); + return true; } catch (e) { return false; } } + return false; } /** * Deletes a folder from FSO. - * @param {string} folder The folder to delete. - * @param {boolean} force Makes force deletion even if the folder is inaccessible. + * @global + * @param {string} folder - The folder to delete. + * @param {boolean} force - Makes force deletion even if the folder is inaccessible. * @returns {boolean} True if the folder was deleted. */ function DeleteFolder(folder, force = true) { @@ -486,7 +478,8 @@ function DeleteFolder(folder, force = true) { /** * Checks if a file exists. - * @param {string} filename The filename to check. + * @global + * @param {string} filename - The filename to check. * @returns {boolean} True if the file exists. */ function IsFile(filename) { @@ -500,7 +493,8 @@ function IsFile(filename) { /** * Checks if a folder exists. - * @param {string} folder The folder to check. + * @global + * @param {string} folder - The folder to check. * @returns {boolean} True if the folder exists. */ function IsFolder(folder) { @@ -514,8 +508,9 @@ function IsFolder(folder) { /** * Opens a file for reading. - * @param {string} file The file to open. - * @param {number} codePage The code page to open the file in. + * @global + * @param {string} file - The file to open. + * @param {number} codePage - The code page to open the file in. * @returns {string} The contents of the file or '' if the file doesn't exist. */ function Open(file, codePage = 0) { @@ -530,7 +525,8 @@ function Open(file, codePage = 0) { /** * Opens the Windows file explorer. - * @param {string} c Command "explorer /open" or "explorer /select" with file path. + * @global + * @param {string} c - Command "explorer /open" or "explorer /select" with file path. * @returns {*} True if Windows file explorer and file path exist. */ function OpenExplorer(c) { @@ -549,7 +545,8 @@ function OpenExplorer(c) { /** * Opens a file in VS Code if installed, otherwise in Notepad. - * @param {string} filePath The path to the file that you want to open. + * @global + * @param {string} filePath - The path to the file that you want to open. * @returns {*} True if the file exists. */ function OpenFile(filePath) { @@ -561,7 +558,8 @@ function OpenFile(filePath) { /** * Sanitizes illegal chars in the file path but skips drive. - * @param {string} value The value to sanitize. Must be a string of space - separated UTF-8 characters. + * @global + * @param {string} value - The value to sanitize. Must be a string of space - separated UTF-8 characters. * @returns {string} The sanitized path string. */ function SanitizePath(value) { @@ -573,9 +571,10 @@ function SanitizePath(value) { /** * Saves value to file, if file doesn't exist it will be created. - * @param {string} file The path to file to save to. - * @param {string} value The value to save to file. - * @param {boolean} bBOM Whether to write BOM or not. + * @global + * @param {string} file - The path to file to save to. + * @param {string} value - The value to save to file. + * @param {boolean} bBOM - Whether to write BOM or not. * @returns {boolean} True if saved or false with error. */ function Save(file, value, bBOM = false) { @@ -592,9 +591,10 @@ function Save(file, value, bBOM = false) { /** * Saves value to file, if file doesn't exist it will be created. - * @param {string} file The file to save to. - * @param {string} value The value to save to file. - * @param {boolean} bUTF16 True if value is UTF-16. + * @global + * @param {string} file - The file to save to. + * @param {string} value - The value to save to file. + * @param {boolean} bUTF16 - True if value is UTF-16. * @returns {boolean} True if file was saved. */ function SaveFSO(file, value, bUTF16) { @@ -619,8 +619,9 @@ function SaveFSO(file, value, bUTF16) { ///////////////// /** * Executes a given function with the provided arguments and returns the result or the error if an exception occurs. - * @param {function} func The function to execute. - * @param {...any} args Allows to pass any number of arguments to the `attempt` function. + * @global + * @param {Function} func - The function to execute. + * @param {...any} args - Allows to pass any number of arguments to the `attempt` function. * @returns {any|Error} The result of the function or the error. */ function Attempt(func, ...args) { @@ -634,10 +635,11 @@ function Attempt(func, ...args) { /** * Delays the execution of a function until a certain amount of time has passed, with an optional leading execution. - * @param {function} func The function to be debounced, will be called after the specified delay has passed. - * @param {number} delay The amount of time in milliseconds that the function should wait before executing. - * @param {boolean=} leading The debounced function will be invoked immediately with the current arguments and then regular debouncing. - * @returns {function} A new function that will execute the provided `func` after a specified `delay` has passed. + * @global + * @param {Function} func - The function to be debounced, will be called after the specified delay has passed. + * @param {number} delay - The amount of time in milliseconds that the function should wait before executing. + * @param {boolean} [leading] - The debounced function will be invoked immediately with the current arguments and then regular debouncing. + * @returns {Function} A new function that will execute the provided `func` after a specified `delay` has passed. */ function Debounce(func, delay, { leading } = {}) { let timerId; @@ -655,21 +657,25 @@ function Debounce(func, delay, { leading } = {}) { /** * Repeats the specified function a specified number of times. - * @param {Function} func The function to be repeated. - * @param {number} times The number of times to repeat the function. - * @returns {Function} The repeated function calls. + * @global + * @param {Function} func - The function to be repeated. + * @param {number} times - The number of times to repeat the function. + * @returns {void} */ function RepeatFunc(func, times) { - func(); - if (times && --times) RepeatFunc(func, times); + if (times > 0) { + func(); + RepeatFunc(func, times - 1); + } } /** * Runs a Windows command prompt and returns false if it fails to run. - * @param {string} command The command for the OS to execute. Typically a webpage, or a path to an executable. - * @param {boolean=} wait Whether to wait for the command to finish. - * @param {boolean=} show Whether to show the command prompt window. + * @global + * @param {string} command - The command for the OS to execute. Typically a webpage, or a path to an executable. + * @param {boolean} [wait] - Whether to wait for the command to finish. + * @param {boolean} [show] - Whether to show the command prompt window. * @returns {boolean} True if the command was successfully executed. */ function RunCmd(command, wait, show) { @@ -686,7 +692,8 @@ function RunCmd(command, wait, show) { /** * Creates a function that can only be called once. * The result of the first call is cached and returned on subsequent calls. - * @param {Function} fn A function to be called only once. + * @global + * @param {Function} fn - A function to be called only once. * @returns {Function} A new function that wraps the `fn` function, caching and returning the result of the first invocation. */ function Once(fn) { @@ -704,8 +711,9 @@ function Once(fn) { /** * Limits the execution of a given function to a specified time frame. - * @param {Function} func The function to be throttled, will be called after the specified time frame has passed since the last invocation. - * @param {number} timeFrame The minimum time interval in milliseconds between function calls. It determines how often the `func` function can be called. + * @global + * @param {Function} func - The function to be throttled, will be called after the specified time frame has passed since the last invocation. + * @param {number} timeFrame - The minimum time interval in milliseconds between function calls. It determines how often the `func` function can be called. * @returns {Function} A new function that will execute only if the time elapsed since the last execution is greater than or equal to the specified timeFrame. */ function Throttle(func, timeFrame) { @@ -722,11 +730,12 @@ function Throttle(func, timeFrame) { /** * Limits the execution of a given function to a specified delay, with an optional immediate execution. - * @param {function} fn The function to be throttled, it is the function that will be called after the specified delay. - * @param {number} delay The time in milliseconds that specifies how long to wait between function invocations. - * @param {boolean} immediate Whether the function should be executed immediately or after the delay. - * @param {object} parent The parent object to bind the function to. - * @returns {function} A throttled version of the original function. + * @global + * @param {Function} fn - The function to be throttled, it is the function that will be called after the specified delay. + * @param {number} delay - The time in milliseconds that specifies how long to wait between function invocations. + * @param {boolean} immediate - Whether the function should be executed immediately or after the delay. + * @param {object} parent - The parent object to bind the function to. + * @returns {Function} A throttled version of the original function. */ function _Throttle(fn, delay, immediate = false, parent = this) { let timerId; @@ -751,8 +760,9 @@ function _Throttle(fn, delay, immediate = false, parent = this) { /** * Creates a function that will try to call the given method on the given object, but will not throw an error if the method does not exist. * It's used for methods that don't have a parent to avoid infinite recursion. - * @param {string} fn The name of the method to call. - * @param {Object} parent The object on which to call the method. + * @global + * @param {string} fn - The name of the method to call. + * @param {object} parent - The object on which to call the method. * @returns {Function} The function that will try to call the method. */ function TryMethod(fn, parent) { @@ -769,9 +779,10 @@ function TryMethod(fn, parent) { //////////////// /** * Converts RGB values to a 32 bit integer. - * @param {number} r The red channel value in the range of 0-255. - * @param {number} g The green channel value in the range of 0-255. - * @param {number} b The blue channel value in the range of 0-255. + * @global + * @param {number} r - The red channel value in the range of 0-255. + * @param {number} g - The green channel value in the range of 0-255. + * @param {number} b - The blue channel value in the range of 0-255. * @returns {number} The RGB value as a 32 bit integer. */ function RGB(r, g, b) { @@ -781,10 +792,11 @@ function RGB(r, g, b) { /** * Converts RGBA values to a 32 bit integer. - * @param {number} r The red channel value in the range of 0-255. - * @param {number} g The green channel value in the range of 0-255. - * @param {number} b The blue channel value in the range of 0-255. - * @param {number} a The alpha channel value in the range of 0-255. + * @global + * @param {number} r - The red channel value in the range of 0-255. + * @param {number} g - The green channel value in the range of 0-255. + * @param {number} b - The blue channel value in the range of 0-255. + * @param {number} a - The alpha channel value in the range of 0-255. * @returns {number} The RGBA value as a 32 bit integer. */ function RGBA(r, g, b, a) { @@ -794,8 +806,9 @@ function RGBA(r, g, b, a) { /** * Converts RGB to RGBA. - * @param {number} rgb The RGB triplet to convert. - * @param {number} a The alpha value of the color. + * @global + * @param {number} rgb - The RGB triplet to convert. + * @param {number} a - The alpha value of the color. * @returns {number} The RGBA triplet converted to the specified alpha value. */ function RGBtoRGBA(rgb, a) { @@ -805,8 +818,9 @@ function RGBtoRGBA(rgb, a) { /** * Converts RGBA to RGB. - * @param {number} rgb The RGB value to convert. - * @param {number} a The alpha value to convert. + * @global + * @param {number} rgb - The RGB value to convert. + * @param {number} a - The alpha value to convert. * @returns {number} The RGBA value. */ function RGBAtoRGB(rgb, a) { @@ -816,9 +830,10 @@ function RGBAtoRGB(rgb, a) { /** * Converts RGB to HEX. - * @param {number} r The red channel value to convert. - * @param {number} g The green channel value to convert. - * @param {number} b The blue channel value to convert. + * @global + * @param {number} r - The red channel value to convert. + * @param {number} g - The green channel value to convert. + * @param {number} b - The blue channel value to convert. * @returns {string} The hex representation of the RGB value. */ function RGBtoHEX(r, g, b) { @@ -831,7 +846,8 @@ function RGBtoHEX(r, g, b) { /** * Converts full RGB color value to HEX. - * @param {number} rgb The RGB value to convert. + * @global + * @param {number} rgb - The RGB value to convert. * @returns {string} The HEX value of the RGB value. */ function RGBFtoHEX(rgb) { @@ -846,11 +862,12 @@ function RGBFtoHEX(rgb) { /** - * Converts RGBA to HEX - * @param {number} r The red channel value in the range of 0-255. - * @param {number} g The green channel value in the range of 0-255. - * @param {number} b The blue channel value in the range of 0-255. - * @param {number} a The alpha channel value in the range of 0-255. + * Converts RGBA to HEX. + * @global + * @param {number} r - The red channel value in the range of 0-255. + * @param {number} g - The green channel value in the range of 0-255. + * @param {number} b - The blue channel value in the range of 0-255. + * @param {number} a - The alpha channel value in the range of 0-255. * @returns {string} The RGB triplet as a HEX. */ function RGBAtoHEX(r, g, b, a) { @@ -864,7 +881,8 @@ function RGBAtoHEX(r, g, b, a) { /** * Converts a hexadecimal string to decimal. - * @param {string} hex The hexadecimal string to convert. + * @global + * @param {string} hex - The hexadecimal string to convert. * @returns {number} The decimal equivalent. */ function HEX(hex) { @@ -874,7 +892,8 @@ function HEX(hex) { /** * Converts a hex string to RGB. - * @param {string} hex The hex string to convert. + * @global + * @param {string} hex - The hex string to convert. * @returns {number} The RGB in the hex string. */ function HEXtoRGB(hex) { @@ -887,8 +906,9 @@ function HEXtoRGB(hex) { /** * Converts a hex string to RGBA. - * @param {string} hex The hex string to convert. - * @param {number} a The alpha value of the color. + * @global + * @param {string} hex - The hex string to convert. + * @param {number} a - The alpha value of the color. * @returns {number} The hex string to RGBA. */ function HEXtoRGBA(hex, a) { @@ -901,7 +921,8 @@ function HEXtoRGBA(hex, a) { /** * Checks if a string is a valid hexadecimal number. - * @param {string} hex The string to check. + * @global + * @param {string} hex - The string to check. * @returns {boolean} Whether the string is a valid hexadecimal number. */ function IsHEX(hex) { @@ -911,7 +932,8 @@ function IsHEX(hex) { /** * Gets the alpha value of a color. - * @param {number} color The RGB color value with or without alpha channel. + * @global + * @param {number} color - The RGB color value with or without alpha channel. * @returns {number} The alpha value of the color. */ function GetAlpha(color) { @@ -921,9 +943,10 @@ function GetAlpha(color) { /** * Returns a blended color based on blend factor. - * @param {number} c1 The color to blend with c2. - * @param {number} c2 The color to blend with c1. - * @param {number} f The blend factor from 0-1. + * @global + * @param {number} c1 - The color to blend with c2. + * @param {number} c2 - The color to blend with c1. + * @param {number} f - The blend factor from 0-1. * @returns {number} The blended color as RGBA. */ function GetBlend(c1, c2, f) { @@ -940,7 +963,8 @@ function GetBlend(c1, c2, f) { /** * Returns the red value of a color. - * @param {number} color The color value to convert, must be in the range of 0-255. + * @global + * @param {number} color - The color value to convert, must be in the range of 0-255. * @returns {number} The red value of a color. */ function GetRed(color) { @@ -950,7 +974,8 @@ function GetRed(color) { /** * Returns the green value of a color. - * @param {number} color The color value to convert, must be in the range of 0-255. + * @global + * @param {number} color - The color value to convert, must be in the range of 0-255. * @returns {number} The green value of a color. */ function GetGreen(color) { @@ -960,7 +985,8 @@ function GetGreen(color) { /** * Returns the blue value of a color. - * @param {number} color The color value to convert, must be in the range of 0-255. + * @global + * @param {number} color - The color value to convert, must be in the range of 0-255. * @returns {number} The blue value of a color. */ function GetBlue(color) { @@ -970,7 +996,8 @@ function GetBlue(color) { /** * Converts a color value to RGB. - * @param {number} c The color value to convert. + * @global + * @param {number} c - The color value to convert. * @returns {Array} The RGB value of the color. */ function ToRGB(c) { @@ -980,7 +1007,8 @@ function ToRGB(c) { /** * Converts a color value to RGBA. - * @param {number} c The color value to convert. + * @global + * @param {number} c - The color value to convert. * @returns {Array} The RGBA value of the color. */ function ToRGBA(c) { @@ -990,7 +1018,8 @@ function ToRGBA(c) { /** * Calculates the brightness of a color. - * @param {number} c The color to calculate the brightness of, must be in the range of 0-255. + * @global + * @param {number} c - The color to calculate the brightness of, must be in the range of 0-255. * @returns {number} The brightness of the color in the range of 0-255. */ function CalcBrightness(c) { @@ -1003,7 +1032,8 @@ function CalcBrightness(c) { /** * Calculates the average brightness of an image. - * @param {GdiBitmap} image The image to calculate brightness for. + * @global + * @param {GdiBitmap} image - The image to calculate brightness for. * @returns {number} The average brightness of the image. */ function CalcImgBrightness(image) { @@ -1029,11 +1059,12 @@ function CalcImgBrightness(image) { Clamp(Math.round(Math.sqrt(bTot / freqTot)), 0, 255) ] / 3); - if (settings.showDebugThemeLog) console.log('Image brightness:', avgCol); + if (grCfg.settings.showDebugThemeLog) console.log('Image brightness:', avgCol); return avgCol; } catch (e) { console.log('\n\n'); + return 0; } } @@ -1043,9 +1074,11 @@ function CalcImgBrightness(image) { * Currently uses the redmean calculation from https://en.wikipedia.org/wiki/Color_difference. * The purpose of this method is mostly to determine whether a color drawn next to another color will * provide enough visual separation. As such, adding some additional weighting based on individual colors differences. - * @param {number} a The first color in numeric form (i.e. RGB(150,250,255)). - * @param {number} b The second color in numeric form (i.e. RGB(150,250,255)). - * @param {boolean=} log Whether to print the distance in the console. Also requires that settings.showDebugThemeLog is true. + * @global + * @param {number} a - The first color in numeric form (i.e. RGB(150,250,255)). + * @param {number} b - The second color in numeric form (i.e. RGB(150,250,255)). + * @param {boolean} [log] - Whether to print the distance in the console. Also requires that settings.showDebugThemeLog is true. + * @returns {number} The color distance as a numeric value. */ function ColorDistance(a, b, log) { const aCol = new Color(a); @@ -1066,7 +1099,7 @@ function ColorDistance(a, b, log) { // then it's very likely there will be enough visual separation between the two, so bump up the diff percentage. distance *= 1.1; } - if (log && settings.showDebugThemeLog) { + if (log && grCfg.settings.showDebugThemeLog) { console.log('Distance from:', aCol.getRGB(), 'to:', bCol.getRGB(), '=', distance); } return distance; @@ -1075,8 +1108,9 @@ function ColorDistance(a, b, log) { /** * Converts a color to RGB. If the alpha is less than 255, it will be converted to RGBA. - * @param {number} c The color to convert. - * @param {boolean} showPrefix Whether to include the "rgb" or "rgba" prefix. + * @global + * @param {number} c - The color to convert. + * @param {boolean} showPrefix - Whether to include the "rgb" or "rgba" prefix. * @returns {string} The color in RGB format. */ function ColToRgb(c, showPrefix) { @@ -1093,9 +1127,30 @@ function ColToRgb(c, showPrefix) { } +/** + * Converts a color string in hexadecimal or RGB format to an RGB integer value. + * The function supports colors in hexadecimal format (#FFFFFF) and RGB format (rgb(255,255,255)). + * @global + * @param {string} colorStr - The color string to convert. + * @returns {number} The corresponding RGB value as an integer, or 0xff000000 if the string cannot be parsed. + */ +function ColStringToRGB(colorStr) { + // If the color is in hex format + if (colorStr.startsWith('#')) { + return parseInt(colorStr.slice(1), 16); + } + // If the color is in rgb format + const rgb = colorStr.match(/\(\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/); + if (rgb) return RGB(parseInt(rgb[1]), parseInt(rgb[2]), parseInt(rgb[3])); + + return 0xff000000; +} + + /** * Converts a color object to HSL. - * @param {number} col The color object with hue, saturation and lightness properties. + * @global + * @param {number} col - The color object with hue, saturation and lightness properties. * @returns {string} HSL representation of the color. */ function ColorToHSLString(col) { @@ -1105,9 +1160,10 @@ function ColorToHSLString(col) { /** * Combines two colors based on a fraction. The fraction should be between 0 and 1. - * @param {number} c1 The first color to combine. This can be an array of [red, green, blue] values or a color object. - * @param {number} c2 The second color to combine. This can be an array of [red, green, blue] values or a color object. - * @param {number} f The fraction of the colors to combine. 0 means c1 is the same as c2 100% means c2 is the same as c1. + * @global + * @param {number} c1 - The first color to combine. This can be an array of [red, green, blue] values or a color object. + * @param {number} c2 - The second color to combine. This can be an array of [red, green, blue] values or a color object. + * @param {number} f - The fraction of the colors to combine. 0 means c1 is the same as c2 100% means c2 is the same as c1. * @returns {number} When f is 0, result is 100% color1. When f is 1, result is 100% color2. */ function CombineColors(c1, c2, f) { @@ -1123,11 +1179,11 @@ function CombineColors(c1, c2, f) { /** -* Darkens a color value based on a percentage. -* @param {number} color The color to darken. -* @param {number} percent The percentage of the color to darken. -* @returns {number} The darkened color value in the range of 0-255. -*/ + * Darkens a color value based on a percentage. + * @param {number} color - The color to darken. + * @param {number} percent - The percentage of the color to darken. + * @returns {number} The darkened color value in the range of 0-255. + */ function DarkenColorVal(color, percent) { const shift = Math.max(color * percent / 100, percent / 2); const val = Math.round(color - shift); @@ -1137,8 +1193,9 @@ function DarkenColorVal(color, percent) { /** * Lightens a color value based on a percentage. - * @param {number} color The color to lighten. - * @param {number} percent The percentage of the color to lighten. + * @global + * @param {number} color - The color to lighten. + * @param {number} percent - The percentage of the color to lighten. * @returns {number} The lightened color value in the range of 0-255. */ function LightenColorVal(color, percent) { @@ -1149,8 +1206,9 @@ function LightenColorVal(color, percent) { /** * Shades a color by a certain percentage. - * @param {number} color The color to shade. - * @param {number} percent The percentage to shade the color by. + * @global + * @param {number} color - The color to shade. + * @param {number} percent - The percentage to shade the color by. * @returns {number} Returns the shaded color. */ function ShadeColor(color, percent) { @@ -1164,8 +1222,9 @@ function ShadeColor(color, percent) { /** * Tints a color by a certain percentage. - * @param {number} color The color to tint. - * @param {number} percent The percentage to tint the color by. + * @global + * @param {number} color - The color to tint. + * @param {number} percent - The percentage to tint the color by. * @returns {number} Returns the tinted color. */ function TintColor(color, percent) { @@ -1181,14 +1240,17 @@ function TintColor(color, percent) { // * GRAPHICS * // ////////////////// /** - * The ImageSize represents the position and dimensions of an image. + * A class that represents the position and dimensions of an image. + * @global */ class ImageSize { /** - * @param {number} x The x-coordinate of the image. - * @param {number} y The y-coordinate of the image. - * @param {number} w The width of the image. - * @param {number} h The height of the image. + * Creates the `ImageSize` instance. + * Initializes the size and position of the image. + * @param {number} x - The x-coordinate of the image. + * @param {number} y - The y-coordinate of the image. + * @param {number} w - The width of the image. + * @param {number} h - The height of the image. */ constructor(x, y, w, h) { this.x = x; @@ -1199,16 +1261,82 @@ class ImageSize { } +/** + * A class that encapsulates the creation and management of GDI image and graphics objects. + * It ensures that only one instance of each object exists within the application at a time (singleton pattern). + * @example + * Use GdiService.getInstance() to access the singleton instance of this service throughout the application: + * const gdiService = GdiService.getInstance(); + * const g = gdiService.getGraphics(); + */ +class GdiService { + /** + * Creates the `GdiService` instance. + * Initializes a new instance of the GdiService class. + */ + constructor() { + /** @private @type {?GdiBitmap} */ + this.image = null; + /** @private @type {?GdiGraphics} */ + this.graphics = null; + } + + /** + * Gets the GDI image object, creating it if it doesn't exist. + * @returns {GdiBitmap} The GDI image object. + */ + getImage() { + if (!this.image) { + this.image = gdi.CreateImage(1, 1); + } + return this.image; + } + + /** + * Gets the GDI graphics object, creating it if it doesn't exist. + * @returns {GdiGraphics} The GDI graphics object. + */ + getGraphics() { + if (!this.graphics) { + const image = this.getImage(); + this.graphics = image.GetGraphics(); + } + return this.graphics; + } + + /** + * Releases the graphics object associated with the GDI image. + * @param {GdiGraphics} graphics - The GDI graphics object to release. + */ + releaseGraphics(graphics) { + const image = this.getImage(); + image.ReleaseGraphics(graphics); + } + + /** + * Retrieves the singleton instance of the GdiService class, creating it if it doesn't exist. + * @returns {GdiService} The singleton instance of the GdiService class. + */ + static getInstance() { + if (!GdiService.instance) { + GdiService.instance = new GdiService(); + } + return GdiService.instance; + } +} + + /** * Creates a GDI graphics object. - * @param {number} w The width of the graphics object - * @param {number} h The height of the graphics object. - * @param {boolean} im Is the graphics type an image (true) or a text object (false). - * @param {function} func The function to call the graphics object. + * @global + * @param {number} w - The width of the graphics object. + * @param {number} h - The height of the graphics object. + * @param {boolean} im - Is the graphics type an image (true) or a text object (false). + * @param {Function} func - The function to call the graphics object. * @returns {GdiGraphics|null} The created or recycled GDI graphics object. */ -function GR(w, h, im, func) { - if (isNaN(w) || isNaN(h)) return; +function GDI(w, h, im, func) { + if (isNaN(w) || isNaN(h)) return null; const i = gdi.CreateImage(Math.max(w, 2), Math.max(h, 2)); let g = i.GetGraphics(); func(g, i); @@ -1220,46 +1348,48 @@ function GR(w, h, im, func) { /** * Crops an image with optional width and/or height. - * @param {GdiBitmap} image The image to crop. - * @param {number} [cropWidth=0] The width to crop from the image. If 0, no width cropping is performed. - * @param {number} [cropHeight=0] The height to crop from the image. If 0, no height cropping is performed. + * @global + * @param {GdiBitmap} image - The image to crop. + * @param {number} [cropWidth] - The width to crop from the image. If 0, no width cropping is performed. + * @param {number} [cropHeight] - The height to crop from the image. If 0, no height cropping is performed. * @returns {GdiBitmap} The cropped image. */ function CropImage(image, cropWidth = 0, cropHeight = 0) { - const maskWidth = cropWidth ? image.Width - cropWidth : image.Width; - const maskHeight = cropHeight ? image.Height - cropHeight : image.Height; - const maskX = cropWidth / 2; - const maskY = cropHeight / 2; - - // * Mask - const maskImg = gdi.CreateImage(maskWidth, maskHeight); - let g = maskImg.GetGraphics(); - g.FillSolidRect(0, 0, maskWidth, maskHeight, 0xff000000); - maskImg.ReleaseGraphics(g); - - // * Canvas with cropped image - const croppedImg = gdi.CreateImage(maskWidth, maskHeight); - g = croppedImg.GetGraphics(); + const maskWidth = cropWidth ? image.Width - cropWidth : image.Width; + const maskHeight = cropHeight ? image.Height - cropHeight : image.Height; + const maskX = cropWidth / 2; + const maskY = cropHeight / 2; + + // * Mask + const maskImg = gdi.CreateImage(maskWidth, maskHeight); + let g = maskImg.GetGraphics(); + g.FillSolidRect(0, 0, maskWidth, maskHeight, 0xff000000); + maskImg.ReleaseGraphics(g); + + // * Canvas with cropped image + const croppedImg = gdi.CreateImage(maskWidth, maskHeight); + g = croppedImg.GetGraphics(); g.SetSmoothingMode(SmoothingMode.None); - g.DrawImage(image, 0, 0, maskWidth, maskHeight, cropWidth ? maskX : 0, cropHeight ? maskY : 0, maskWidth, maskHeight); - croppedImg.ReleaseGraphics(g); - croppedImg.ApplyMask(maskImg); + g.DrawImage(image, 0, 0, maskWidth, maskHeight, cropWidth ? maskX : 0, cropHeight ? maskY : 0, maskWidth, maskHeight); + croppedImg.ReleaseGraphics(g); + croppedImg.ApplyMask(maskImg); - return croppedImg; + return croppedImg; } /** * Creates a blended filled round rectangle, a custom method not implemented in SMP. - * @param {GdiGraphics} gr - * @param {number} x The x-coordinate of the rectangle. - * @param {number} y The y-coordinate of the rectangle. - * @param {number} w The width of the rectangle. - * @param {number} h The height of the rectangle. - * @param {number} arc_width The width of the arcs. - * @param {number} arc_height The height of the arcs. - * @param {float=} angle The angle of the arc in degrees. - * @param {float=} focus The focus where the centered color will be at its highest intensity. Values 0 or 1. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} x - The x-coordinate of the rectangle. + * @param {number} y - The y-coordinate of the rectangle. + * @param {number} w - The width of the rectangle. + * @param {number} h - The height of the rectangle. + * @param {number} arc_width - The width of the arcs. + * @param {number} arc_height - The height of the arcs. + * @param {number} [angle] - The angle of the arc in degrees. + * @param {number} [focus] - The focus where the centered color will be at its highest intensity. Values 0 or 1. * @returns {GdiGraphics} The blended filled round rectangle. */ function FillBlendedRoundRect(gr, x, y, w, h, arc_width, arc_height, angle, focus) { @@ -1274,27 +1404,28 @@ function FillBlendedRoundRect(gr, x, y, w, h, arc_width, arc_height, angle, focu // * Blended rect const gradRectImg = gdi.CreateImage(w + SCALE(1), h + SCALE(1)); g = gradRectImg.GetGraphics(); - g.DrawImage(blendedImg, 0, 0, w - SCALE(1), h - SCALE(1), 0, h, blendedImg.Width, blendedImg.Height); + g.DrawImage(grCol.imgBlended, 0, 0, w - SCALE(1), h - SCALE(1), 0, h, grCol.imgBlended.Width, grCol.imgBlended.Height); gradRectImg.ReleaseGraphics(g); const mask = maskImg.Resize(w + SCALE(1), h + SCALE(1)); gradRectImg.ApplyMask(mask); - gr.DrawImage(gradRectImg, x, y, w - SCALE(1), h - SCALE(1), 0, 0, w, h, 0, 255); + return gr.DrawImage(gradRectImg, x, y, w - SCALE(1), h - SCALE(1), 0, 0, w, h, 0, 255); } /** * Creates a gradient filled ellipse, a custom method not implemented in SMP. - * @param {GdiGraphics} gr - * @param {number} x The X-coordinate of the ellipse. - * @param {number} y The Y-coordinate of the ellipse. - * @param {number} w The width of the ellipse. - * @param {number} h The height of the ellipse. - * @param {float=} angle The angle of the ellipse in degrees. - * @param {number} color1 The color of the top side of the ellipse. - * @param {number} color2 The color of the bottom side of the ellipse. - * @param {float=} focus The focus where the centered color will be at its highest intensity. Values 0 or 1. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} x - The X-coordinate of the ellipse. + * @param {number} y - The Y-coordinate of the ellipse. + * @param {number} w - The width of the ellipse. + * @param {number} h - The height of the ellipse. + * @param {number} angle - The angle of the ellipse in degrees. + * @param {number} color1 - The color of the top side of the ellipse. + * @param {number} color2 - The color of the bottom side of the ellipse. + * @param {number} focus - The focus where the centered color will be at its highest intensity. Values 0 or 1. * @returns {GdiGraphics} The gradient filled ellipse. */ function FillGradEllipse(gr, x, y, w, h, angle, color1, color2, focus) { @@ -1316,23 +1447,24 @@ function FillGradEllipse(gr, x, y, w, h, angle, color1, color2, focus) { const mask = maskImg.Resize(w + SCALE(1), h + SCALE(1)); gradEllipseImg.ApplyMask(mask); - gr.DrawImage(gradEllipseImg, x, y, w - SCALE(1), h - SCALE(1), 0, 0, w, h, 0, 255); + return gr.DrawImage(gradEllipseImg, x, y, w - SCALE(1), h - SCALE(1), 0, 0, w, h, 0, 255); } /** * Creates a gradient filled round rectangle, a custom method not implemented in SMP. - * @param {GdiGraphics} gr - * @param {number} x The X-coordinate of the rectangle. - * @param {number} y The Y-coordinate of the rectangle. - * @param {number} w The width of the rectangle. - * @param {number} h The height of the rectangle. - * @param {number} arc_width The width of the arcs. - * @param {number} arc_height The height of the arcs. - * @param {float=} angle The angle of the arc in degrees. - * @param {number} color1 The color of the top side of the gradient. - * @param {number} color2 The color of the bottom side of the gradient. - * @param {float=} focus The focus where the centered color will be at its highest intensity. Values 0 or 1. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} x - The X-coordinate of the rectangle. + * @param {number} y - The Y-coordinate of the rectangle. + * @param {number} w - The width of the rectangle. + * @param {number} h - The height of the rectangle. + * @param {number} arc_width - The width of the arcs. + * @param {number} arc_height - The height of the arcs. + * @param {number} angle - The angle of the arc in degrees. + * @param {number} color1 - The color of the top side of the gradient. + * @param {number} color2 - The color of the bottom side of the gradient. + * @param {number} focus - The focus where the centered color will be at its highest intensity. Values 0 or 1. * @returns {GdiGraphics} The gradient filled round rectangle. */ function FillGradRoundRect(gr, x, y, w, h, arc_width, arc_height, angle, color1, color2, focus) { @@ -1353,25 +1485,28 @@ function FillGradRoundRect(gr, x, y, w, h, arc_width, arc_height, angle, color1, const mask = maskImg.Resize(w + SCALE(1), h + SCALE(1)); gradRectImg.ApplyMask(mask); - gr.DrawImage(gradRectImg, x, y, w - SCALE(1), h - SCALE(1), 0, 0, w, h, 0, 255); + return gr.DrawImage(gradRectImg, x, y, w - SCALE(1), h - SCALE(1), 0, 0, w, h, 0, 255); } /** * Creates a rotated image, mostly used for disc art. - * @param {GdiBitmap} img The source image. - * @param {number} w The width of image. - * @param {number} h The height of image. - * @param {number=} degrees The degrees are clockwise. - * @returns {GdiBitmap} The rotated image. + * @global + * @param {GdiBitmap} img - The source image. + * @param {number} w - The width of image. + * @param {number} h - The height of image. + * @param {number} [degrees] - The degrees are clockwise. + * @returns {GdiBitmap|null} The rotated image or null if an error occurs. */ function RotateImg(img, w, h, degrees) { + if (!img || w <= 0 || h <= 0) return null; + /** * Because foobar x86 can allocate only 4 gigs memory, we must limit disc art res for 4K when using * high pref.spinDiscArtImageCount, i.e 90 (4 degrees), 120 (3 degrees), 180 (2 degrees) to prevent crash. * When SMP has x64 support, we could try to increase this limit w (1836px max possible res for 4K). */ - const imgMaxRes = ({ 90: 1400, 120: 1200, 180: 1000 })[pref.spinDiscArtImageCount] || w; + const imgMaxRes = ({ 90: 1400, 120: 1200, 180: 1000 })[grSet.spinDiscArtImageCount] || w; w = Math.min(w, imgMaxRes); h = Math.min(h, imgMaxRes); @@ -1383,18 +1518,21 @@ function RotateImg(img, w, h, degrees) { rotatedImg.ReleaseGraphics(gotGraphics); return rotatedImg; } + return img.Clone(0, 0, img.Width, img.Height).Resize(w, h); } /** * Creates a drop shadow rectangle. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} radius The shadow radius. - * @param {number} color The shadow color. + * @global + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} radius - The shadow radius. + * @param {number} color - The shadow color. + * @returns {GdiBitmap} The image object with the applied drop shadow. */ function ShadowRect(x, y, w, h, radius, color) { const shadow = gdi.CreateImage(w + 2 * radius, h + 2 * radius); @@ -1407,17 +1545,55 @@ function ShadowRect(x, y, w, h, radius, color) { } +///////////////// +// * DISPLAY * // +///////////////// +/** + * Scales the value based on 4K mode or not. + * @global + * @param {number} val - The value that needs to be scaled for 4K resolution. + * @returns {number} The value doubled. + */ +function SCALE(val) { + return RES._4K ? val * 2 : val; +} + + +/** + * NOT USED AT THE MOMENT. + * Converts a size value from points to pixels using the DPI value. + * @global + * @param {number} size - The size in pixels. + * @returns {number} The size in points. + */ +function Scale(size) { + /** + * Checks and sets the actual display DPI number via reading the Windows registry value. + * @type {number} Default display dots per inch setting. + */ + const DPI = (() => { + try { + return WshShell.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI'); + } catch (e) { + return 96; + } + })(); + return Math.round(size * DPI / 72); +} + + /////////////// // * FONTS * // /////////////// /** * Given an array of fonts, returns a single font which the given text will fully fit the availableWidth, * or the last font in the list (should be the smallest and text will be truncated). - * @param {GdiGraphics} gr - * @param {number} availableWidth The maximum width the text should be. - * @param {string} text The text to be measured. - * @param {Array} fontList The list of fonts to choose from. - * @param {number} maxLines The maximum number of lines the text should be. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} availableWidth - The maximum width the text should be. + * @param {string} text - The text to be measured. + * @param {Array} fontList - The list of fonts to choose from. + * @param {number} maxLines - The maximum number of lines the text should be. * @returns {GdiFont|null} The font that fits the given width, or null if no font fits. */ function ChooseFontForWidth(gr, availableWidth, text, fontList, maxLines = 1) { @@ -1435,9 +1611,10 @@ function ChooseFontForWidth(gr, availableWidth, text, fontList, maxLines = 1) { /** * Loads a font as a GDI object. - * @param {string} name The name of the font to load. - * @param {number} size The size of the font in pixels. - * @param {string} style The style of the font. See style constants for valid values. + * @global + * @param {string} name - The name of the font to load. + * @param {number} size - The size of the font in pixels. + * @param {string} style - The style of the font. See style constants for valid values. * @returns {GdiFont} The font or null if there was an error. */ function Font(name, size, style) { @@ -1453,7 +1630,8 @@ function Font(name, size, style) { /** * Checks if a font exists and is installed. Prints an error message if the font doesn't exist. - * @param {string} fontName The name of the font to test. + * @global + * @param {string} fontName - The name of the font to test. * @returns {boolean} True if the font exists and is installed, otherwise error message in the console. */ function TestFont(fontName) { @@ -1470,16 +1648,17 @@ function TestFont(fontName) { ////////////// /** * Calculates the maximum width of a text grid. It is used to determine the width of the grid's maximal text. - * @param {GdiGraphics} gr - * @param {Array} gridArray The array of grid elements that will be measured. - * @param {GdiFont} font The font to use for measuring the text. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {Array} gridArray - The array of grid elements that will be measured. + * @param {GdiFont} font - The font to use for measuring the text. * @returns {number} The maximum width of the text. */ function CalcGridMaxTextWidth(gr, gridArray, font) { let maxWidth = 0; if (gridArray) { for (const el of gridArray) { - const width = Math.ceil(gr.MeasureString(el.label, font, 0, 0, ww, wh).Width) + 1; + const width = Math.ceil(gr.MeasureString(el.label, font, 0, 0, grm.ui.ww, grm.ui.wh).Width) + 1; if (width > maxWidth) { maxWidth = width; } @@ -1493,16 +1672,17 @@ function CalcGridMaxTextWidth(gr, gridArray, font) { * Given two different texts, and two different font arrays, draws both lines of text * in the maximum number of lines available, using the largest font where all of the text will fit. * Where text1 ends and text2 begins will be on the same line if possible, switching fonts in between. - * @param {GdiGraphics} gr - * @param {number} availableWidth The maximum width a line of text can occupy. - * @param {number} left The X-coordinate of the text. - * @param {number} top The Y-coordinate of the text. - * @param {number} color The color of the text. - * @param {string} text1 The first text snippet. - * @param {GdiFont[]} fontList1 The array of fonts to try to fit text1 within availableWidth and maxLines. - * @param {string=} text2 The second text snippet if supplied. - * @param {GdiFont[]=} fontList2 The array of fonts to try to fit text2 within availableWidth and maxLines after drawing text1. - * @param {number} [maxLines=2] The max number of lines to attempt to draw text1 & text2 in. If text doesn't fit, ellipses will be added. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} availableWidth - The maximum width a line of text can occupy. + * @param {number} left - The X-coordinate of the text. + * @param {number} top - The Y-coordinate of the text. + * @param {number} color - The color of the text. + * @param {string} text1 - The first text snippet. + * @param {GdiFont[]} fontList1 - The array of fonts to try to fit text1 within availableWidth and maxLines. + * @param {string} [text2] - The second text snippet if supplied. + * @param {GdiFont[]} [fontList2] - The array of fonts to try to fit text2 within availableWidth and maxLines after drawing text1. + * @param {number} [maxLines] - The max number of lines to attempt to draw text1 & text2 in. If text doesn't fit, ellipses will be added. * @returns {number} The height of the drawn text. */ function DrawMultipleLines(gr, availableWidth, left, top, color, text1, fontList1, text2, fontList2, maxLines = 2) { @@ -1551,7 +1731,7 @@ function DrawMultipleLines(gr, availableWidth, left, top, color, text1, fontList line.text += ' ABCDEFGHIJKMLNOPQRSTUVWXYZABCDEFGHIJKMLNOPQRSTUVWXYZ'; // Trigger ellipses } gr.DrawString(line.text, line.font, color, left + line.x_offset, top + yOffset, - availableWidth - line.x_offset, lineHeight, g_string_format.trim_ellipsis_word); + availableWidth - line.x_offset, lineHeight, Stringformat.trim_ellipsis_word); yOffset += lineHeight; } return linesDrawn * lineHeight; @@ -1561,31 +1741,33 @@ function DrawMultipleLines(gr, availableWidth, left, top, color, text1, fontList /** * Enhances the original SMP DrawString method to render fonts correctly when text strings contain special symbols. * Should be only used for artist, track, album or other metadata text strings. - * @param {GdiGraphics} gr - * @param {string} str The text string to draw. Can be any string but only UTF-8 is supported. - * @param {GdiFont} font The font to use. Can be any font supported by GDI. - * @param {number} color The X-position to start measuring. - * @param {number} x The X-position of the text. - * @param {number} y The y-position of the text. - * @param {number} w The width of the text. - * @param {number} h The height of the text. - * @param {number=} flags The text string format flags. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {string} str - The text string to draw. Can be any string but only UTF-8 is supported. + * @param {GdiFont} font - The font to use. Can be any font supported by GDI. + * @param {number} color - The X-position to start measuring. + * @param {number} x - The X-position of the text. + * @param {number} y - The y-position of the text. + * @param {number} w - The width of the text. + * @param {number} h - The height of the text. + * @param {number} [flags] - The text string format flags. * @returns {GdiGraphics} The drawn text string with replaced Segoe UI Symbol font as fallback when the string contains special symbols. */ function DrawString(gr, str, font, color, x, y, w, h, flags) { const specialSymbolsRegex = /[\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2020-\u26FF]|\uD83E[\uDD10-\uDDFF]/; - gr.DrawString(str, specialSymbolsRegex.test(str) ? gdi.Font('Segoe UI Symbol', font.Size, font.Style) : font, color, x, y, w, h, flags); + return gr.DrawString(str, specialSymbolsRegex.test(str) ? gdi.Font('Segoe UI Symbol', font.Size, font.Style) : font, color, x, y, w, h, flags); } /** * Measures the size of a string. - * @param {string} text The text to measure. Can be any string but only UTF-8 is supported. - * @param {GdiFont} font The font to use. Can be any font supported by GDI. - * @param {number} x The X-position to start measuring. - * @param {number} y The Y-position to start measuring. - * @param {number} width The width of the text to measure. - * @param {number} height The height of the text to measure. + * @global + * @param {string} text - The text to measure. Can be any string but only UTF-8 is supported. + * @param {GdiFont} font - The font to use. Can be any font supported by GDI. + * @param {number} x - The X-position to start measuring. + * @param {number} y - The Y-position to start measuring. + * @param {number} width - The width of the text to measure. + * @param {number} height - The height of the text to measure. * @returns {number} The size of the string in pixels. */ function MeasureString(text, font, x, y, width, height) { @@ -1599,9 +1781,10 @@ function MeasureString(text, font, x, y, width, height) { /** * Writes a fancy header string with a given title, decorated with slashes and asterisks. - * @param {string} title The title to be included in the header. + * @global + * @param {string} title - The title to be included in the header. * @returns {string} A string that represents a fancy header: - * /////////////// + * ///////////////. * // * TITLE * // * /////////////// */ @@ -1617,7 +1800,8 @@ function WriteFancyHeader(title) { /** * Deep assign function that accepts objects as arguments. * The source objects are copied into the target object. - * @param {boolean} options The parameter has following options: + * @global + * @param {boolean} options - The parameter has following options: * - nonEnum: Copy only non - enumerable properties. * - symbols: Copy symbols from source to target. * - descriptors: Copy descriptors from source to target. @@ -1628,8 +1812,8 @@ function DeepAssign(options = { nonEnum: false, symbols: false, descriptors: fal /** * Deep assign objects with options. This function is identical to deepAssign except * that it accepts objects as arguments instead of using Object.assign. - * @param {Object} target The object to receive the deep assignment. - * @param {Object} sources The objects to deep assign. + * @param {object} target - The object to receive the deep assignment. + * @param {object} sources - The objects to deep assign. * @returns {void} Void if all sources are valid Objects. */ return function deepAssignWithOptions (target, ...sources) { @@ -1678,8 +1862,9 @@ function DeepAssign(options = { nonEnum: false, symbols: false, descriptors: fal /** * Finds keys in an object that match a predicate and returns the first match. - * @param {Object} obj An object to search for a key in. - * @param {Function} predicate A function that is used to determine whether a given value meets a certain condition. + * @global + * @param {object} obj - An object to search for a key in. + * @param {Function} predicate - A function that is used to determine whether a given value meets a certain condition. * @returns {string} The key of the object that matches the predicate. It takes three arguments: * - The value of the current property being evaluated. * - The key of the current property. @@ -1693,7 +1878,8 @@ function FindKey(obj, predicate = (o) => o) { /** * Checks if an object is a deep object. * This is similar to typeOf except that it returns true for objects that are themselves objects. - * @param {Object} obj The object to check, can be anything. + * @global + * @param {object} obj - The object to check, can be anything. * @returns {boolean} True if the object is a deep object. */ function IsDeepObject(obj) { @@ -1703,7 +1889,8 @@ function IsDeepObject(obj) { /** * Checks if it is either an empty object or an empty array. - * @param {Object|Array} obj An object or an array. + * @global + * @param {object | Array} obj - An object or an array. * @returns {boolean} True if the object is empty, false otherwise. */ function IsEmpty(obj) { @@ -1713,7 +1900,8 @@ function IsEmpty(obj) { /** * Checks if a given value is an instance of the Error class. - * @param {Object} err An error object. + * @global + * @param {object} err - An error object. * @returns {boolean} True if the object is an error, false otherwise. */ function IsError(err) { @@ -1723,7 +1911,8 @@ function IsError(err) { /** * Checks if a given value is an object. - * @param {*} a Any value that we want to check if it is an object. + * @global + * @param {*} a - Any value that we want to check if it is an object. * @returns {boolean} True if the object is an object, false otherwise. */ function IsObject(a) { @@ -1733,8 +1922,9 @@ function IsObject(a) { /** * Returns the type of the input parameter. - * @param {*} a Any value that we want to determine the type of. - * @returns {String} The type of object or array as it was. + * @global + * @param {*} a - Any value that we want to determine the type of. + * @returns {string} The type of object or array as it was. */ function ToType(a) { return ({}).toString.call(a).match(/([a-z]+)(:?\])/i)[1]; @@ -1747,8 +1937,9 @@ function ToType(a) { /** * Takes two arrays as input and returns a new array containing the elements * that are present in the first array but not in the second array. - * @param {Array} arr1 The first array from which we want to find the difference. - * @param {Array} arr2 The second array for comparison. + * @global + * @param {Array} arr1 - The first array from which we want to find the difference. + * @param {Array} arr2 - The second array for comparison. * @returns {Array} A new array with the elements of arr1 that are not in arr2. */ function Difference(arr1, arr2) { @@ -1758,8 +1949,9 @@ function Difference(arr1, arr2) { /** * Checks and compares if two arrays are equal. - * @param {array} arr1 The first array to compare. - * @param {array} arr2 The second array to compare. Must be of the same type as the first array. + * @global + * @param {Array} arr1 - The first array to compare. + * @param {Array} arr2 - The second array to compare. Must be of the same type as the first array. * @returns {boolean} True if the arrays are equal, false otherwise. */ function Equal(arr1, arr2) { @@ -1775,7 +1967,8 @@ function Equal(arr1, arr2) { /** * Checks if the passed value is an array. - * @param {Array} arr The array to check. + * @global + * @param {Array} arr - The array to check. * @returns {boolean} True if the array is an array. */ function IsArray(arr) { @@ -1785,7 +1978,8 @@ function IsArray(arr) { /** * Returns the last element of the given array. - * @param {array} arr The array to get the last element from. + * @global + * @param {Array} arr - The array to get the last element from. * @returns {*} The last element of the array. */ function Last(arr) { @@ -1795,9 +1989,10 @@ function Last(arr) { /** * Creates an array of numbers within a specified range, with an optional increment value. - * @param {number} start The starting value of the range. - * @param {number} end The end value of the range. It specifies the upper limit of the range of numbers that will be generated. - * @param {number} increment The value by which each element in the range is incremented. + * @global + * @param {number} start - The starting value of the range. + * @param {number} end - The end value of the range. It specifies the upper limit of the range of numbers that will be generated. + * @param {number} increment - The value by which each element in the range is incremented. * If the `increment` parameter is not provided, it will be set to the sign of the difference between the `end` and `start` values. * @returns {Array} An array of numbers that represents a range of values. */ @@ -1825,9 +2020,10 @@ function Range(start, end, increment) { /** * Removes a specified number of elements from either the beginning or end of an array. - * @param {Array} array The array parameter is the array that you want to trim. - * @param {number} count The number of elements to be removed from the array. - * @param {boolean} fromHead Trims from the beginning of the array (if true) or from the end of the array (if false). + * @global + * @param {Array} array - The array parameter is the array that you want to trim. + * @param {number} count - The number of elements to be removed from the array. + * @param {boolean} fromHead - Trims from the beginning of the array (if true) or from the end of the array (if false). */ function TrimArray(array, count, fromHead) { // Length deduction is much faster then _.drop or slice, since it does not create a new array. @@ -1844,8 +2040,9 @@ function TrimArray(array, count, fromHead) { /** * Takes an array and any number of additional arrays as arguments, * and returns a new array that contains all unique elements from all the arrays. - * @param {Array} arr The first array. - * @param {...Array} args The remaining arrays. + * @global + * @param {Array} arr - The first array. + * @param {...Array} args - The remaining arrays. * @returns {Array} The new array. */ function Union(arr, ...args) { @@ -1855,8 +2052,9 @@ function Union(arr, ...args) { /** * Zips the given arrays together into a single array of arrays. - * @param {Array} arr The first array that will be used as the base for creating a new array. - * @param {...Array} args The remaining arrays. + * @global + * @param {Array} arr - The first array that will be used as the base for creating a new array. + * @param {...Array} args - The remaining arrays. * @returns {Array} The new array. */ function Zip(arr, ...args) { @@ -1869,7 +2067,9 @@ function Zip(arr, ...args) { ///////////////// /** * Rounds a number to the nearest integer using the "round half up" method. - * @param {number} number The number that will be round to the nearest whole number (integer) using the absolute value method. + * @global + * @param {number} number - The number that will be round to the nearest whole number (integer) using the absolute value method. + * @returns {number} The rounded integer. */ function AbsRound(number) { return (0.5 + number) << 0; @@ -1878,9 +2078,10 @@ function AbsRound(number) { /** * Takes a number and limits it to a specified range. - * @param {number} num The number to clamp between the minimum and maximum values. - * @param {number} min The minimum value that the `num` parameter can be. - * @param {number} max The maximum value that the `num` parameter can be. + * @global + * @param {number} num - The number to clamp between the minimum and maximum values. + * @param {number} min - The minimum value that the `num` parameter can be. + * @param {number} max - The maximum value that the `num` parameter can be. * If the `num` parameter is greater than `max`, it will be clamped to `max`. * @returns {number} The clamped value of `num`. */ @@ -1892,8 +2093,9 @@ function Clamp(num, min, max) { /** * Generates a random number between a minimum and maximum value. - * @param {number} min The minimum value that for the random number. - * @param {number} max The maximum value that for the random number. + * @global + * @param {number} min - The minimum value that for the random number. + * @param {number} max - The maximum value that for the random number. * @returns {number} A random number between the minimum and maximum values. */ function RandomMinMax(min, max) { @@ -1903,10 +2105,11 @@ function RandomMinMax(min, max) { /** * Rounds the given float number to the specified number of decimals. - * @param {Number} floatnum The float number to round. - * @param {Number} decimals The number of decimals to round to. If decimals is less than or equal to 0 the number is rounded to the nearest integer. - * @param {Number} eps A small number to add to the float number to avoid rounding errors. - * @returns {Number} The rounded number. + * @global + * @param {number} floatnum - The float number to round. + * @param {number} decimals - The number of decimals to round to. If decimals is less than or equal to 0 the number is rounded to the nearest integer. + * @param {number} eps - A small number to add to the float number to avoid rounding errors. + * @returns {number} The rounded number. */ function Round(floatnum, decimals, eps = 10 ** -14) { let result; @@ -1921,26 +2124,20 @@ function Round(floatnum, decimals, eps = 10 ** -14) { /** * Takes a number as input and returns a string representation of that number with leading zeroes added if necessary. - * @param {number} num The number that we want to pad with zeroes. + * @global + * @param {number} num - The number that we want to pad with zeroes. + * @returns {string} The number padded with leading zeroes as a string. */ function PadZeroes(num) { return (` ${num}`).substr(-3, 3); } -/** - * Converts a percentage value to a value between 0 and 255. - * @param {number} p The percentage number. - */ -function PerToVal(p) { - return isPercent.test(p) ? AbsRound(parseInt(p) * 2.55) : parseInt(p); -} - - /** * Rounds a number to the specified number of decimal places. - * @param {number} number The number to round, must be non-negative. - * @param {number} precision The number of decimal places to round to. + * @global + * @param {number} number - The number to round, must be non-negative. + * @param {number} precision - The number of decimal places to round to. * @returns {number} The number rounded to the specified precision. */ function ToFixed(number, precision) { @@ -1954,9 +2151,10 @@ function ToFixed(number, precision) { ///////////////// /** * Formats a title and returns the result. - * @param {string} titleFormatString The title format string to evaluate. - * @param {FbMetadbHandle=} metadb The handle to evaluate string with. - * @param {boolean=} force An optional force evaluate. + * @global + * @param {string} titleFormatString - The title format string to evaluate. + * @param {FbMetadbHandle} metadb - The handle to evaluate string with. + * @param {boolean} [force] - An optional force evaluate. * @returns {string} The formatted title or an error message. */ function $(titleFormatString, metadb = undefined, force = false) { @@ -1971,6 +2169,7 @@ function $(titleFormatString, metadb = undefined, force = false) { /** * Escapes special characters in a string for use in title formatting expressions. * This includes escaping single quotes, parentheses, square brackets, commas, percent signs, and dollar signs. + * @global * @param {string} string - The string to be escaped. * @returns {string} The escaped string, safe for use in title formatting expressions. */ @@ -1984,7 +2183,8 @@ function $Escape(string) { /** * Checks if all characters in a string are equal. - * @param {string} str The string to check. + * @global + * @param {string} str - The string to check. * @returns {boolean} True if all characters in the string are equal. */ function AllEqual(str) { @@ -1994,7 +2194,8 @@ function AllEqual(str) { /** * Capitalizes first letter of a string. - * @param {string} s The string that will be capitalized. + * @global + * @param {string} s - The string that will be capitalized. * @returns {string} The capitalized string. */ function CapitalizeString(s) { @@ -2004,16 +2205,17 @@ function CapitalizeString(s) { /** * Converts a full country name to its ISO country code. - * @param {string} name The full country name. + * @global + * @param {string} name - The full country name. * @returns {string} The country ISO code. */ function ConvertFullCountryToIso(name) { if (Array.isArray(name)) name = name[0]; if (typeof name !== 'string') return null; - for (const code in countryCodes) { - if (Object.prototype.hasOwnProperty.call(countryCodes, code) && - countryCodes[code].toLowerCase() === name.toLowerCase()) { + for (const code in CountryCodes) { + if (Object.prototype.hasOwnProperty.call(CountryCodes, code) && + CountryCodes[code].toLowerCase() === name.toLowerCase()) { return code; } } @@ -2023,12 +2225,13 @@ function ConvertFullCountryToIso(name) { /** * Converts an ISO country code to its full name. - * @param {string} code The two letter ISO country code. + * @global + * @param {string} code - The two letter ISO country code. * @returns {string} The full name of the country. */ function ConvertIsoCountryCodeToFull(code) { if (code.length === 2) { - return countryCodes[code]; + return CountryCodes[code]; } return code; } @@ -2036,7 +2239,8 @@ function ConvertIsoCountryCodeToFull(code) { /** * Checks if a given value is a string. - * @param {*} str The value to check if it is a string. + * @global + * @param {*} str - The value to check if it is a string. * @returns {boolean} True if the value is a string, false otherwise. */ function IsString(str) { @@ -2049,9 +2253,10 @@ function IsString(str) { /** * Left pads a string to a specified size. - * @param {string} val The value to pad. Can be any type but not necessarily a string. - * @param {number} size The size to pad to. Must be greater than or equal to the value of val. - * @param {string} ch The character to use for padding. If null, a space will be used. + * @global + * @param {string} val - The value to pad. Can be any type but not necessarily a string. + * @param {number} size - The size to pad to. Must be greater than or equal to the value of val. + * @param {string} ch - The character to use for padding. If null, a space will be used. * @returns {string} The left padded string. */ function LeftPad(val, size, ch) { @@ -2068,7 +2273,8 @@ function LeftPad(val, size, ch) { /** * Takes an array of strings as input and returns the string with the longest length. - * @param {array} arr The array to compare. + * @global + * @param {Array} arr - The array to compare. * @returns {string} The longest string. */ function LongestString(arr) { @@ -2078,9 +2284,10 @@ function LongestString(arr) { /** * Pads a number with zeros to a given length. - * @param {number} num The number to be padded. Must be convertible to the specified base. - * @param {number} len The length of the number to be padded. - * @param {number} base The base to pad the number to. Default is 10. + * @global + * @param {number} num - The number to be padded. Must be convertible to the specified base. + * @param {number} len - The length of the number to be padded. + * @param {number} base - The base to pad the number to. Default is 10. * @returns {string} The number with the specified length and padded with zeros to the specified base. */ function PadNumber(num, len, base) { @@ -2091,7 +2298,8 @@ function PadNumber(num, len, base) { /** * Wraps a string in double quotes. - * @param {string} value The value to be quoted. + * @global + * @param {string} value - The value to be quoted. * @returns {string} The quoted value. */ function Quotes(value) { @@ -2102,7 +2310,8 @@ function Quotes(value) { /** * Replaces unicode characters such as apostrophes and multi-timestamps which will print as crap. * May not be needed when using UTF-8 code page. - * @param {*} rawString The raw string that has certain characters that need to be replaced. + * @global + * @param {string} rawString - The raw string that has certain characters that need to be replaced. * @returns {string} The modified string with replaced characters. */ function ReplaceChars(rawString) { @@ -2124,7 +2333,8 @@ function ReplaceChars(rawString) { /** * Replaces special characters in filenames. - * @param {string} s The string to be replaced. + * @global + * @param {string} s - The string to be replaced. * @returns {string} The modified string with replaced characters. */ function ReplaceFileChars(s) { @@ -2142,10 +2352,11 @@ function ReplaceFileChars(s) { /** * Formats a text string, accepts 1-4 parameters, corresponding to h_align, v_align, trimming, flags. - * @param {number} [h_align] 0: Near, 1: Center, 2: Far. - * @param {number} [v_align] 0: Near, 1: Center, 2: Far. - * @param {number} [trimming] 0: None, 1: Char, 2: Word, 3: Ellipses char, 4: Ellipses word, 5: Ellipses path. - * @param {number} [flags] `|`'d together flags. See g_string_format in Common.js. + * @global + * @param {number} [h_align] - 0: Near, 1: Center, 2: Far. + * @param {number} [v_align] - 0: Near, 1: Center, 2: Far. + * @param {number} [trimming] - 0: None, 1: Char, 2: Word, 3: Ellipses char, 4: Ellipses word, 5: Ellipses path. + * @param {number} [flags] - `|`'d together flags. See Stringformat in gr-common.js. * @returns {number} The string format value. */ function StringFormat(h_align, v_align, trimming, flags) { @@ -2160,8 +2371,9 @@ function StringFormat(h_align, v_align, trimming, flags) { /** * Converts a number to a padded hex string. - * @param {number} num The number to convert. - * @param {number} len The length of the padded string. + * @global + * @param {number} num - The number to convert. + * @param {number} len - The length of the padded string. * @returns {string} The padded hex string. */ function ToPaddedHexString(num, len) { @@ -2176,8 +2388,9 @@ function ToPaddedHexString(num, len) { * Compares two values, providing a safe comparison for strings and numbers. * If both values are strings, it uses the localeCompare function and returns -1, 0, or 1. * If not, it subtracts b from a (considering undefined or null as 0), and returns the subtraction result, which could be any number. - * @param {string|number} a The first value to compare. - * @param {string|number} b The second value to compare. + * @global + * @param {string|number} a - The first value to compare. + * @param {string|number} b - The second value to compare. * @returns {number} The result of the comparison. * When comparing strings, -1 if a < b, 0 if a = b, 1 if a > b. * When comparing numbers, the result of (a - b). @@ -2189,8 +2402,9 @@ function CompareValues(a, b) { /** * Returns the key from `sumObj` and `countObj` with the highest average value. - * @param {(Object|Map)} sumObj The object or map containing sum values, where each key represents a unique category. - * @param {(Object|Map)} countObj The object or map containing count values, where each key should match a key in `sumObj`. + * @global + * @param {(object | Map)} sumObj - The object or map containing sum values, where each key represents a unique category. + * @param {(object | Map)} countObj - The object or map containing count values, where each key should match a key in `sumObj`. * @returns {(string|undefined)} The key associated with the highest average value. * Returns `undefined` if `sumObj` and `countObj` do not have any matching keys, or if any value in `countObj` is zero (to avoid division by zero). */ @@ -2217,7 +2431,8 @@ function GetKeyByHighestAvg(sumObj, countObj) { /** * Returns the first key associated with the highest value in an object or map. - * @param {(Object|Map)} obj The input object or map whose key-value pairs are examined. + * @global + * @param {(object | Map)} obj - The input object or map whose key-value pairs are examined. * @returns {(string|null)} The first key associated with the highest value, or null if the object or map is empty. */ function GetKeyByHighestVal(obj) { @@ -2229,8 +2444,9 @@ function GetKeyByHighestVal(obj) { /** * Sorts the keys of an object or map in descending order based on the average of their corresponding values in two different objects or Maps. - * @param {(Object|Map)} sumObj The object or map whose keys are to be sorted. The values are summed values for each key. - * @param {(Object|Map)} countObj The object or map with the same keys as sumObj. The values are the count of occurrences for each key. + * @global + * @param {(object | Map)} sumObj - The object or map whose keys are to be sorted. The values are summed values for each key. + * @param {(object | Map)} countObj - The object or map with the same keys as sumObj. The values are the count of occurrences for each key. * @returns {Array} An array of keys from `sumObj` and `countObj`, sorted in descending order of their corresponding average values (sum / count). */ function SortKeyValuesByAvg(sumObj, countObj) { @@ -2249,7 +2465,8 @@ function SortKeyValuesByAvg(sumObj, countObj) { /** * Sorts the keys of an object or map in descending order based on their corresponding values. - * @param {(Object|Map)} obj The object or map whose keys are to be sorted. + * @global + * @param {(object | Map)} obj - The object or map whose keys are to be sorted. * @returns {Array} An array of keys from `obj`, sorted in descending order of their corresponding values. */ function SortKeyValuesByDsc(obj) { @@ -2261,12 +2478,10 @@ function SortKeyValuesByDsc(obj) { ////////////// // * TIME * // ////////////// -/** @type {number} */ -let timezoneOffset = 0; - /** * Calculate the age as the difference between the current time and the given date in seconds. - * @param {Date} date The date to calculate the age for. + * @global + * @param {Date} date - The date to calculate the age for. * @returns {number} The aging of the passed time. */ function CalcAge(date) { @@ -2278,8 +2493,9 @@ function CalcAge(date) { /** * Calculates the age ratio. - * @param {number} num The number to calculate the age ratio for. - * @param {number} divisor The number to divide the age by ( should be between 0 and 1 ). + * @global + * @param {number} num - The number to calculate the age ratio for. + * @param {number} divisor - The number to divide the age by ( should be between 0 and 1 ). * @returns {number} The age ratio in 3 decimal places. */ function CalcAgeRatio(num, divisor) { @@ -2289,14 +2505,16 @@ function CalcAgeRatio(num, divisor) { /** * Calculates the difference between the input date and the current date. - * @param {string} date The date to calculate age for. + * @global + * @param {string} date - The date to calculate age for. * @returns {string} The age date in format YYYY-MM-DD. */ function CalcAgeDateString(date) { let str = ''; + const timezoneOffset = UpdateTimezoneOffset(); if (date.length) { try { - str = DateDiff($Date(date)); + str = DateDiff($Date(date), undefined, timezoneOffset); } catch (e) { console.log('date has invalid value', date, 'in CalcAgeDateString()'); // Throw new ArgumentError('date', date, 'in CalcAgeDateString()'); @@ -2308,7 +2526,8 @@ function CalcAgeDateString(date) { /** * Passes any date string to $Date ('Y - m - d H : i : s'). - * @param {string} dateStr A date string in the format YYYY-MM-DD. + * @global + * @param {string} dateStr - A date string in the format YYYY-MM-DD. * @returns {string} The formatted date. */ function $Date(dateStr) { @@ -2317,12 +2536,14 @@ function $Date(dateStr) { /** - * Returns the difference between a start and end date in the form of "2y 3m 24d". Order of the two dates does not matter. - * @param {string} startingDate The start date in YYYY-MM-DD format. - * @param {string=} endingDate The end date in YYYY-MM-DD format. If no endingDate is supplied, use current time. + * Returns the difference between a start and end date in the form of "1y 12m 31d". Order of the two dates does not matter. + * @global + * @param {string} startingDate - The start date in YYYY-MM-DD format. + * @param {string} endingDate - The end date in YYYY-MM-DD format. If no endingDate is supplied, use current time. + * @param {number} timezoneOffset - The timezone offset in milliseconds. This offset is subtracted from the current time if no endingDate is supplied. * @returns {string} The difference between the two dates in the format YYYY-MM-DD. */ -function DateDiff(startingDate, endingDate) { +function DateDiff(startingDate, endingDate, timezoneOffset) { if (!startingDate) return ''; const hasStartDay = (startingDate.length > 7); if (!hasStartDay) { @@ -2369,7 +2590,8 @@ function DateDiff(startingDate, endingDate) { /** * Converts a date object to a YYYY-MM-DD string. - * @param {string} date The date to convert. Must be non null and not out of range. + * @global + * @param {string} date - The date to convert. Must be non null and not out of range. * @returns {string} The date in YYYY-MM-DD format. */ function DateToYMD(date) { @@ -2382,7 +2604,8 @@ function DateToYMD(date) { /** * Converts a 24-hour time format hour to 12-hour time format. - * @param {number|string} hour The hour in 24-hour format. + * @global + * @param {number|string} hour - The hour in 24-hour format. * @returns {string} The hour in 12-hour format with AM/PM suffix. */ function To12HourTimeFormat(hour) { @@ -2395,8 +2618,9 @@ function To12HourTimeFormat(hour) { /** * Converts a date string to a date time string. - * @param {string} dateTimeStr The date string to convert. - * @returns {string} The date time string + * @global + * @param {string} dateTimeStr - The date string to convert. + * @returns {string} The date time string. */ function ToDatetime(dateTimeStr) { return dateTimeStr.replace(' ', 'T'); @@ -2406,10 +2630,12 @@ function ToDatetime(dateTimeStr) { /** * The foobar time strings are already in local time, so converting them to date objects treats them as UTC time, * and again adjusts to local time, and the timezone offset is applied twice. Therefore we need to add it back in here. - * @param {string} dateTimeStr The date string to convert. - * @returns {number} The converted time in milliseconds. + * @global + * @param {string} dateTimeStr - The date string to convert. + * @param {number} timezoneOffset - The timezone offset in milliseconds to add back to the time. + * @returns {number|undefined} The converted time in milliseconds, or undefined if dateTimeStr is 'N/A'. */ -function ToTime(dateTimeStr) { +function ToTime(dateTimeStr, timezoneOffset) { if (dateTimeStr === 'N/A') { return undefined; } @@ -2418,12 +2644,13 @@ function ToTime(dateTimeStr) { /** - * Updates timezoneOffset based on DST adjustments, this method is called from on_playback_new_track. - * @returns {number} Updates the `timezoneOffset` global variable. + * Updates the current timezone offset based on DST adjustments. This function can be called, for example, from on_playback_new_track. + * @global + * @returns {number} The current timezone offset in milliseconds. */ function UpdateTimezoneOffset() { const temp = new Date(); - timezoneOffset = temp.getTimezoneOffset() * 60 * 1000; + return temp.getTimezoneOffset() * 60 * 1000; } @@ -2432,53 +2659,58 @@ function UpdateTimezoneOffset() { //////////////////////// /** * Deletes the Biography cache on auto or manual usage. + * @global */ function DeleteBiographyCache() { - try { fso.DeleteFolder(pref.customBiographyDir ? `${globals.customBiographyDir}\\*.*` : `${fb.ProfilePath}cache\\biography\\biography-cache`); } + try { fso.DeleteFolder(grSet.customBiographyDir ? `${grCfg.customBiographyDir}\\*.*` : `${fb.ProfilePath}cache\\biography\\biography-cache`); } catch (e) {} } /** * Deletes the Library cache on auto or manual usage. + * @global */ function DeleteLibraryCache() { - try { fso.DeleteFolder(pref.customLibraryDir ? `${globals.customLibraryDir}\\*.*` : `${fb.ProfilePath}cache\\library\\library-tree-cache`); } + try { fso.DeleteFolder(grSet.customLibraryDir ? `${grCfg.customLibraryDir}\\*.*` : `${fb.ProfilePath}cache\\library\\library-tree-cache`); } catch (e) {} } /** * Deletes the Lyrics cache on auto or manual usage. + * @global */ function DeleteLyrics() { - try { fso.DeleteFile(pref.customLyricsDir ? `${globals.customLyricsDir}\\*.*` : `${fb.ProfilePath}cache\\lyrics\\*.*`); } + try { fso.DeleteFile(grSet.customLyricsDir ? `${grCfg.customLyricsDir}\\*.*` : `${fb.ProfilePath}cache\\lyrics\\*.*`); } catch (e) {} } /** * Deletes the Waveform bar cache on auto or manual usage. + * @global */ function DeleteWaveformBarCache() { - try { fso.DeleteFolder(pref.customWaveformBarDir ? `${globals.customWaveformBarDir}\\*.*` : `${fb.ProfilePath}cache\\waveform\\*.*`); } + try { fso.DeleteFolder(grSet.customWaveformBarDir ? `${grCfg.customWaveformBarDir}\\*.*` : `${fb.ProfilePath}cache\\waveform\\*.*`); } catch (e) {} } /** * Formats the theme day/night mode time range string into a more readable format. - * @param {string|null} themeDayNightMode The time range for theme day/night mode in 24-hour format, e.g. '6-18' for 6am to 6pm. If null or undefined, it returns 'Deactivated (default)'. + * @global + * @param {string|null} themeDayNightMode - The time range for theme day/night mode in 24-hour format, e.g. '6-18' for 6am to 6pm. If null or undefined, it returns 'Deactivated (default)'. * @returns {string} A formatted string representing the time range in 12-hour format with am/pm suffixes, e.g. '06am (day) - 06pm (night)', or 'Deactivated (default)' if themeDayNightMode is falsy. */ function FormatThemeDayNightModeString(themeDayNightMode) { if (!themeDayNightMode) return 'Deactivated (default)'; // * Safeguard to handle number values and convert them to string with default end hour - if (typeof pref.themeDayNightMode === 'number') { - pref.themeDayNightMode = `${pref.themeDayNightMode}-18`; // Defaulting end hour to 18 (6 PM) - } else if (typeof pref.themeDayNightMode === 'string' && !pref.themeDayNightMode.includes('-')) { - pref.themeDayNightMode = `${pref.themeDayNightMode}-${pref.themeDayNightMode}`; // Same start and end hour + if (typeof grSet.themeDayNightMode === 'number') { + grSet.themeDayNightMode = `${grSet.themeDayNightMode}-18`; // Defaulting end hour to 18 (6 PM) + } else if (typeof grSet.themeDayNightMode === 'string' && !grSet.themeDayNightMode.includes('-')) { + grSet.themeDayNightMode = `${grSet.themeDayNightMode}-${grSet.themeDayNightMode}`; // Same start and end hour } const [start, end] = themeDayNightMode.split('-').map(part => { @@ -2493,8 +2725,9 @@ function FormatThemeDayNightModeString(themeDayNightMode) { /** * Makes or restores a theme backup. - * @param {boolean} make Should a theme backup be made. - * @param {boolean} restore Should a theme backup be restored. + * @global + * @param {boolean} make - Should a theme backup be made. + * @param {boolean} restore - Should a theme backup be restored. */ function ManageBackup(make, restore) { const backupPath = `${fb.ProfilePath}backup\\profile\\`; @@ -2590,27 +2823,26 @@ function ManageBackup(make, restore) { if (!checkFolders()) return; - await setThemeSettings(true); + await grm.settings.setThemeSettings(true); await copyFolders(); - if (detectWine || !detectIE) { // Disable fancy popup on Linux or if no IE is installed, otherwise it will crash and is not yet supported + if (Detect.Wine || !Detect.IE) { // Disable fancy popup on Linux or if no IE is installed, otherwise it will crash and is not yet supported const msgFb = `>>> Theme backup has been successfully saved <<<\n\n${fb.ProfilePath}backup`; await fb.ShowPopupMessage(msgFb, 'Theme backup'); } else { const msg = `Theme backup has been successfully saved:\n\n${fb.ProfilePath}backup\n\n\n`; - await popUpBox.confirm('Georgia-ReBORN', msg, 'OK', false, false, 'center', false); + await lib.popUpBox.confirm('Georgia-ReBORN', msg, 'OK', false, false, 'center', false); } }; const restoreBackup = async () => { if (!checkFolders()) return; - pref.customThemeSettings = true; - pref.restoreBackupPlaylist = true; + grSet.restoreBackupPlaylist = true; await checkVersion(); await copyFolders(); - await setThemeSettings(); + await grm.settings.setThemeSettings(false, true); await setTimeout(() => { fb.RunMainMenuCommand('File/Restart'); }, 1000); }; @@ -2628,17 +2860,17 @@ function ManageBackup(make, restore) { * If theme backup has been successfully restored, foobar automatically deletes all restored playlist files in the playlist directory. * On the next foobar restart and initialization, foobar adds crap playlist files in the playlist directory making them useless. * To fix this issue, all playlist files from the backup directory will be copied and restored again. + * @global */ function RestoreBackupPlaylist() { const plistOld = `${fb.ProfilePath}backup\\profile\\playlists-v1.4`; const plistNew = `${fb.ProfilePath}backup\\profile\\playlists-v2.0`; let playlistDir; - let oldVersion = false; const checkVersion = async () => { - if (IsFolder(plistOld)) { playlistDir = plistOld; oldVersion = true; } - else if (IsFolder(plistNew)) { playlistDir = plistNew; oldVersion = false; } + if (IsFolder(plistOld)) { playlistDir = plistOld; } + else if (IsFolder(plistNew)) { playlistDir = plistNew; } }; const checkFolders = () => { @@ -2661,12 +2893,12 @@ function RestoreBackupPlaylist() { }; const restoreBackup = async () => { - pref.restoreBackupPlaylist = false; + grSet.restoreBackupPlaylist = false; if (!checkFolders()) return; await checkVersion(); await copyFolders(); - await setThemeSettings(); + await grm.settings.setThemeSettings(false, true); await console.log('\n>>> Georgia-ReBORN theme backup has been successfully restored <<<\n\n'); await setTimeout(() => { fb.RunMainMenuCommand('File/Restart'); }, 1000); }; @@ -2677,15 +2909,18 @@ function RestoreBackupPlaylist() { /** * Displays red rectangles to show all repaint areas when activating "Draw areas" in dev tools, used for debugging. + * @global */ function RepaintRectAreas() { + const originalRepaintRect = window.RepaintRect.bind(window); + window.RepaintRect = (x, y, w, h, force = undefined) => { - if (timings.drawRepaintRects) { - repaintRects.push({ x, y, w, h }); + if (grm.ui.drawRepaintRects) { + grm.ui.repaintRects.push({ x, y, w, h }); window.Repaint(); } else { - repaintRectCount++; - window.oldRepaintRect(x, y, w, h, force); + grm.ui.repaintRectCount++; + originalRepaintRect(x, y, w, h, force); } }; } @@ -2693,6 +2928,7 @@ function RepaintRectAreas() { /** * Prints logs for window.Repaint() in the console, used for debugging. + * @global */ function RepaintWindow() { DebugLog('Repainting from RepaintWindow()'); @@ -2702,18 +2938,18 @@ function RepaintWindow() { /** * Continuously repaints rectangles for a short period of time ( 1 sec ), used when changing the layout width. + * @global */ function RepaintWindowRectAreas() { DebugLog('Repainting from RepaintWindowRectAreas()'); + const originalRepaintRect = window.RepaintRect.bind(window); window.RepaintRect = () => { window.Repaint(); }; - setTimeout(() => { // Restore window.RepaintRect afterwards - window.RepaintRect = (x, y, w, h, force = undefined) => { - window.oldRepaintRect(x, y, w, h, force); - }; + setTimeout(() => { + window.RepaintRect = originalRepaintRect; }, 1000); } @@ -2722,12 +2958,13 @@ function RepaintWindowRectAreas() { * Displays a popup with customizable message and button labels. * The behavior of the popup depends on the environment; if running under Wine or without Internet Explorer, * it will show a simple popup message. Otherwise, it will show a confirm box with two buttons. - * @param {boolean} fbPopup Determines if the fb.ShowPopupMessage should be shown. - * @param {string} fbMsg The message to be displayed in the fb.ShowPopupMessage popup. - * @param {string} popUpMsg The message to be displayed in the confirm box popup. - * @param {string} btn1Label The label for the first button in the confirm box. - * @param {string} btn2Label The label for the second button in the confirm box. If not provided, no second button is shown. - * @param {Function} callback The callback function that is called with the confirmation status. + * @global + * @param {boolean} fbPopup - Determines if the fb.ShowPopupMessage should be shown. + * @param {string} fbMsg - The message to be displayed in the fb.ShowPopupMessage popup. + * @param {string} popUpMsg - The message to be displayed in the confirm box popup. + * @param {string} btn1Label - The label for the first button in the confirm box. + * @param {string} btn2Label - The label for the second button in the confirm box. If not provided, no second button is shown. + * @param {Function} callback - The callback function that is called with the confirmation status. */ function ShowPopup(fbPopup, fbMsg, popUpMsg, btn1Label, btn2Label, callback) { const continue_confirmation = (status, confirmed) => { @@ -2738,12 +2975,12 @@ function ShowPopup(fbPopup, fbMsg, popUpMsg, btn1Label, btn2Label, callback) { callback(confirmed); }; - if (detectWine || !detectIE) { // Disable fancy popup on Linux or if no IE is installed, otherwise it will crash and is not yet supported + if (Detect.Wine || !Detect.IE) { // Disable fancy popup on Linux or if no IE is installed, otherwise it will crash and is not yet supported continue_confirmation(false, btn1Label); if (fbPopup) fb.ShowPopupMessage(fbMsg, 'Georgia-ReBORN'); } else { - popUpBox.confirm('Georgia-ReBORN', popUpMsg, btn1Label, btn2Label, false, 'center', btn2Label ? continue_confirmation : false); + lib.popUpBox.confirm('Georgia-ReBORN', popUpMsg, btn1Label, btn2Label, false, 'center', btn2Label ? continue_confirmation : false); } } @@ -2751,80 +2988,91 @@ function ShowPopup(fbPopup, fbMsg, popUpMsg, btn1Label, btn2Label, callback) { /** * Displays a popup message for the theme day/night mode. * If the mode is active, it provides an option to deactivate it. + * @global */ function ShowThemeDayNightModePopup() { const msg = 'Theme day/night mode is active\nand has locked the theme.\n\nIn order to change themes,\nthis mode must be deactivated first.\n\nDeactivate it now?\n\n\n'; const msgFb = 'Theme day/night mode has been deactivated in order to change themes.'; ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) pref.themeDayNightMode = false; + if (confirmed) grSet.themeDayNightMode = false; }); } /** * Writes %GR_THEMECOLOR%, %GR_THEME%, %GR_STYLE%, %GR_PRESET% tags to music files via the Playlist or Library context menu. + * @global */ function WriteThemeTags() { - const grTags = []; const plItems = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); - const libItems = new FbMetadbHandleList(pop.getHandleList('newItems')); - const items = displayLibrary && !displayPlaylist || displayLibrarySplit() && state.mouse_x < ww * 0.5 ? libItems : plItems; + const libItems = new FbMetadbHandleList(lib.pop.getHandleList('newItems')); + const items = grm.ui.displayLibrary && !grm.ui.displayPlaylist || grm.ui.displayLibrarySplit() && grm.ui.state.mouse_x < grm.ui.ww * 0.5 ? libItems : plItems; + + if (!items || items.Count === 0) return; - if (!items) return; + const grTags = []; + const themeColor = grSet.theme === 'random' ? ColToRgb(grCol.primary) : ''; + const theme = grSet.preset === false ? grSet.theme : ''; + const preset = grSet.preset !== false ? grSet.preset : ''; + let style = ''; + + if (grSet.preset === false) { + const styleOptions = [ + grSet.styleNighttime && 'styleNighttime', + grSet.styleBevel && 'bevel', + grSet.styleBlend && 'blend', + grSet.styleBlend2 && 'blend2', + grSet.styleGradient && 'gradient', + grSet.styleGradient2 && 'gradient2', + grSet.styleAlternative && 'alternative', + grSet.styleAlternative2 && 'alternative2', + grSet.styleBlackAndWhite && 'blackAndWhite', + grSet.styleBlackAndWhite2 && 'blackAndWhite2', + grSet.styleBlackReborn && 'blackReborn', + grSet.styleRebornWhite && 'rebornWhite', + grSet.styleRebornBlack && 'rebornBlack', + grSet.styleRebornFusion && 'rebornFusion', + grSet.styleRebornFusion2 && 'rebornFusion2', + grSet.styleRandomPastel && 'randomPastel', + grSet.styleRandomDark && 'randomDark', + grSet.styleRebornFusionAccent && 'rebornFusionAccent', + grSet.styleTopMenuButtons === 'filled' && 'topMenuButtons=filled', + grSet.styleTopMenuButtons === 'bevel' && 'topMenuButtons=bevel', + grSet.styleTopMenuButtons === 'inner' && 'topMenuButtons=inner', + grSet.styleTopMenuButtons === 'emboss' && 'topMenuButtons=emboss', + grSet.styleTopMenuButtons === 'minimal' && 'topMenuButtons=minimal', + grSet.styleTransportButtons === 'bevel' && 'transportButtons=bevel', + grSet.styleTransportButtons === 'inner' && 'transportButtons=inner', + grSet.styleTransportButtons === 'emboss' && 'transportButtons=emboss', + grSet.styleTransportButtons === 'minimal' && 'transportButtons=minimal', + grSet.styleProgressBarDesign === 'rounded' && 'progressBarDesign=rounded', + grSet.styleProgressBarDesign === 'lines' && 'progressBarDesign=lines', + grSet.styleProgressBarDesign === 'blocks' && 'progressBarDesign=blocks', + grSet.styleProgressBarDesign === 'dots' && 'progressBarDesign=dots', + grSet.styleProgressBarDesign === 'thin' && 'progressBarDesign=thin', + grSet.styleProgressBar === 'bevel' && 'progressBarBg=bevel', + grSet.styleProgressBar === 'inner' && 'progressBarBg=inner', + grSet.styleProgressBarFill === 'bevel' && 'progressBarFill=bevel', + grSet.styleProgressBarFill === 'inner' && 'progressBarFill=inner', + grSet.styleProgressBarFill === 'blend' && 'progressBarFill=blend', + grSet.styleVolumeBarDesign === 'rounded' && 'volumeBarDesign=rounded', + grSet.styleVolumeBar === 'bevel' && 'volumeBarBg=bevel', + grSet.styleVolumeBar === 'inner' && 'volumeBarBg=inner', + grSet.styleVolumeBarFill === 'bevel' && 'volumeBarFill=bevel', + grSet.styleVolumeBarFill === 'bevel' && 'volumeBarFill=inner' + ]; + + // * Filter out the empty strings and join the remaining ones + style = styleOptions.filter(Boolean).join('; '); + } for (let i = 0; i < items.Count; ++i) { grTags.push({ - GR_THEMECOLOR: pref.theme === 'random' ? ColToRgb(col.primary) : '', - - GR_THEME: pref.preset === false ? pref.theme : '', - - GR_STYLE: pref.preset === false ? [ - pref.styleNighttime ? 'styleNighttime' : '', - pref.styleBevel ? 'bevel' : '', - pref.styleBlend ? 'blend' : '', - pref.styleBlend2 ? 'blend2' : '', - pref.styleGradient ? 'gradient' : '', - pref.styleGradient2 ? 'gradient2' : '', - pref.styleAlternative ? 'alternative' : '', - pref.styleAlternative2 ? 'alternative2' : '', - pref.styleBlackAndWhite ? 'blackAndWhite' : '', - pref.styleBlackAndWhite2 ? 'blackAndWhite2' : '', - pref.styleBlackReborn ? 'blackReborn' : '', - pref.styleRebornWhite ? 'rebornWhite' : '', - pref.styleRebornBlack ? 'rebornBlack' : '', - pref.styleRebornFusion ? 'rebornFusion' : '', - pref.styleRebornFusion2 ? 'rebornFusion2' : '', - pref.styleRandomPastel ? 'randomPastel' : '', - pref.styleRandomDark ? 'randomDark' : '', - pref.styleRebornFusionAccent ? 'rebornFusionAccent' : '', - pref.styleTopMenuButtons === 'filled' ? 'topMenuButtons=filled' : '', - pref.styleTopMenuButtons === 'bevel' ? 'topMenuButtons=bevel' : '', - pref.styleTopMenuButtons === 'inner' ? 'topMenuButtons=inner' : '', - pref.styleTopMenuButtons === 'emboss' ? 'topMenuButtons=emboss' : '', - pref.styleTopMenuButtons === 'minimal' ? 'topMenuButtons=minimal' : '', - pref.styleTransportButtons === 'bevel' ? 'transportButtons=bevel' : '', - pref.styleTransportButtons === 'inner' ? 'transportButtons=inner' : '', - pref.styleTransportButtons === 'emboss' ? 'transportButtons=emboss' : '', - pref.styleTransportButtons === 'minimal' ? 'transportButtons=minimal' : '', - pref.styleProgressBarDesign === 'rounded' ? 'progressBarDesign=rounded' : '', - pref.styleProgressBarDesign === 'lines' ? 'progressBarDesign=lines' : '', - pref.styleProgressBarDesign === 'blocks' ? 'progressBarDesign=blocks' : '', - pref.styleProgressBarDesign === 'dots' ? 'progressBarDesign=dots' : '', - pref.styleProgressBarDesign === 'thin' ? 'progressBarDesign=thin' : '', - pref.styleProgressBar === 'bevel' ? 'progressBarBg=bevel' : '', - pref.styleProgressBar === 'inner' ? 'progressBarBg=inner' : '', - pref.styleProgressBarFill === 'bevel' ? 'progressBarFill=bevel' : '', - pref.styleProgressBarFill === 'inner' ? 'progressBarFill=inner' : '', - pref.styleProgressBarFill === 'blend' ? 'progressBarFill=blend' : '', - pref.styleVolumeBarDesign === 'rounded' ? 'volumeBarDesign=rounded' : '', - pref.styleVolumeBar === 'bevel' ? 'volumeBarBg=bevel' : '', - pref.styleVolumeBar === 'inner' ? 'volumeBarBg=inner' : '', - pref.styleVolumeBarFill === 'bevel' ? 'volumeBarFill=bevel' : '', - pref.styleVolumeBarFill === 'bevel' ? 'volumeBarFill=inner' : '' - ] : '', - - GR_PRESET: pref.preset !== false ? pref.preset : '' + GR_THEMECOLOR: themeColor, + GR_THEME: theme, + GR_STYLE: style, + GR_PRESET: preset }); } diff --git a/profile/georgia-reborn/scripts/Base/gr-initialize.js b/profile/georgia-reborn/scripts/Base/gr-initialize.js new file mode 100644 index 00000000..a5106dc7 --- /dev/null +++ b/profile/georgia-reborn/scripts/Base/gr-initialize.js @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Initialization * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +//////////////////////// +// * MAIN INSTANCES * // +//////////////////////// +grm.utils = new Utilities(); + +grm.ui = new MainUI(); +grm.settings = new ThemeSettingsManager(); +grm.display = new Display(); +grm.color = new ColorMethods(); +grm.theme = new ThemeColors(); +grm.style = new StyleColors(); +grm.preset = new ThemePreset(); + +grm.topMenu = new TopMenu(); +grm.options = new TopMenuOptions(); +grm.ctxMenu = new ContextMenus(); +grm.inputBox = new InputBox(); +grm.cusMenu = new CustomMenu(); +grm.cthMenu = new CustomThemeMenu(); +grm.gridMenu = new MetadataGridMenu(); + +grm.artCache = new ArtCache(); +grm.button = new Button(); +grm.pseBtn = new PauseButton(); +grm.volBtn = new VolumeButton(); +grm.ttip = new TooltipHandler(); +grm.lowerTip = new LowerBarTooltip(); +grm.gridTip = new MetadataGridTooltip(); + +grm.timeline = new Timeline(); +grm.jSearch = new JumpSearch(); +grm.progBar = new ProgressBar(); +grm.peakBar = new PeakmeterBar(); +grm.waveBar = new WaveformBar(); + +grm.lyrics = new Lyrics(); + + +///////////////////////////// +// ! MAIN INITIALIZATION ! // +///////////////////////////// +if (grSet.systemFirstLaunch) { + grm.ui.initFonts(); + grm.ui.systemFirstLaunch(); +} else { + grm.ui.initFonts(); + grm.ui.initMain(); +} + +/** + * Called when drawing graphics. + * Draws the main user interface. + * @global + * @param {GdiGraphics} gr - The GDI graphics object. + */ +function on_paint(gr) { + grm.ui.drawMain(gr); +} diff --git a/profile/georgia-reborn/scripts/Base/gr-lyrics.js b/profile/georgia-reborn/scripts/Base/gr-lyrics.js index 61d1f5bd..3e84bccc 100644 --- a/profile/georgia-reborn/scripts/Base/gr-lyrics.js +++ b/profile/georgia-reborn/scripts/Base/gr-lyrics.js @@ -1,90 +1,65 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Lyrics * // -// * Author: TT * // -// * Org. Author: Mordred + WilB * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Lyrics * // +// * Author: TT * // +// * Org. Author: Mordred + WilB * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////// -// * VARIABLES * // -/////////////////// -/** @type {Lyrics} */ -let lyrics; -/** @type {>} */ -let lyricSource; -/** @type {number} */ -let lyricsSourceQueue = 0; -/** @type {string} */ -let lyricType; - - -/////////////////////// -// * LYRICS OBJECT * // -/////////////////////// +//////////////// +// * LYRICS * // +//////////////// /** - * Loads and displays lyrics on album art in the Lyrics panel from the cache directory or embedded files. + * A class that loads and displays lyrics on album art in the Lyrics panel + * from the cache directory or embedded files. */ class Lyrics { + /** + * Creates the `Lyrics` instance. + * Initializes variables and settings for loading and displaying lyrics. + */ constructor() { - /** @type {>} */ + /** @private @type {Array} An array to hold individual lyrics. */ this.lyr = []; - /** @type {>} */ + /** @private @type {Array} An array to hold the lyrics in a structured format. */ this.lyrics = []; - /** @type {>} */ - this.timestamps = /(\s*)\[(\d{1,2}:|)\d{1,2}:\d{2}(]|\.\d{1,3}])(\s*)/g; - /** @type {>} */ + /** @private @type {Array} An array to hold the sources of lyrics. */ + this.lyricSource = []; + /** @private @type {string} The type of the lyrics, `.lrc` for synced and `.txt` for unsynced. */ + this.lyricType = ''; + /** @private @type {number} A counter to manage lyrics source loading queue. */ + this.lyricsSourceQueue = 0; + /** @private @type {RegExp} A regular expression to match standard timestamps in lyrics. */ + this.timestamps = /(\s*)\[(\d{1,2}:|)\d{1,2}:\d{2}(]|\.\d{1,3}])(\s*)/g; + /** @private @type {RegExp} A regular expression to match enhanced timestamps in lyrics. */ this.enhancedTimestamps = /(\s*)<(\d{1,2}:|)\d{1,2}:\d{2}(>|\.\d{1,3}>)(\s*)/g; - /** @type {>} */ - this.leadingTimestamps = /^(\s*\[(\d{1,2}:|)\d{1,2}:\d{2}(]|\.\d{1,3}]))+/; - /** @type {number} */ + /** @private @type {RegExp} A regular expression to match leading timestamps in lyrics. */ + this.leadingTimestamps = /^(\s*\[(\d{1,2}:|)\d{1,2}:\d{2}(]|\.\d{1,3}]))+/; + /** @private @type {number} A time step value for manual adjusting lyrics synchronization. */ this.stepTime = 0; - /** @type {>} */ - this.stringNoLyrics = ['No lyrics found']; - /** @type {>} */ - this.stringNotFound = ['Search completed\nNo lyrics were found']; - /** @type {>} */ + /** @private @type {Array} A message displayed when no lyrics are found. */ + this.stringNoLyrics = ['No lyrics found']; + /** @private @type {Array} A message displayed when the search is completed but no lyrics were found. */ + this.stringNotFound = ['Search completed\nNo lyrics were found']; + /** @private @type {Array} A message displayed when the lyrics search is in progress. */ this.stringSearching = ['Searching for lyrics...\nPlease wait...']; - /** @type {number} */ + /** @private @type {number} A TitleFormat object to get the track length in seconds. */ this.tfLength = fb.TitleFormat('%length_seconds%'); - /** @type {number} */ + /** @private @type {number} A padding value for text display. */ this.textPad = SCALE(12); - /** @type {GdiFont} */ - this.font = { lyrics: pref.lyricsLargerCurrentSync ? ft.lyricsHighlight : ft.lyrics }; - - /** @type {GdiGraphics} */ - GR(1, 1, false, g => { - this.font.lyrics_h = Math.round(g.CalcTextHeight('STRING', this.font.lyrics) + (pref.lyricsLargerCurrentSync ? 0 : this.textPad)); - }); this.clear(); - - /** Changes current lyrics when changing the lyrics source, only used for ESLyric "Lyric search". */ - const changeLyrics = (meta) => { - this.clear(); - if (!meta) return; - - lyricSource = [meta.lyricText]; - lyricSource = lyricSource.map(line => line.split('\n')).flat(); - - const timestamps = /\[\d{2}:\d{2}\.\d{2}\]/; - lyricType = timestamps.test(lyricSource) ? 'lrc' : 'txt'; - - this.loadLyrics(lyricSource); - }; - const esl = new ActiveXObject('eslyric'); - esl.SetPlayingLyricChangedCallback(changeLyrics); // Start ESLyric callback listener when lyrics change } - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Initializes the lyrics as follows: * - Loads them from cache if found locally or from embedded tags. @@ -93,11 +68,14 @@ class Lyrics { */ initLyrics() { let rawLyrics = []; - const embeddedLyrics = $(tf.lyrics); + const embeddedLyrics = $(grTF.lyrics); const foundLyrics = this.findLyrics(); + // * Start ESLyric callback listener when lyrics change + esl.SetPlayingLyricChangedCallback(this.changeLyrics.bind(this)); + if (foundLyrics) { - if (pref.displayLyrics) console.log('Found Lyrics:', this.fileName); + if (grm.ui.displayLyrics) console.log('Found Lyrics:', this.fileName); rawLyrics = utils.ReadTextFile(this.fileName, 65001).split('\n'); } else if (embeddedLyrics.length) { @@ -116,11 +94,13 @@ class Lyrics { if (rawLyrics.length) { // * Lyrics found this.loadLyrics(rawLyrics); - } else if (componentESLyric) { // * No lyrics found locally, searching... + } else if (Component.ESLyric) { // * No lyrics found locally, searching... this.searchLyrics(); } else { // * Search completed, no lyrics were found this.loadLyrics(this.stringNoLyrics); } + + this.on_size(grm.ui.albumArtSize.x, grm.ui.albumArtSize.y, grm.ui.albumArtSize.w, grm.ui.albumArtSize.h); } /** @@ -132,12 +112,12 @@ class Lyrics { const tpath = []; const tfilename = []; const stripReservedChars = (filename) => filename.replace(/[<>:"/\\|?*]/g, '_'); - const lyricPaths = pref.customLyricsDir ? globals.customLyricsDir : globals.lyr_path; + const lyricPaths = grSet.customLyricsDir ? grCfg.customLyricsDir : grPath.lyricsPath; for (const path of lyricPaths) { tpath.push($($Escape(path))); } - for (const filename of globals.lyricFilenamePatterns) { + for (const filename of grCfg.lyricsFilenamePatterns) { tfilename.push(stripReservedChars($(filename))); } @@ -155,11 +135,13 @@ class Lyrics { /** * Checks if the lyrics file exists at path+filename and sets this.fileName if it does. - * @param {string} path The lyric file path - * @param {string} filename The lyric file name + * @param {string} path - The lyric file path. + * @param {string} filename - The lyric file name. + * @returns {boolean} True if the file exists and this.fileName is set, false otherwise. */ checkLyrics(path, filename) { - const types = [lyricType, 'lrc', 'txt']; + this.lyricType = this.timestamps.test(this.lyricSource) ? 'lrc' : 'txt'; + const types = [this.lyricType, 'lrc', 'txt']; for (const type of types) { const currentFile = `${path + filename}.${type}`; @@ -183,46 +165,57 @@ class Lyrics { this.searchTimeout = setTimeout(() => { if (!this.findLyrics()) { this.loadLyrics(this.stringNotFound); - this.on_size(albumArtSize.x, albumArtSize.y, albumArtSize.w, albumArtSize.h); + this.on_size(grm.ui.albumArtSize.x, grm.ui.albumArtSize.y, grm.ui.albumArtSize.w, grm.ui.albumArtSize.h); } clearInterval(this.lyricsSearchTimer); }, 60000); } /** - * Cycles through the next lyric source in the search results, used for ESLyric's "Next lyric" feature. + * Changes current lyrics when changing the lyrics source, used only for ESLyric "Lyric search". + * @param {object} meta - The metadata object containing the new lyrics text. + */ + changeLyrics(meta) { + this.clear(); + if (!meta) return; + + this.lyricSource = [meta.lyricText]; + this.lyricSource = this.lyricSource.map(line => line.split('\n')).flat(); + + this.loadLyrics(this.lyricSource); + setTimeout(() => { this.initLyrics(); }, 1000); + } + + /** + * Cycles through the next lyric source in the search results, used only for ESLyric's "Next lyric" feature. */ nextLyrics() { const nextSrc = (meta) => { - fb.RunMainMenuCommand('View/ESLyric/Panels/Delete lyric'); - lyricsSourceQueue++; + this.lyricsSourceQueue++; RepeatFunc(() => { fb.RunMainMenuCommand('View/ESLyric/Panels/Select lyric/Next lyric'); if (!meta) return; - lyricSource = [meta.lyricText]; - lyricSource = lyricSource.map(line => line.split('\n')).flat(); + this.lyricSource = [meta.lyricText]; + this.lyricSource = this.lyricSource.map(line => line.split('\n')).flat(); + }, this.lyricsSourceQueue); - const timestamps = /\[\d{2}:\d{2}\.\d{2}\]/; - lyricType = timestamps.test(lyricSource) ? 'lrc' : 'txt'; - }, lyricsSourceQueue); - - return lyricsSourceQueue; + return this.lyricsSourceQueue; }; nextSrc(); setTimeout(() => { fb.RunMainMenuCommand('View/ESLyric/Panels/Save lyric'); }, 1000); - setTimeout(() => { initLyrics(); }, 1000); + setTimeout(() => { grm.lyrics.initLyrics(); }, 1000); } /** * Automatically saves the lyrics file to cache once it was successfully found. - * @param {FbMetadbHandle} metadb The metadb of the track. + * @param {FbMetadbHandle} metadb - The metadb of the track. */ saveLyrics(metadb) { clearInterval(this.lyricsSearchTimer); - if (!componentESLyric && !metadb) return; + if (!Component.ESLyric && !metadb) return; this.lyricsSearchTimer = setInterval(() => { if (this.findLyrics()) { @@ -236,7 +229,7 @@ class Lyrics { /** * Initializes various variables and settings for displaying lyrics and sends them to parse. - * @param {string} lyr The lyrics that will be loaded and displayed. + * @param {string} lyr - The lyrics that will be loaded and displayed. */ loadLyrics(lyr) { const newLyrics = !Equal(lyr, this.lyr); @@ -245,19 +238,27 @@ class Lyrics { this.userOffset = 0; } - this.x = albumArtSize.x + SCALE(40); - this.y = albumArtSize.y + SCALE(40); - this.w = albumArtSize.w - SCALE(80); - this.h = albumArtSize.h - SCALE(80); - if (noAlbumArtStub) resetLyricsPosition(); + /** @type {GdiFont} */ + this.font = { lyrics: grSet.lyricsLargerCurrentSync ? grFont.lyricsHighlight : grFont.lyrics }; + + /** @type {GdiGraphics} */ + GDI(1, 1, false, g => { + this.font.lyrics_h = Math.round(g.CalcTextHeight('STRING', this.font.lyrics) + (grSet.lyricsLargerCurrentSync ? 0 : this.textPad)); + }); + + this.x = grm.ui.albumArtSize.x + SCALE(40); + this.y = grm.ui.albumArtSize.y + SCALE(40); + this.w = grm.ui.albumArtSize.w - SCALE(80); + this.h = grm.ui.albumArtSize.h - SCALE(80); + if (grm.ui.noAlbumArtStub) this.resetLyricsPosition(); this.alignCenter = StringFormat(1, 1); this.alignRight = StringFormat(2, 1); this.init = true; this.lineHeight = this.font.lyrics_h; this.arc = SCALE(6); - this.lyricsScrollTimeMax = pref.lyricsScrollRateAvg * 0.5; - this.lyricsScrollTimeAvg = pref.lyricsScrollRateMax; + this.lyricsScrollTimeMax = grSet.lyricsScrollRateAvg * 0.5; + this.lyricsScrollTimeAvg = grSet.lyricsScrollRateMax; this.lyricsScrollMaxMethod = 0; this.durationScroll = this.lyricsScrollMaxMethod ? this.lyricsScrollTimeMax : Math.round(this.lyricsScrollTimeAvg * 2 / 3); this.factor = this.durationScroll < 1500 ? 20 : 24; @@ -269,7 +270,7 @@ class Lyrics { this.minDurationScroll = Math.min(this.durationScroll, 250); this.newHighlighted = false; this.scroll = 0; - this.dropShadowLevel = pref.lyricsDropShadowLevel; + this.dropShadowLevel = grSet.lyricsDropShadowLevel; this.dropNegativeShadowLevel = this.dropShadowLevel > 1 ? Math.floor(this.dropShadowLevel / 2) : 0; this.shadowEffect = this.dropShadowLevel > 0; this.showOffsetTimer = null; @@ -294,7 +295,7 @@ class Lyrics { /** * Determines if the lyrics are synced or unsynced, formats and parses them accordingly. - * @param {string} lyr The lyrics that need to be parsed. + * @param {string} lyr - The lyrics that need to be parsed. */ parseLyrics(lyr) { if (!lyr.length) { @@ -318,7 +319,7 @@ class Lyrics { } case this.type.unsynced: { this.formatLyrics(this.parseUnsyncedLyrics(lyr, this.type.none)); - const ratio = isStreaming ? 2000 : this.trackLength / this.lyrics.length * 1000; + const ratio = grm.ui.isStreaming ? 2000 : this.trackLength / this.lyrics.length * 1000; for (const [i, line] of this.lyrics.entries()) line.timestamp = ratio * i; break; } @@ -329,8 +330,8 @@ class Lyrics { /** * Parses synced lyrics that contain timestamps and content of each line, sorted by timestamp. - * @param {string} lyr The lyrics of the song. Each string in the array represents a line of lyric. - * @param {boolean} isNone Indicates if the lyrics array starts with a timestamp or not. + * @param {string} lyr - The lyrics of the song. Each string in the array represents a line of lyric. + * @param {boolean} isNone - Indicates if the lyrics array starts with a timestamp or not. * @returns {Array} An array of objects with properties `timestamp` and `content`. */ parseSyncLyrics(lyr, isNone) { @@ -351,9 +352,9 @@ class Lyrics { /** * Parses unsynced lyrics when there are no timestamps included. - * @param {string} lyr The lyrics of the song. - * @param {boolean} isNone The first line of lyrics should be treated as a timestamp or not. - * @returns {Object} An array of objects with "timestamp" and "content" properties for each line of the lyric. + * @param {string} lyr - The lyrics of the song. + * @param {boolean} isNone - The first line of lyrics should be treated as a timestamp or not. + * @returns {object} An array of objects with "timestamp" and "content" properties for each line of the lyric. */ parseUnsyncedLyrics(lyr, isNone) { const lyrics = []; @@ -366,13 +367,13 @@ class Lyrics { /** * Formats the lyrics with synchronization and line wrapping. - * @param {string} lyrics An array of objects with the properties "content" and "timestamp". - * @param {boolean} isSynced Whether the lyrics are synced with the audio or not. + * @param {string} lyrics - An array of objects with the properties "content" and "timestamp". + * @param {boolean} isSynced - Whether the lyrics are synced with the audio or not. */ formatLyrics(lyrics, isSynced) { if (lyrics.length && this.w > 10) { if (isSynced && lyrics[0].content && lyrics[0].timestamp > this.durationScroll) lyrics.unshift({ timestamp: 0, content: '' }); - GR(1, 1, false, g => { + GDI(1, 1, false, g => { for (let i = 0; i < lyrics.length; i++) { const l = g.EstimateLineWrap(lyrics[i].content, this.font.lyrics, this.w - 10); if (l[1] > this.maxLyrWidth) this.maxLyrWidth = l[1]; @@ -402,39 +403,39 @@ class Lyrics { /** * Defines the draw area when lyrics should be displayed. - * @param {number} y The distance from the top of the screen to the top of the lyrics. - * @param {number} top The top position of the lyrics display area. + * @param {number} y - The distance from the top of the screen to the top of the lyrics. + * @param {number} top - The top position of the lyrics display area. * @returns {boolean} True if the given `y` value is within the range of `top` and `top` plus the line height. */ displayLyrics(y, top) { - return y >= top && y + (pref.lyricsFadeScroll ? this.lineHeight : 0) <= this.h + top; + return y >= top && y + (grSet.lyricsFadeScroll ? this.lineHeight : 0) <= this.h + top; } /** * Draws the lyrics on screen and also applies various visual effects such as fading and drop shadows. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ drawLyrics(gr) { - if (!(pref.displayLyrics && this.lyrics.length && this.locus >= 0)) return; + if (!(grm.ui.displayLyrics && this.lyrics.length && this.locus >= 0)) return; const top = this.locus * this.lineHeight - this.locusOffset; const transition_factor = Clamp((this.lineHeight - this.scroll) / this.lineHeight, 0, 1); const transition_factor_in = !this.lyrics[this.locus].multiLine ? transition_factor : 1; const transition_factor_out = Clamp(transition_factor_in * 3, 0, 1); const alpha = Math.min(255 * transition_factor * 4 / 3, 255); - const blendIn = this.type.synced ? GetBlend(col.lyricsHighlight, col.lyricsNormal, transition_factor_in) : col.lyricsNormal; - const blendOut = this.type.synced ? GetBlend(col.lyricsNormal, col.lyricsHighlight, transition_factor_out) : col.lyricsNormal; + const blendIn = this.type.synced ? GetBlend(grCol.lyricsHighlight, grCol.lyricsNormal, transition_factor_in) : grCol.lyricsNormal; + const blendOut = this.type.synced ? GetBlend(grCol.lyricsNormal, grCol.lyricsHighlight, transition_factor_out) : grCol.lyricsNormal; const y = this.y + this.scroll; - let color = col.lyricsNormal; + let color = grCol.lyricsNormal; - let fadeBot = pref.lyricsFadeScroll ? this.transBot[transition_factor] : color; + let fadeBot = grSet.lyricsFadeScroll ? this.transBot[transition_factor] : color; if (!fadeBot) { fadeBot = RGBtoRGBA(color, alpha); this.transBot[transition_factor] = fadeBot; } - let fadeTop = pref.lyricsFadeScroll ? this.transTop[transition_factor] : color; + let fadeTop = grSet.lyricsFadeScroll ? this.transTop[transition_factor] : color; if (!fadeTop) { fadeTop = RGBtoRGBA(color, 255 - alpha); this.transTop[transition_factor] = fadeTop; @@ -443,31 +444,31 @@ class Lyrics { gr.SetTextRenderingHint(5); for (const [i, lyric] of this.lyrics.entries()) { - const font = lyric.highlight && pref.lyricsLargerCurrentSync ? ft.lyricsHighlight : ft.lyrics; + const font = lyric.highlight && grSet.lyricsLargerCurrentSync ? grFont.lyricsHighlight : grFont.lyrics; const lyric_y = this.lineHeight * i; const line_y = Math.round(y - top + lyric_y - ((this.stringSearching || this.stringNotFound) ? SCALE(24) : 0)); - const bottomLine = line_y > this.bot + this.lineHeight * (pref.lyricsFadeScroll ? 2 : 3); + const bottomLine = line_y > this.bot + this.lineHeight * (grSet.lyricsFadeScroll ? 2 : 3); if (!this.displayLyrics(lyric_y, top)) continue; if (this.shadowEffect && line_y >= this.top && !bottomLine) { // * Do not show drop shadow at top and bottom if (this.dropNegativeShadowLevel) { - gr.DrawString(lyric.content, font, col.lyricsShadow, this.x - this.dropNegativeShadowLevel, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); - gr.DrawString(lyric.content, font, col.lyricsShadow, this.x, line_y - this.dropNegativeShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, grCol.lyricsShadow, this.x - this.dropNegativeShadowLevel, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, grCol.lyricsShadow, this.x, line_y - this.dropNegativeShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); } - gr.DrawString(lyric.content, font, col.lyricsShadow, this.x + this.dropShadowLevel, line_y + this.dropShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, grCol.lyricsShadow, this.x + this.dropShadowLevel, line_y + this.dropShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); } - color = line_y >= this.top ? lyric.highlight ? blendIn : i === this.locus - 1 ? blendOut : bottomLine ? fadeBot : col.lyricsNormal : fadeTop; + color = line_y >= this.top ? lyric.highlight ? blendIn : i === this.locus - 1 ? blendOut : bottomLine ? fadeBot : grCol.lyricsNormal : fadeTop; gr.DrawString(lyric.content, font, color, this.x, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); } if (this.showOffset) { - this.offsetW = gr.CalcTextWidth(`Offset: ${this.userOffset / 1000}s`, ft.notification) + this.lineHeight; - gr.FillRoundRect(this.x + this.w - this.offsetW, this.y, this.offsetW, this.lineHeight + 1, this.arc, this.arc, col.popupBg); + this.offsetW = gr.CalcTextWidth(`Offset: ${this.userOffset / 1000}s`, grFont.notification) + this.lineHeight; + gr.FillRoundRect(this.x + this.w - this.offsetW, this.y, this.offsetW, this.lineHeight + 1, this.arc, this.arc, grCol.popupBg); gr.DrawRoundRect(this.x + this.w - this.offsetW, this.y, this.offsetW, this.lineHeight + 1, this.arc, this.arc, 1, 0x64000000); - gr.DrawString(`Offset: ${this.userOffset / 1000}s`, ft.notification, col.popupText, this.x - this.lineHeight * 0.5, this.y, this.w, this.lineHeight + 1, this.alignRight); + gr.DrawString(`Offset: ${this.userOffset / 1000}s`, grFont.notification, grCol.popupText, this.x - this.lineHeight * 0.5, this.y, this.w, this.lineHeight + 1, this.alignRight); } } @@ -515,6 +516,7 @@ class Lyrics { /** * Gets the index of the current lyric line. + * @returns {number} The index of the current lyric line based on the playback time. */ getCurPos() { return this.lyrics.findIndex(v => v.timestamp >= this.playbackTime()); @@ -522,7 +524,7 @@ class Lyrics { /** * Converts timestamped lyrics to milliseconds. - * @param {string} t The timestamped string. + * @param {string} t - The timestamped string. * @returns {number} The time in milliseconds. */ getMilliseconds(t) { @@ -532,7 +534,6 @@ class Lyrics { /** * Gets the duration of the scroll animation. - * @returns {number} The duration of the scroll animation in milliseconds. */ getScrollSpeed() { let { durationScroll } = this; @@ -565,7 +566,7 @@ class Lyrics { /** * Gets the timestamp for a given word. - * @param {string} v The word to get the timestamp for. + * @param {string} v - The word to get the timestamp for. * @returns {number} The timestamp for the word, or undefined if the word is not found. */ getTimestamp(v) { @@ -577,7 +578,7 @@ class Lyrics { * @returns {number} The current playback time in milliseconds. */ playbackTime() { - const time = isStreaming ? fb.PlaybackTime - txt.reader.trackStartTime : fb.PlaybackTime; + const time = grm.ui.isStreaming ? fb.PlaybackTime - bio.txt.reader.trackStartTime : fb.PlaybackTime; return Math.round(time * 1000) + this.lyricsOffset + this.transitionOffset + this.userOffset; } @@ -588,6 +589,36 @@ class Lyrics { window.RepaintRect(this.x, this.y, this.w, this.h); } + /** + * Resets the size and position of the lyrics. + */ + resetLyricsPosition() { + const fullW = grSet.layout === 'default' && grSet.lyricsLayout === 'full' && grm.ui.displayLyrics; + const noAlbumArtSize = grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight; + + const lyricsX = + grm.ui.noAlbumArtStub ? + fullW ? grSet.panelWidthAuto && grSet.lyricsLayout === 'normal' ? noAlbumArtSize * 0.5 : grm.ui.ww * 0.333 : + grSet.panelWidthAuto ? grm.ui.albumArtSize.x : 0 : + grm.ui.albumArtSize.x; + + const lyricsY = grm.ui.noAlbumArtStub ? grm.ui.topMenuHeight : grm.ui.albumArtSize.y; + + const lyricsW = + grm.ui.noAlbumArtStub ? + grSet.layout === 'artwork' ? grm.ui.ww : + fullW ? grm.ui.ww * 0.333 : + grSet.panelWidthAuto ? noAlbumArtSize : + grm.ui.ww * 0.5 : + grm.ui.albumArtSize.w; + + const lyricsH = grm.ui.noAlbumArtStub ? grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight : grm.ui.albumArtSize.h; + + if (grm.lyrics) { + this.on_size(lyricsX, lyricsY, lyricsW, lyricsH); + } + } + /** * Checks if the lyrics array has more than one element and if the playback time is greater than the timestamp of the next lyric. * @returns {boolean} True if the scroll needs to be updated. @@ -621,7 +652,7 @@ class Lyrics { * Smoothly scrolls the lyrics to the next lyric on scroll animation. */ smoothScroll() { - if (!pref.displayLyrics) return; + if (!grm.ui.displayLyrics) return; if (this.scrollUpdateNeeded()) { this.advanceHighLighted(); @@ -652,18 +683,19 @@ class Lyrics { /** * Tidy ups a string by removing all timestamps and enhanced timestamps. - * @param {!string|number} n The string containing timestamped data which needs cleaning up. + * @param {!string|number} n - The string containing timestamped data which needs cleaning up. * @returns {!string} A cleaned version of the original string without any timestamps. */ tidy(n) { return n.replace(this.timestamps, '$1$4').replace(this.enhancedTimestamps, '$1$4').trim(); } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Adjusts the user offset, updates the display, and seeks to the new position. - * @param {number} step The amount of scrolling offset that should be performed. + * @param {number} step - The amount of scrolling offset that should be performed. */ on_mouse_wheel(step) { step *= Clamp(Math.round(500 / ((Date.now() - this.stepTime) * 5)), 1, 5); @@ -681,7 +713,7 @@ class Lyrics { /** * Checks if the playback is paused and stops or starts accordingly. - * @param {boolean} isPaused Wether playback is currently paused or not. + * @param {boolean} isPaused - Wether playback is currently paused or not. */ on_playback_pause(isPaused) { if (isPaused) this.stop(); @@ -690,7 +722,7 @@ class Lyrics { /** * Stops the playback. - * @param {number} reason The type of playback stop. + * @param {number} reason - The type of playback stop. */ on_playback_stop(reason) { this.stop(); @@ -698,10 +730,10 @@ class Lyrics { /** * Adjusts the size and position of the lyrics object and repaints it. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. */ on_size(x, y, w, h) { this.x = x + SCALE(40); @@ -710,4 +742,5 @@ class Lyrics { this.h = h - SCALE(80); this.repaintRect(); } + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-main-components.js b/profile/georgia-reborn/scripts/Base/gr-main-components.js index 6a734fce..2bdbbb92 100644 --- a/profile/georgia-reborn/scripts/Base/gr-main-components.js +++ b/profile/georgia-reborn/scripts/Base/gr-main-components.js @@ -1,13 +1,13 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Main Components * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Main Components * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; @@ -17,42 +17,49 @@ // * IMAGE CACHING * // /////////////////////// /** - * @typedef {Object} ArtCacheObj - * @property {GdiBitmap} image - * @property {number} filesize - */ - -/** - * Caches album art and playlist thumbnail images. + * A class that creates album art and playlist thumbnails cache. */ class ArtCache { /** - * Creates ArtCache. ArtCache is a Least-Recently Used cache meaning that each cache hit will bump + * Creates the `ArtCache` instance. + * The ArtCache is a Least-Recently Used cache meaning that each cache hit will bump * that image to be the last image to be removed from the cache (if maxCacheSize is exceeded). - * @param {number} maxCacheSize The maximum number of images to keep in the cache. + * @param {number} maxCacheSize - The maximum number of images to keep in the cache. */ - constructor(maxCacheSize) { - /** @private @type {Object.} */ + constructor(maxCacheSize = 15) { + /** + * @typedef {object} ArtCacheObj + * @property {GdiBitmap} image - The GDI+ bitmap image object cached. + * @property {number} filesize - The size of the image file in bytes. + */ + + /** @private @type {object.} */ this.cache = {}; /** @private @type {string[]} */ this.cacheIndexes = []; - /** @private */ this.cacheMaxSize = maxCacheSize; - /** @private */ this.imgMaxWidth = SCALE(1440); // * These are the maximum width and height an image can be displayed in Georgia-ReBORN - /** @private */ this.imgMaxHeight = SCALE(872); - - // The second cache, mainly used for discArtCover to prevent ovewrite albumArt with the masked image - /** @private @type {Object.} */ + /** @private @type {number} */ + this.cacheMaxSize = maxCacheSize; + /** @private @type {number} */ + this.imgMaxWidth = SCALE(1440); // * These are the maximum width and height an image can be displayed in Georgia-ReBORN + /** @private @type {number} */ + this.imgMaxHeight = SCALE(872); + + // The second cache, mainly used for discArtCover to prevent overwrite albumArt with the masked image + /** @private @type {object.} */ this.cache2 = {}; /** @private @type {string[]} */ this.cacheIndexes2 = []; - /** @private */ this.cacheMaxSize2 = maxCacheSize; + /** @private @type {number} */ + this.cacheMaxSize2 = maxCacheSize; } + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Gets cached image if it exists under the location string. If image is found, move it's index to the end of the cacheIndexes. - * @param {string} location The string value to check if image is cached under. - * @param {number} cacheIndex The first or second index of the cache to check. - * @returns {GdiBitmap} The cached image. + * @param {string} location - The string value to check if image is cached under. + * @param {number} cacheIndex - The first or second index of the cache to check. + * @returns {GdiBitmap|null} The cached image, or null if not found or the file does not exist. */ getImage(location, cacheIndex = 1) { const cache = cacheIndex === 1 ? this.cache : this.cache2; @@ -61,7 +68,7 @@ class ArtCache { if (cache[location]) { if (!fso.FileExists(location)) { // If image in location does not exist, return to prevent crash. - return; + return null; } const f = fso.GetFile(location); @@ -84,9 +91,9 @@ class ArtCache { /** * Adds a rescaled image to the cache under string `location` and returns the cached image. - * @param {GdiBitmap} img The image object to cache. - * @param {string} location The string value to cache image under. Does not need to be a path. - * @param {number} cacheIndex The first or second index of the cache to check. + * @param {GdiBitmap} img - The image object to cache. + * @param {string} location - The string value to cache image under. Does not need to be a path. + * @param {number} cacheIndex - The first or second index of the cache to check. * @returns {GdiBitmap} The image stored in the cache at the specified location. * If there is no image in the cache at that location, it returns the original image passed as a parameter. */ @@ -122,7 +129,7 @@ class ArtCache { } } catch (e) { // Do not console.log inverted band logo and label images in the process of being created - if (invertedBandLogo) console.log(`\n\n`); + if (grm.ui.bandLogoInverted) console.log(`\n\n`); } if (cache[location]) { @@ -148,6 +155,7 @@ class ArtCache { delete this.cache2[remove]; } } + // #endregion } @@ -155,23 +163,60 @@ class ArtCache { // * MENU * // ////////////// /** - * Creates menus, submenus, radio groups, toggle items, etc. + * A class that creates menus, submenus, radio groups, toggle items, etc. */ class Menu { /** - * @param {string=} title The title of the menu item. It is optional and defaults to an empty string if not provided. + * Creates the `Menu` instance. + * @param {string} [title] - The title of the menu item. It is optional and defaults to an empty string if not provided. */ constructor(title = '') { - _MenuItemIndex++; + if (!Menu.menuItemIndex) { + Menu.menuStartIndex = 100; + Menu.menuItemIndex = Menu.menuStartIndex; + Menu.menuCallbacks = []; + Menu.menuVariables = []; + } + + /** @private @type {number} Auto-incrementing index for each menu item created. */ + Menu.menuItemIndex++; + /** @private @type {PopupMenu} The instance of the popup menu created for this menu. */ this.menu = window.CreatePopupMenu(); + /** @private @type {string} The title of the menu item. */ this.title = title; + /** @private @type {boolean} Indicates if the menu is a system menu. */ this.systemMenu = false; + /** @private @type {LibMenuManager|null} A reference to the menu manager handling this menu, if any. */ this.menuManager = null; } + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Adds a menu item with a label, checked state, callback function, and optional variable to a menu. + * @param {string} label - The text that will be displayed for the menu item. + * @param {boolean} checked - Whether the menu item should be checked. + * @param {*} variable - A variable which will be passed to callback when item is clicked. + * @param {Function} callback - A function that will be executed when the menu item is clicked. + * @param {boolean} disabled - Whether the item should be disabled or not. + * @private + */ + _addItemWithVariable(label, checked, variable, callback, disabled) { + this.menu.AppendMenuItem(MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), Menu.menuItemIndex, label); + this.menu.CheckMenuItem(Menu.menuItemIndex, checked); + Menu.menuCallbacks[Menu.menuItemIndex] = callback; + if (typeof variable !== 'undefined') { + Menu.menuVariables[Menu.menuItemIndex] = variable; + } + Menu.menuItemIndex++; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Creates the default foobar menu corresponding to `name`. - * @param {string} name The name of the menu. + * @param {string} name - The name of the menu. */ initFoobarMenu(name) { if (!name) return; @@ -181,31 +226,24 @@ class Menu { this.menuManager.BuildMenu(this.menu, 1, 1000); } - /** - * Adds a separator to the menu. - */ - addSeparator() { - this.menu.AppendMenuSeparator(); - } - /** * Adds an item with a label, checked status, callback function, and optional disabled status. - * @param {string} label The label for the item. - * @param {boolean} checked Whether the menu item should be checked. - * @param {Function} callback A function that will be executed when the item is clicked. - * @param {boolean=} [disabled=false] Whether the item should be disabled or not. + * @param {string} label - The label for the item. + * @param {boolean} checked - Whether the menu item should be checked. + * @param {Function} callback - A function that will be executed when the item is clicked. + * @param {boolean} [disabled] - Whether the item should be disabled or not. */ addItem(label, checked, callback, disabled = false) { - this.addItemWithVariable(label, checked, undefined, callback, disabled); + this._addItemWithVariable(label, checked, undefined, callback, disabled); } /** * Adds a toggle item to a list with a label, properties object, property name, callback function, and disabled state. - * @param {string} label The label for the item. - * @param {object} propertiesObj An object which contains propertyName. - * @param {string} propertyName The name of the property to toggle on/off. - * @param {?Function} callback A function that will be executed when the item is clicked. - * @param {?boolean=} [disabled=false] Whether the item should be disabled or not. + * @param {string} label - The label for the item. + * @param {object} propertiesObj - An object which contains propertyName. + * @param {string} propertyName - The name of the property to toggle on/off. + * @param {?Function} callback - A function that will be executed when the item is clicked. + * @param {?boolean} [disabled] - Whether the item should be disabled or not. */ addToggleItem(label, propertiesObj, propertyName, callback = () => { }, disabled = false) { this.addItem(label, propertiesObj[propertyName], () => { @@ -218,59 +256,59 @@ class Menu { /** * Creates a set of toggled items and checks the value specified. - * @param {string[]} labels The label for each item. - * @param {*} selectedValue The value of the item to be checked. - * @param {*[]} variables An array of values which correspond to each entry. - * @param {Function} callback A function that will be executed when the item is clicked. - * @param {boolean} [disabled=false] Whether the item should be disabled or not. + * @param {string[]} labels - The label for each item. + * @param {*} selectedValues - The value of the item to be checked. + * @param {*[]} variables - An array of values which correspond to each entry. + * @param {Function} callback - A function that will be executed when the item is clicked. + * @param {boolean} [disabled] - Whether the item should be disabled or not. */ addToggleItems(labels, selectedValues, variables, callback = () => { }, disabled = false) { for (let i = 0; i < labels.length; i++) { - this.menu.AppendMenuItem(MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), _MenuItemIndex, labels[i]); - _MenuCallbacks[_MenuItemIndex] = callback; - _MenuVariables[_MenuItemIndex] = variables[i]; + this.menu.AppendMenuItem(MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), Menu.menuItemIndex, labels[i]); + Menu.menuCallbacks[Menu.menuItemIndex] = callback; + Menu.menuVariables[Menu.menuItemIndex] = variables[i]; if (selectedValues.includes(variables[i])) { - this.menu.CheckMenuItem(_MenuItemIndex, true); + this.menu.CheckMenuItem(Menu.menuItemIndex, true); } - _MenuItemIndex++; + Menu.menuItemIndex++; } } /** * Creates a set of radio items and checks the value specified. - * @param {string[]} labels The label for each radio item. - * @param {*} selectedValue The value of the radio item to be checked. - * @param {*[]} variables An array of values which correspond to each radio entry. - * @param {Function} callback A function that will be executed when the item is clicked. - * @param {boolean} [disabled=false] Whether the item should be disabled or not. - * @param {boolean} [disableCheckMarking=false] Whether the radio check marking should be disabled or not. + * @param {string[]} labels - The label for each radio item. + * @param {*} selectedValue - The value of the radio item to be checked. + * @param {*[]} variables - An array of values which correspond to each radio entry. + * @param {Function} callback - A function that will be executed when the item is clicked. + * @param {boolean} [disabled] - Whether the item should be disabled or not. + * @param {boolean} [disableCheckMarking] - Whether the radio check marking should be disabled or not. */ addRadioItems(labels, selectedValue, variables, callback = () => { }, disabled = false, disableCheckMarking = false) { - const startIndex = _MenuItemIndex; + const startIndex = Menu.menuItemIndex; let selectedIndex; for (let i = 0; i < labels.length; i++) { - this.menu.AppendMenuItem(MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), _MenuItemIndex, labels[i]); - _MenuCallbacks[_MenuItemIndex] = callback; - _MenuVariables[_MenuItemIndex] = variables[i]; + this.menu.AppendMenuItem(MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), Menu.menuItemIndex, labels[i]); + Menu.menuCallbacks[Menu.menuItemIndex] = callback; + Menu.menuVariables[Menu.menuItemIndex] = variables[i]; if (selectedValue === variables[i]) { - selectedIndex = _MenuItemIndex; + selectedIndex = Menu.menuItemIndex; } - _MenuItemIndex++; + Menu.menuItemIndex++; } if (!disableCheckMarking && selectedIndex) { - this.menu.CheckMenuRadioItem(startIndex, _MenuItemIndex - 1, selectedIndex); + this.menu.CheckMenuRadioItem(startIndex, Menu.menuItemIndex - 1, selectedIndex); } } /** * Creates a submenu consisting of radio items. - * @param {string} subMenuName The name of the sub menu. - * @param {string[]} labels The label for each radio item. - * @param {*} selectedValue The value of the radio item to be checked. - * @param {*[]} variables An array of values which correspond to each radio entry. - * @param {Function} callback A function that will be executed when the menu item is clicked. - * @param {boolean=} [disabled=false] Whether the item should be disabled or not. - * @param {boolean} [disableCheckMarking=false] Whether the radio check marking should be disabled or not. + * @param {string} subMenuName - The name of the sub menu. + * @param {string[]} labels - The label for each radio item. + * @param {*} selectedValue - The value of the radio item to be checked. + * @param {*[]} variables - An array of values which correspond to each radio entry. + * @param {Function} callback - A function that will be executed when the menu item is clicked. + * @param {boolean} [disabled] - Whether the item should be disabled or not. + * @param {boolean} [disableCheckMarking] - Whether the radio check marking should be disabled or not. */ createRadioSubMenu(subMenuName, labels, selectedValue, variables, callback, disabled = false, disableCheckMarking = false) { const subMenu = new Menu(subMenuName); @@ -279,27 +317,16 @@ class Menu { } /** - * Adds a menu item with a label, checked state, callback function, and optional variable to a menu. - * @param {string} label The text that will be displayed for the menu item. - * @param {boolean} checked Whether the menu item should be checked. - * @param {*} variable A variable which will be passed to callback when item is clicked - * @param {Function} callback A function that will be executed when the menu item is clicked. - * @param {boolean} disabled Whether the item should be disabled or not. - */ - addItemWithVariable(label, checked, variable, callback, disabled) { - this.menu.AppendMenuItem(MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), _MenuItemIndex, label); - this.menu.CheckMenuItem(_MenuItemIndex, checked); - _MenuCallbacks[_MenuItemIndex] = callback; - if (typeof variable !== 'undefined') { - _MenuVariables[_MenuItemIndex] = variable; - } - _MenuItemIndex++; + * Adds a separator to the menu. + */ + addSeparator() { + this.menu.AppendMenuSeparator(); } /** * Appends a menu to a parent menu. - * @param {Menu} parentMenu The menu to append the submenu to. - * @param {boolean=} [disabled=false] Whether the menu items should be disabled or not. + * @param {Menu} parentMenu - The menu to append the submenu to. + * @param {boolean} [disabled] - Whether the menu items should be disabled or not. */ appendTo(parentMenu, disabled = false) { this.menu.AppendTo(parentMenu.menu, MF_STRING | (disabled ? MF_DISABLED | MF_GRAYED : 0), this.title); @@ -307,28 +334,290 @@ class Menu { /** * Handles callback and automatically disposes the menu. - * @param {number} idx The value of the menu item's callback to call. Comes from menu.trackPopupMenu(x, y). + * @param {number} idx - The value of the menu item's callback to call. Comes from menu.trackPopupMenu(x, y). */ doCallback(idx) { - if (idx > menuStartIndex && _MenuCallbacks[idx]) { - _MenuCallbacks[idx](_MenuVariables[idx]); + if (idx > Menu.menuStartIndex && Menu.menuCallbacks[idx]) { + Menu.menuCallbacks[idx](Menu.menuVariables[idx]); } else if (this.systemMenu && idx) { this.menuManager.ExecuteByID(idx - 1); this.menuManager = null; } this.menu = null; - // Reset globals as menu is about to be destroyed - _MenuCallbacks = []; - _MenuVariables = []; - _MenuItemIndex = menuStartIndex; + + // Reset static properties as menu is about to be destroyed + Menu.menuCallbacks = []; + Menu.menuVariables = []; + Menu.menuItemIndex = Menu.menuStartIndex; } /** + * Tracks a popup menu at the given coordinates and returns the index of the clicked menu item. + * @param {number} x - The x-coordinate where the menu will be displayed. + * @param {number} y - The y-coordinate where the menu will be displayed. * @returns {number} The index of the menu item clicked on. */ trackPopupMenu(x, y) { return this.menu.TrackPopupMenu(x, y); } + // #endregion +} + + +/////////////////// +// * INPUT BOX * // +/////////////////// +/** + * A class that creates input boxes for allowing users to customize settings. + */ +class InputBox { + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Prompts the user to enter the playlist where tracks will be added when using the add tracks button. + * @throws Will throw an error if the new value is not a string. + */ + addTracksPlaylist() { + const oldValStr = JSON.stringify(grCfg.themeControls.addTracksPlaylist).replace(/"/g, ''); + let newVal; + let input; + try { + input = utils.InputBox(window.ID, 'Enter your new add tracks playlist or an existing playlist with its exact name:', 'Georgia-ReBORN', oldValStr, true); + newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); + if (typeof newVal !== 'string') throw new Error('Invalid type'); + } + catch (e) { + if (e.message === 'Invalid type' || e.name === 'SyntaxError') { + fb.ShowPopupMessage(`Playlist name is not valid:\n${input}\n\nDo not use any " at the beginning and the end of the playlist name.`, 'Add tracks playlist'); + } + return; + } + grCfg.themeControls.addTracksPlaylist = newVal; + grCfg.config.updateConfigObjValues('themeControls', true); + } + + /** + * Prompts the user to enter a custom directory path for various cache types and updates the configuration. + * It supports custom directories for library, biography, lyrics, and waveform bar. + * @param {string} directory - One of the following options to specify the type of cache directory: + * - 'library' - sets the custom Library cache directory. + * - 'biography' - sets the custom Biography cache directory. + * - 'lyrics' - sets the custom Lyrics cache directory. + * - 'waveformBar' - sets the custom Waveform bar cache directory. + * @throws Will throw an error if the new value is not a string. + */ + customCacheDir(directory) { + const dirMap = { + library: { + path: grCfg.customLibraryDir, + string: 'library', + schema: grDef.customLibraryDirSchema + }, + biography: { + path: grCfg.customBiographyDir, + string: 'biography', + schema: grDef.customBiographyDirSchema + }, + lyrics: { + path: grCfg.customLyricsDir, + string: 'lyrics', + schema: grDef.customLyricsDirSchema + }, + waveformBar: { + path: grCfg.customWaveformBarDir, + string: 'waveform', + schema: grDef.customWaveformBarDirSchema + } + }; + + const dirInfo = dirMap[directory] || {}; + const customDirPath = dirInfo.path || ''; + const customDirString = dirInfo.string || ''; + const customDirSchema = dirInfo.schema || ''; + const oldValStr = JSON.stringify(customDirPath).replace(/["[\]]/g, '').replace(/\\\\/g, '\\'); + + let newVal; + let input; + try { + input = utils.InputBox(window.ID, `Enter your custom ${customDirString} directory:`, 'Georgia-ReBORN', oldValStr, true); + newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input.replace(/[\\/]/g, '\\\\')}"`); + if (typeof newVal !== 'string') throw new Error('Invalid type'); + } + catch (e) { + if (e.message === 'Invalid type' || e.name === 'SyntaxError') { + fb.ShowPopupMessage(`Path is not valid:\n${input}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExample of a correct path:\n\nD:\\Stuff\\Directory\\`, `Custom ${customDirString} directory`); + } + return; + } + grCfg.configCustom.addConfigurationObject(customDirSchema, [newVal]); + grCfg.configCustom.writeConfiguration(); + } + + /** + * Prompts the user to enter a new name for the currently active custom theme and updates the configuration. + * It handles renaming for pre-defined custom themes, identified by keys like 'custom01', 'custom02', etc. + * @throws Will throw an error if the new value is not a string. + */ + renameCustomTheme() { + const customThemes = { + custom01: 'customTheme01', + custom02: 'customTheme02', + custom03: 'customTheme03', + custom04: 'customTheme04', + custom05: 'customTheme05', + custom06: 'customTheme06', + custom07: 'customTheme07', + custom08: 'customTheme08', + custom09: 'customTheme09', + custom10: 'customTheme10' + }; + const customTheme = customThemes[grSet.theme] || ''; + + const customThemeNames = { + custom01: grCfg.customTheme01, + custom02: grCfg.customTheme02, + custom03: grCfg.customTheme03, + custom04: grCfg.customTheme04, + custom05: grCfg.customTheme05, + custom06: grCfg.customTheme06, + custom07: grCfg.customTheme07, + custom08: grCfg.customTheme08, + custom09: grCfg.customTheme09, + custom10: grCfg.customTheme10 + }; + const customThemeName = customThemeNames[grSet.theme] || ''; + + let newVal; + let input; + try { + input = utils.InputBox(window.ID, 'Enter your desired name for your current active custom theme', 'Georgia-ReBORN', customThemeName.name, true); + newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); + if (typeof newVal !== 'string') throw new Error('Invalid type'); + } + catch (e) { + if (e.message === 'Invalid type' || e.name === 'SyntaxError') { + fb.ShowPopupMessage(`Name is not valid:\n${input}\n\nSomething went wrong...`, 'Custom theme name'); + } + return; + } + customThemeName.name = newVal; + grCfg.configCustom.updateConfigObjValues(customTheme, true); + } + + /** + * Prompts the user to enter a custom pattern for playlist header information and updates the settings. + * @throws Will throw an error if the new value is not a string. + */ + playlistCustomHeaderInfo() { + const oldValStr = JSON.stringify(grCfg.settings.playlistCustomHeaderInfo).replace(/"/g, ''); + let newVal; + let input; + try { + input = utils.InputBox(window.ID, 'Enter your custom playlist header info pattern:', 'Georgia-ReBORN', oldValStr, true); + newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); + if (typeof newVal !== 'string') throw new Error('Invalid type'); + } + catch (e) { + if (e.message === 'Invalid type' || e.name === 'SyntaxError') { + fb.ShowPopupMessage(`Pattern is not valid:\n${input}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExamples of correct patterns:\n\n%album artist% %date% %album% %discnumber% %tracknumber% %title%\n\n$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%`, 'Custom playlist header info'); + } + return; + } + grCfg.settings.playlistCustomHeaderInfo = newVal; + grCfg.config.updateConfigObjValues('settings', true); + } + + /** + * Prompts the user to enter custom patterns for playlist track rows with and without headers. + * @throws Will throw an error if the new values are not strings. + */ + playlistCustomTrackRow() { + const oldValStr1 = JSON.stringify(grCfg.settings.playlistCustomTitle).replace(/"/g, ''); + const oldValStr2 = JSON.stringify(grCfg.settings.playlistCustomTitleNoHeader).replace(/"/g, ''); + let newVal1; + let newVal2; + let input1; + let input2; + try { + input1 = utils.InputBox(window.ID, 'Enter your custom playlist track row pattern:', 'Georgia-ReBORN', oldValStr1, true); + input2 = utils.InputBox(window.ID, 'Enter your custom playlist track row pattern when no header displayed:', 'Georgia-ReBORN', oldValStr2, true); + newVal1 = !input1 || typeof input1 !== 'string' && !input1.length ? '' : JSON.parse(`"${input1}"`); + newVal2 = !input2 || typeof input2 !== 'string' && !input2.length ? '' : JSON.parse(`"${input2}"`); + if (typeof newVal1 !== 'string') throw new Error('Invalid type'); + if (typeof newVal2 !== 'string') throw new Error('Invalid type'); + } + catch (e) { + if (e.message === 'Invalid type' || e.name === 'SyntaxError') { + fb.ShowPopupMessage(`Pattern is not valid:\n${input1 || input2}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExamples of correct patterns:\n\n%album artist% %date% %album% %discnumber% %tracknumber% %title%\n\n$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%`, 'Custom playlist track row'); + } + return; + } + grCfg.settings.playlistCustomTitle = newVal1; + grCfg.settings.playlistCustomTitleNoHeader = newVal2; + grCfg.config.updateConfigObjValues('settings', true); + } + + /** + * Prompts the user to enter a custom sort pattern for the playlist. + * @throws Will throw an error if the new value is not a string. + */ + playlistSortCustom() { + const oldValStr = JSON.stringify(grCfg.settings.playlistSortCustom).replace(/"/g, ''); + let newVal; + let input; + try { + input = utils.InputBox(window.ID, 'Enter your custom playlist order pattern:', 'Georgia-ReBORN', oldValStr, true); + newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); + if (typeof newVal !== 'string') throw new Error('Invalid type'); + } + catch (e) { + if (e.message === 'Invalid type' || e.name === 'SyntaxError') { + fb.ShowPopupMessage(`Pattern is not valid:\n${input}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExamples of correct patterns:\n\n%album artist% %date% %album% %discnumber% %tracknumber% %title%\n\n$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%`, 'Custom playlist order'); + } + return; + } + grCfg.settings.playlistSortCustom = newVal; + grCfg.config.updateConfigObjValues('settings', true); + } + + /** + * Prompts the user to enter a custom day-night mode start and end times. + * @throws Will throw an error if the format or times are invalid. + */ + themeDayNightModeCustom() { + const oldValues = Array.isArray(grCfg.themeSettings.themeDayNightMode) ? grCfg.themeSettings.themeDayNightMode.join('-') : grCfg.themeSettings.themeDayNightMode || '6-18'; + let input; + try { + input = utils.InputBox(window.ID, 'Enter your custom day-night mode (e.g 6-18):', 'Georgia-ReBORN', oldValues, true); + + const validFormat = /^\s*(\d+)\s*-\s*(\d+)\s*$/; // Regex to match a valid input format + const match = input.match(validFormat); + if (!match) throw new Error('Invalid format'); + + const startTime = Number(match[1]); + const endTime = Number(match[2]); + if (startTime === endTime || startTime < 0 || startTime > 23 || endTime < 0 || endTime > 23) { + throw new Error('Invalid time'); + } + + grCfg.themeSettings.themeDayNightMode = grSet.themeDayNightMode = input; + } + catch (e) { + if (e.message === 'Invalid format' || e.message === 'Invalid time') { + fb.ShowPopupMessage(`Input is not valid: ${input}\n\nPlease enter valid times in 24-hour format separated by a hyphen (e.g 6-18), where both times are between 0 and 23.`, 'Custom Day/Night Mode'); + } + } + grCfg.config.updateConfigObjValues('themeSettings', true); + initThemeDayNightMode(new Date()); + grm.ui.resetTheme(); + grm.ui.initThemeFull = true; + if (grSet.theme.startsWith('custom')) grm.ui.initCustomTheme(); + if (!fb.IsPlaying) grm.color.setThemeColors(); + grm.ui.initTheme(); + grm.ui.initStyleState(); + grm.preset.initThemePresetState(); + } + // #endregion } @@ -336,35 +625,54 @@ class Menu { // * TOOLTIP * // ///////////////// /** - * Creates or stops the tooltip timer. + * A class that creates or stops the tooltip timer. */ class TooltipTimer { + /** + * Creates the `TooltipTimer` instance. + */ constructor() { - this.tooltip_timer = undefined; - this.tt_caller = undefined; + /** @private @type {number|undefined} The timer ID for the tooltip display timeout. */ + this.tooltipTimer = undefined; + /** @private @type {number|undefined} The identifier of the current tooltip caller. */ + this.tooltipCaller = undefined; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Displays the tooltip. + * @param {string} text - The text to show in the tooltip. + * @param {boolean} [force] - Activates the tooltip whether or not text has changed. + */ + displayTooltip(text, force) { + if (grm.ui.ttip && (grm.ui.ttip.Text !== text.toString() || force)) { + grm.ui.ttip.Text = text; + grm.ui.ttip.Activate(); + } } /** * Starts a tooltip. - * @param {number} id The id of the caller. - * @param {string} text The text to show in the tooltip. + * @param {number} id - The id of the caller. + * @param {string} text - The text to show in the tooltip. */ start(id, text) { - const old_caller = this.tt_caller; - this.tt_caller = id; + const oldCaller = this.tooltipCaller; + this.tooltipCaller = id; - if (!this.tooltip_timer && g_tooltip.Text) { - this.tt(text, old_caller !== this.tt_caller); + if (!this.tooltipTimer && grm.ui.ttip.Text) { + this.displayTooltip(text, oldCaller !== this.tooltipCaller); } else { // * There can be only one tooltip present at all times, so we can kill the timer w/o any worries - if (this.tooltip_timer) { + if (this.tooltipTimer) { this.forceStop(); } - if (!this.tooltip_timer) { - this.tooltip_timer = setTimeout(() => { - this.tt(text); - this.tooltip_timer = null; + if (!this.tooltipTimer) { + this.tooltipTimer = setTimeout(() => { + this.displayTooltip(text); + this.tooltipTimer = null; }, 300); } } @@ -372,10 +680,10 @@ class TooltipTimer { /** * Stops a tooltip. - * @param {number} id The id of the caller. + * @param {number} id - The id of the caller. */ stop(id) { - if (this.tt_caller === id) { // Do not stop other callers + if (this.tooltipCaller === id) { // Do not stop other callers this.forceStop(); } } @@ -384,58 +692,55 @@ class TooltipTimer { * Forces the tooltip to stop. */ forceStop() { - this.tt(''); - if (!this.tooltip_timer) return; - clearTimeout(this.tooltip_timer); - this.tooltip_timer = null; - this.tt_caller = null; - } - - /** - * Actually displays the tooltip. - * @param {string} text The text to show in the tooltip. - * @param {boolean=} force Activates the tooltip whether or not text has changed. - */ - tt(text, force) { - if (g_tooltip && (g_tooltip.Text !== text.toString() || force)) { - g_tooltip.Text = text; - g_tooltip.Activate(); - } + this.displayTooltip(''); + if (!this.tooltipTimer) return; + clearTimeout(this.tooltipTimer); + this.tooltipTimer = null; + this.tooltipCaller = null; } + // #endregion } /** - * Creates or clears the tooltip text for normal and styled tooltips. + * A class that creates or clears the tooltip text for normal and styled tooltips. */ class TooltipHandler { + /** + * Creates the `TooltipHandler` instance. + * Constructs a unique ID and a reference to the TooltipTimer instance. + */ constructor() { + /** @private @type {number} The unique identifier for this TooltipHandler instance. */ this.id = Math.ceil(Math.random() * 10000); - this.timer = g_tooltip_timer; + /** @private @type {TooltipTimer} A reference to the TooltipTimer instance used to manage tooltip timing. */ + this.timer = new TooltipTimer(); } + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Shows tooltip after delay (300ms). - * @param {string} text The text to show in the tooltip. + * @param {string} text - The text to show in the tooltip. */ showDelayed(text) { - styledTooltipText = text; - styledTooltipReady = true; - if (!pref.showStyledTooltips) { + grm.ui.styledTooltipText = text; + grm.ui.styledTooltipReady = true; + if (!grSet.showStyledTooltips) { this.timer.start(this.id, text); } } /** * Shows the tooltip immediately. - * @param {string} text The text to show in the tooltip. + * @param {string} text - The text to show in the tooltip. */ showImmediate(text) { - styledTooltipText = text; - styledTooltipReady = true; - if (!pref.showStyledTooltips) { + grm.ui.styledTooltipText = text; + grm.ui.styledTooltipReady = true; + if (!grSet.showStyledTooltips) { this.timer.stop(this.id); - this.timer.tt(text); + this.timer.displayTooltip(text); } } @@ -443,7 +748,7 @@ class TooltipHandler { * Clears this tooltip if this handler created it. */ clear() { - styledTooltipReady = false; + grm.ui.styledTooltipReady = false; this.timer.stop(this.id); } @@ -451,9 +756,10 @@ class TooltipHandler { * Clears the tooltip regardless of which handler created it. */ stop() { - styledTooltipReady = false; + grm.ui.styledTooltipReady = false; this.timer.forceStop(); } + // #endregion } @@ -461,92 +767,77 @@ class TooltipHandler { // * DETAILS METADATA GRID TOOLTIP * // /////////////////////////////////////// /** - * Creates tooltips on the metadata grid in Details when artist, title or album is truncated. + * A class that creates tooltips on the metadata grid in Details when artist, title or album is truncated. */ class MetadataGridTooltip { /** - * @param {number} height The height. + * Creates the `MetadataGridTooltip` instance. + * @param {number} height - The height of the tooltip area. */ constructor(height) { + /** @private @type {number} The x-coordinate of the tooltip's position. */ this.x = 0; + /** @private @type {number} The y-coordinate of the tooltip's position. */ this.y = 0; - this.w = albumArtSize.x; + /** @private @type {number} The width of the tooltip. This typically matches the width of the album art. */ + this.w = grm.ui.albumArtSize.x; + /** @private @type {number} The height of the tooltip as specified by the height parameter. */ this.h = height; + /** @private @type {string} The text content of the tooltip. */ this.tooltipText = ''; } - // * METHODS * // - - /** - * Sets the width and position of the metadata grid tooltip area. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} width The width. - */ - setSize(x, y, width) { - if (this.x === x && this.y === y && this.w === width) return; - this.x = x; - this.y = y; - this.w = width; - } - - /** - * Sets the height of the metadata grid tooltip area. - * @param {number} height The height. - */ - setHeight(height) { - this.h = height; - } - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the metadata grid tooltip. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { - const showGridArtist = pref[`showGridArtist_${pref.layout}`]; - const showGridTitle = pref[`showGridTitle_${pref.layout}`]; - const showGridTimeline = pref[`showGridTimeline_${pref.layout}`]; - const showGridArtistFlags = pref[`showGridArtistFlags_${pref.layout}`]; - const gridAlbumFontSize = pref[`gridAlbumFontSize_${pref.layout}`]; - const textLeft = SCALE(pref.layout !== 'default' ? 20 : 40); + const showGridArtist = grSet[`showGridArtist_${grSet.layout}`]; + const showGridTitle = grSet[`showGridTitle_${grSet.layout}`]; + const showGridTimeline = grSet[`showGridTimeline_${grSet.layout}`]; + const showGridArtistFlags = grSet[`showGridArtistFlags_${grSet.layout}`]; + const gridAlbumFontSize = grSet[`gridAlbumFontSize_${grSet.layout}`]; + const textLeft = SCALE(grSet.layout !== 'default' ? 20 : 40); const textRight = SCALE(20); const lineSpacing = SCALE(8); const trackNumSpacing = SCALE(8); const timelineHeight = showGridTimeline ? SCALE(55) : SCALE(20); - const top = albumArtSize.y ? albumArtSize.y + textLeft : geo.topMenuHeight + textLeft; + const top = grm.ui.albumArtSize.y ? grm.ui.albumArtSize.y + textLeft : grm.ui.topMenuHeight + textLeft; const flagSize = - flagImgs.length >= 6 ? SCALE(84 + gridAlbumFontSize * 6) : - flagImgs.length === 5 ? SCALE(70 + gridAlbumFontSize * 5) : - flagImgs.length === 4 ? SCALE(56 + gridAlbumFontSize * 4) : - flagImgs.length === 3 ? SCALE(42 + gridAlbumFontSize * 3) : - flagImgs.length === 2 ? SCALE(28 + gridAlbumFontSize * 2) : - flagImgs.length === 1 ? SCALE(14 + gridAlbumFontSize) : ''; - const availableFlags = showGridArtistFlags && flagImgs.length ? flagSize : 0; + grm.ui.flagImgs.length >= 6 ? SCALE(84 + gridAlbumFontSize * 6) : + grm.ui.flagImgs.length === 5 ? SCALE(70 + gridAlbumFontSize * 5) : + grm.ui.flagImgs.length === 4 ? SCALE(56 + gridAlbumFontSize * 4) : + grm.ui.flagImgs.length === 3 ? SCALE(42 + gridAlbumFontSize * 3) : + grm.ui.flagImgs.length === 2 ? SCALE(28 + gridAlbumFontSize * 2) : + grm.ui.flagImgs.length === 1 ? SCALE(14 + gridAlbumFontSize) : ''; + const availableFlags = showGridArtistFlags && grm.ui.flagImgs.length ? flagSize : 0; - this.gridSpace = Math.floor((!albumArt && discArt ? discArtSize.x : albumArtSize.x) - geo.discArtShadow - textLeft - textRight); + this.gridSpace = Math.floor((!grm.ui.albumArt && grm.ui.discArt ? grm.ui.discArtSize.x : grm.ui.albumArtSize.x) - grm.ui.discArtShadow - textLeft - textRight); if (showGridArtist) { - this.artistWidthTrailingSpace = Math.ceil(gr.MeasureString(str.artist, ft.grd_artist, 0, 0, 0, 0, g_string_format.measure_trailing_spaces).Width); - this.artistWidth = Math.ceil(gr.MeasureString(str.artist, ft.grd_artist, 0, 0, 0, 0).Width + availableFlags - this.artistWidthTrailingSpace); - this.artistHeight = gr.MeasureString(str.artist, ft.grd_artist, 0, 0, 0, 0).Height; - this.artistTxtRec = gr.MeasureString(str.artist, ft.grd_artist, 0, 0, this.gridSpace, 0); + this.artistWidthTrailingSpace = Math.ceil(gr.MeasureString(grStr.artist, grFont.gridArtist, 0, 0, 0, 0, Stringformat.measure_trailing_spaces).Width); + this.artistWidth = Math.ceil(gr.MeasureString(grStr.artist, grFont.gridArtist, 0, 0, 0, 0).Width + availableFlags - this.artistWidthTrailingSpace); + this.artistHeight = gr.MeasureString(grStr.artist, grFont.gridArtist, 0, 0, 0, 0).Height; + this.artistTxtRec = gr.MeasureString(grStr.artist, grFont.gridArtist, 0, 0, this.gridSpace, 0); this.artistNumLines = Math.min(3, this.artistTxtRec.Lines); this.artistNumLinesHeight = this.artistNumLines === 3 ? this.artistHeight * 2 : (this.artistNumLines === 2 || this.artistWidth > this.gridSpace) ? this.artistHeight * 2 : this.artistHeight; } if (showGridTitle) { - this.titleWidthTrailingSpace = Math.ceil(gr.MeasureString(str.title, ft.grd_title, 0, 0, 0, 0, g_string_format.measure_trailing_spaces).Width); - this.trackNumWidth = Math.ceil(gr.MeasureString(str.tracknum, ft.grd_tracknum, 0, 0, 0, 0).Width); - this.titleWidth = Math.ceil(gr.MeasureString(str.title, ft.grd_title, 0, 0, 0, 0).Width + this.trackNumWidth + trackNumSpacing - this.titleWidthTrailingSpace); - this.titleHeight = gr.MeasureString(str.title, ft.grd_title, 0, 0, 0, 0).Height; - this.titleTxtRec = gr.MeasureString(isStreaming ? str.tracknum + str.title : str.tracknum === '' ? str.title : `${str.tracknum}\xa0${str.title}`, ft.grd_title, 0, 0, this.gridSpace, wh); + this.titleWidthTrailingSpace = Math.ceil(gr.MeasureString(grStr.title, grFont.gridTitle, 0, 0, 0, 0, Stringformat.measure_trailing_spaces).Width); + this.trackNumWidth = Math.ceil(gr.MeasureString(grStr.tracknum, grFont.gridTrackNumber, 0, 0, 0, 0).Width); + this.titleWidth = Math.ceil(gr.MeasureString(grStr.title, grFont.gridTitle, 0, 0, 0, 0).Width + this.trackNumWidth + trackNumSpacing - this.titleWidthTrailingSpace); + this.titleHeight = gr.MeasureString(grStr.title, grFont.gridTitle, 0, 0, 0, 0).Height; + this.titleTxtRec = gr.MeasureString(grm.ui.isStreaming ? grStr.tracknum + grStr.title : grStr.tracknum === '' ? grStr.title : `${grStr.tracknum}\xa0${grStr.title}`, grFont.gridTitle, 0, 0, this.gridSpace, grm.ui.wh); this.titleNumLines = Math.min(3, this.titleTxtRec.Lines); this.titleNumLinesHeight = this.titleNumLines === 3 ? this.titleHeight * 2 : (this.titleNumLines === 2 || this.titleWidth > this.gridSpace) ? this.titleHeight * 2 : this.titleHeight; } - this.albumWidthTrailingSpace = Math.ceil(gr.MeasureString(str.album, ft.grd_album, 0, 0, 0, 0, g_string_format.measure_trailing_spaces).Width); - this.albumWidth = Math.ceil(gr.MeasureString(str.album, ft.grd_album, 0, 0, 0, 0).Width - this.albumWidthTrailingSpace); - this.albumHeight = gr.MeasureString(str.album, ft.grd_album, 0, 0, 0, 0).Height; - this.albumTxtRec = gr.MeasureString(str.album, ft.grd_album, 0, 0, this.gridSpace, 0); + this.albumWidthTrailingSpace = Math.ceil(gr.MeasureString(grStr.album, grFont.gridAlbum, 0, 0, 0, 0, Stringformat.measure_trailing_spaces).Width); + this.albumWidth = Math.ceil(gr.MeasureString(grStr.album, grFont.gridAlbum, 0, 0, 0, 0).Width - this.albumWidthTrailingSpace); + this.albumHeight = gr.MeasureString(grStr.album, grFont.gridAlbum, 0, 0, 0, 0).Height; + this.albumTxtRec = gr.MeasureString(grStr.album, grFont.gridAlbum, 0, 0, this.gridSpace, 0); this.albumNumLines = Math.min(!showGridArtist && !showGridTitle ? 4 : 3, this.albumTxtRec.Lines); this.albumNumLinesHeight = !showGridArtist && !showGridTitle || this.albumNumLines === 3 ? this.albumHeight * 3 : this.albumNumLines === 2 ? this.albumHeight * 2 : this.albumHeight; @@ -615,21 +906,43 @@ class MetadataGridTooltip { } } + /** + * Sets the width and position of the metadata grid tooltip area. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} width - The width. + */ + setSize(x, y, width) { + if (this.x === x && this.y === y && this.w === width) return; + this.x = x; + this.y = y; + this.w = width; + } + + /** + * Sets the height of the metadata grid tooltip area. + * @param {number} height - The height. + */ + setHeight(height) { + this.h = height; + } + /** * Clears the metadata grid tooltip. */ clearTooltip() { this.tooltipText = ''; - tt.stop(); + grm.ttip.stop(); } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Checks if the mouse is over the metadata grid tooltip area. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @return {boolean} True or false. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. */ mouseInThis(x, y) { this.metadataGridTooltipArtist = x >= this.x && x < this.x + this.w && y >= this.topArtist && y < this.topArtist + this.artistNumLinesHeight; @@ -645,40 +958,41 @@ class MetadataGridTooltip { /** * Handles the tooltip when the mouse is in the metadata grid tooltip area. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ on_mouse_move(x, y, m) { - if (!pref.showTooltipMain && !pref.showTooltipTruncated) return; + if (!grSet.showTooltipMain && !grSet.showTooltipTruncated) return; let tooltip = ''; - const showGridArtist = pref[`showGridArtist_${pref.layout}`]; - const showGridTitle = pref[`showGridTitle_${pref.layout}`]; - const showLowerBarComposer = pref[`showLowerBarComposer_${pref.layout}`]; + const showGridArtist = grSet[`showGridArtist_${grSet.layout}`]; + const showGridTitle = grSet[`showGridTitle_${grSet.layout}`]; + const showLowerBarComposer = grSet[`showLowerBarComposer_${grSet.layout}`]; // * Artist if (showGridArtist && this.metadataGridTooltipArtist && (this.artistNumLines === 2 && this.artistWidth > this.gridSpace || this.artistNumLines > 2)) { - tooltip = str.artist; + tooltip = grStr.artist; } // * Title if (showGridTitle && this.metadataGridTooltipTitle && (this.titleNumLines === 2 && this.titleWidth > this.gridSpace || this.titleNumLines > 2)) { - tooltip = `${str.tracknum} ${str.title}${showLowerBarComposer ? str.composer : ''}`; + tooltip = `${grStr.tracknum} ${grStr.title}${showLowerBarComposer ? grStr.composer : ''}`; } // * Album if (!showGridArtist && !showGridTitle && this.metadataGridTooltipAlbum && this.albumNumLines > 3 || (showGridArtist || showGridTitle) && this.metadataGridTooltipAlbum && this.albumNumLines > 2) { - tooltip = str.album + (showLowerBarComposer ? str.composer : ''); + tooltip = grStr.album + (showLowerBarComposer ? grStr.composer : ''); } if (tooltip.length) { this.tooltipText = tooltip; - tt.showDelayed(this.tooltipText); + grm.ttip.showDelayed(this.tooltipText); } else if (!this.metadataGridTooltipAll) { this.clearTooltip(); } } + // #endregion } @@ -686,41 +1000,45 @@ class MetadataGridTooltip { // * LOWER BAR TOOLTIP * // /////////////////////////// /** - * Creates tooltips on the lower bar when artist or title is truncated. + * A class that creates tooltips on the lower bar when artist or title is truncated. */ class LowerBarTooltip { + /** + * Creates the `LowerBarTooltip` instance. + */ constructor() { + /** @private @type {string} The text content of the tooltip. */ this.tooltipText = ''; } - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the tooltip at the left lower bar area. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { - const lowerBarFontSize = pref[`lowerBarFontSize_${pref.layout}`]; - const showLowerBarComposer = pref[`showLowerBarComposer_${pref.layout}`]; - const showLowerBarArtistFlags = pref[`showLowerBarArtistFlags_${pref.layout}`]; - const showPlaybackOrderBtn = pref[`showPlaybackOrderBtn_${pref.layout}`]; - const showReloadBtn = pref[`showReloadBtn_${pref.layout}`]; - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; - const transportBtnSize = pref[`transportButtonSize_${pref.layout}`]; - const transportBtnSpacing = pref[`transportButtonSpacing_${pref.layout}`]; + const lowerBarFontSize = grSet[`lowerBarFontSize_${grSet.layout}`]; + const showLowerBarComposer = grSet[`showLowerBarComposer_${grSet.layout}`]; + const showLowerBarArtistFlags = grSet[`showLowerBarArtistFlags_${grSet.layout}`]; + const showPlaybackOrderBtn = grSet[`showPlaybackOrderBtn_${grSet.layout}`]; + const showReloadBtn = grSet[`showReloadBtn_${grSet.layout}`]; + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; + const transportBtnSize = grSet[`transportButtonSize_${grSet.layout}`]; + const transportBtnSpacing = grSet[`transportButtonSpacing_${grSet.layout}`]; const flagSize = - flagImgs.length >= 6 ? SCALE(84 + lowerBarFontSize * 6) : - flagImgs.length === 5 ? SCALE(70 + lowerBarFontSize * 5) : - flagImgs.length === 4 ? SCALE(56 + lowerBarFontSize * 4) : - flagImgs.length === 3 ? SCALE(42 + lowerBarFontSize * 3) : - flagImgs.length === 2 ? SCALE(28 + lowerBarFontSize * 2) : - flagImgs.length === 1 ? SCALE(14 + lowerBarFontSize) : ''; - const availableFlags = showLowerBarArtistFlags && flagImgs.length ? flagSize : 0; - const playbackTime = pref[`showPlaybackTime_${pref.layout}`]; - - this.timeAreaWidth = str.disc !== '' && pref.layout === 'default' ? gr.CalcTextWidth(`${str.disc} ${str.time} ${str.length}`, ft.lower_bar_title) : gr.CalcTextWidth(` ${str.time} ${str.length}`, ft.lower_bar_title); - this.lowerMargin = SCALE((pref.layout === 'compact' || pref.layout === 'artwork' ? 60 : pref.showTransportControls_default ? 60 : 100) + (!playbackTime ? -this.timeAreaWidth : 0)); + grm.ui.flagImgs.length >= 6 ? SCALE(84 + lowerBarFontSize * 6) : + grm.ui.flagImgs.length === 5 ? SCALE(70 + lowerBarFontSize * 5) : + grm.ui.flagImgs.length === 4 ? SCALE(56 + lowerBarFontSize * 4) : + grm.ui.flagImgs.length === 3 ? SCALE(42 + lowerBarFontSize * 3) : + grm.ui.flagImgs.length === 2 ? SCALE(28 + lowerBarFontSize * 2) : + grm.ui.flagImgs.length === 1 ? SCALE(14 + lowerBarFontSize) : ''; + const availableFlags = showLowerBarArtistFlags && grm.ui.flagImgs.length ? flagSize : 0; + const playbackTime = grSet[`showPlaybackTime_${grSet.layout}`]; + + this.timeAreaWidth = grStr.disc !== '' && grSet.layout === 'default' ? gr.CalcTextWidth(`${grStr.disc} ${grStr.time} ${grStr.length}`, grFont.lowerBarTitle) : gr.CalcTextWidth(` ${grStr.time} ${grStr.length}`, grFont.lowerBarTitle); + this.lowerMargin = SCALE((grSet.layout === 'compact' || grSet.layout === 'artwork' ? 60 : grSet.showTransportControls_default ? 60 : 100) + (!playbackTime ? -this.timeAreaWidth : 0)); // * Calculate all transport buttons width const buttonSize = SCALE(transportBtnSize); @@ -728,14 +1046,14 @@ class LowerBarTooltip { const buttonSpacing = SCALE(transportBtnSpacing); // * Setup width for artist and song title - this.availableWidth = pref.layout === 'default' && pref.showTransportControls_default && (pref.showLowerBarArtist_default || pref.showLowerBarTitle_default) ? Math.round(ww * 0.5 - this.lowerMargin - ((buttonSize * buttonCount + buttonSpacing * buttonCount) / 2)) : Math.round(ww - this.lowerMargin - (playbackTime ? this.timeAreaWidth : 0)); - this.artistWidth = gr.MeasureString(str.artist, ft.lower_bar_artist, 0, 0, 0, 0).Width + availableFlags; - this.trackNumWidth = Math.ceil(gr.MeasureString(str.tracknum, ft.lower_bar_title, 0, 0, 0, 0).Width); - this.titleWidth = this.trackNumWidth + gr.MeasureString(showLowerBarComposer ? str.title_lower + str.composer + str.original_artist : str.title_lower + str.original_artist, ft.lower_bar_title, 0, 0, 0, 0).Width + gr.MeasureString(str.original_artist, ft.lower_bar_title, 0, 0, 0, 0).Width; - this.artistMaxWidth_no_default = ww - (this.titleWidth + this.trackNumWidth + this.timeAreaWidth + this.lowerMargin); - this.titleMaxWidth_no_default = ww - (this.artistWidth + this.timeAreaWidth + this.lowerMargin); - this.artistOnlyMaxWidth_no_default = ww - (this.timeAreaWidth + this.lowerMargin); - this.titleOnlyMaxWidth_no_default = ww - (this.trackNumWidth + this.timeAreaWidth + this.lowerMargin); + this.availableWidth = grSet.layout === 'default' && grSet.showTransportControls_default && (grSet.showLowerBarArtist_default || grSet.showLowerBarTitle_default) ? Math.round(grm.ui.ww * 0.5 - this.lowerMargin - ((buttonSize * buttonCount + buttonSpacing * buttonCount) / 2)) : Math.round(grm.ui.ww - this.lowerMargin - (playbackTime ? this.timeAreaWidth : 0)); + this.artistWidth = gr.MeasureString(grStr.artist, grFont.lowerBarArtist, 0, 0, 0, 0).Width + availableFlags; + this.trackNumWidth = Math.ceil(gr.MeasureString(grStr.tracknum, grFont.lowerBarTitle, 0, 0, 0, 0).Width); + this.titleWidth = this.trackNumWidth + gr.MeasureString(showLowerBarComposer ? grStr.titleLower + grStr.composer + grStr.original_artist : grStr.titleLower + grStr.original_artist, grFont.lowerBarTitle, 0, 0, 0, 0).Width + gr.MeasureString(grStr.original_artist, grFont.lowerBarTitle, 0, 0, 0, 0).Width; + this.artistMaxWidth_no_default = grm.ui.ww - (this.titleWidth + this.trackNumWidth + this.timeAreaWidth + this.lowerMargin); + this.titleMaxWidth_no_default = grm.ui.ww - (this.artistWidth + this.timeAreaWidth + this.lowerMargin); + this.artistOnlyMaxWidth_no_default = grm.ui.ww - (this.timeAreaWidth + this.lowerMargin); + this.titleOnlyMaxWidth_no_default = grm.ui.ww - (this.trackNumWidth + this.timeAreaWidth + this.lowerMargin); } /** @@ -743,22 +1061,23 @@ class LowerBarTooltip { */ clearTooltip() { this.tooltipText = ''; - tt.stop(); + grm.ttip.stop(); } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Checks if the mouse is over the lower bar tooltip area. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @return {boolean} True or false. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. */ mouseInThis(x, y) { - const zoneX = SCALE(pref.layout !== 'default' ? 20 : 40); - const zoneY = wh - geo.lowerBarHeight + SCALE(15); - const zoneW = pref.layout === 'compact' || pref.layout === 'artwork' ? ww - this.lowerMargin - this.timeAreaWidth : this.availableWidth; - const zoneH = geo.lowerBarHeight * 0.33; + const zoneX = SCALE(grSet.layout !== 'default' ? 20 : 40); + const zoneY = grm.ui.wh - grm.ui.lowerBarHeight + SCALE(15); + const zoneW = grSet.layout === 'compact' || grSet.layout === 'artwork' ? grm.ui.ww - this.lowerMargin - this.timeAreaWidth : this.availableWidth; + const zoneH = grm.ui.lowerBarHeight * 0.33; const zone = zoneX <= x && zoneY <= y && zoneX + zoneW >= x && zoneY + zoneH >= y; if (!zone && this.tooltipText.length) { @@ -769,96 +1088,111 @@ class LowerBarTooltip { /** * Handles the tooltip if the mouse is in the lower bar tooltip area. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ on_mouse_move(x, y) { let tooltip = ''; - const showLowerBarArtist = pref[`showLowerBarArtist_${pref.layout}`]; - const showLowerBarTitle = pref[`showLowerBarTitle_${pref.layout}`]; - const showLowerBarComposer = pref[`showLowerBarComposer_${pref.layout}`]; + const showLowerBarArtist = grSet[`showLowerBarArtist_${grSet.layout}`]; + const showLowerBarTitle = grSet[`showLowerBarTitle_${grSet.layout}`]; + const showLowerBarComposer = grSet[`showLowerBarComposer_${grSet.layout}`]; - if (pref.layout === 'default' && (this.artistWidth > this.availableWidth || this.titleWidth > this.availableWidth)) { - tooltip = (`${str.artist}\n${str.tracknum === '' ? '' : `${str.tracknum} `}${str.title}${showLowerBarComposer ? str.composer : ''}`); + if (grSet.layout === 'default' && (this.artistWidth > this.availableWidth || this.titleWidth > this.availableWidth)) { + tooltip = (`${grStr.artist}\n${grStr.tracknum === '' ? '' : `${grStr.tracknum} `}${grStr.title}${showLowerBarComposer ? grStr.composer : ''}`); } else if ((showLowerBarArtist && (this.titleWidth > this.titleMaxWidth_no_default) || !showLowerBarArtist && (this.titleWidth > this.titleOnlyMaxWidth_no_default)) || (showLowerBarTitle && (this.artistWidth > this.artistMaxWidth_no_default) || !showLowerBarTitle && (this.artistWidth > this.artistOnlyMaxWidth_no_default))) { - tooltip = (`${str.artist}\n${str.tracknum} ${str.title}${showLowerBarComposer ? str.composer : ''}`); + tooltip = (`${grStr.artist}\n${grStr.tracknum} ${grStr.title}${showLowerBarComposer ? grStr.composer : ''}`); } if (tooltip.length && this.mouseInThis(x, y)) { this.tooltipText = tooltip; - tt.showDelayed(this.tooltipText); + grm.ttip.showDelayed(this.tooltipText); } else { this.clearTooltip(); } } + // #endregion } ////////////////////////////// // * INTERFACE HYPERLINKS * // ////////////////////////////// -/** @enum {number} */ -const HyperlinkStates = { - Normal: 0, - Hovered: 1 -}; - /** - * Creates clickable hyperlinks in the Playlist header and in the lower bar. + * A class that creates clickable hyperlinks in the Playlist header and in the lower bar. */ class Hyperlink { /** + * Creates the `Hyperlink` instance. * Initializes properties for the text element in the playlist. - * @param {string} text The text that will be displayed in the hyperlink. - * @param {GdiFont} font The font to use. - * @param {string} type The field name which will be searched when clicking on the hyperlink - * @param {number} xOffset The x-offset of the hyperlink. Negative values will be subtracted from the containerWidth to right justify. - * @param {number} yOffset The y-offset of the hyperlink. - * @param {number} containerWidth The width of the container the hyperlink will be in. Used for right justification purposes. - * @param {boolean} [inPlaylist=false] If the hyperlink is drawing in a scrolling container like a playlist, then it is drawn differently. + * @param {string} text - The text that will be displayed in the hyperlink. + * @param {GdiFont} font - The font to use. + * @param {string} type - The field name which will be searched when clicking on the hyperlink. + * @param {number} xOffset - The x-offset of the hyperlink. Negative values will be subtracted from the containerWidth to right justify. + * @param {number} yOffset - The y-offset of the hyperlink. + * @param {number} containerWidth - The width of the container the hyperlink will be in. Used for right justification purposes. + * @param {boolean} [inPlaylist] - If the hyperlink is drawing in a scrolling container like a playlist, then it is drawn differently. */ constructor(text, font, type, xOffset, yOffset, containerWidth, inPlaylist = false) { + /** @private @type {string} */ this.text = text; + /** @private @type {string} */ this.type = type; + /** @private @type {number} */ this.x_offset = xOffset; + /** @private @type {number} */ this.x = xOffset < 0 ? containerWidth + xOffset : xOffset; + /** @private @type {number} */ this.y_offset = yOffset; + /** @private @type {number} */ this.y = yOffset; + /** @private @type {number} */ this.container_w = containerWidth; + /** @private @type {boolean} */ this.state = HyperlinkStates.Normal; + /** @private @type {boolean} */ this.inPlaylist = inPlaylist; this.setFont(font); } - // * METHODS * // + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the hyperlink. When drawing in a playlist, we draw from the y-offset instead of y, because the playlist scrolls. + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} color - The color of the hyperlink. + */ + draw(gr, color) { + const font = this.state === HyperlinkStates.Hovered ? this.hoverFont : this.font; + DrawString(gr, this.text, font, color, this.x, this.inPlaylist ? this.y_offset : this.y, this.w + SCALE(1), this.h, Stringformat.trim_ellipsis_char); + } /** * Sets the xOffset of the hyperlink after it has been created. - * @param {number} xOffset The x-offset of the hyperlink. Negative values will be subtracted from the containerWidth to right justify. + * @param {number} xOffset - The x-offset of the hyperlink. Negative values will be subtracted from the containerWidth to right justify. */ - set_xOffset(xOffset) { + setXOffset(xOffset) { this.x = xOffset < 0 ? this.container_w + xOffset : xOffset; } /** * Sets the vertical position of the hyperlink. * The playlist requires subtracting 2 additional pixels from y for some reason. - * @param {number} y The y-coordinate. + * @param {number} y - The y-coordinate. */ - set_y(y) { + setY(y) { this.y = y + this.y_offset + (-2); } /** * Sets the font for the hyperlink. - * @param {GdiFont} font The font that will be used. + * @param {GdiFont} font - The font that will be used. */ setFont(font) { this.font = font; - this.hoverFont = gdi.Font(font.Name, font.Size, font.Style | g_font_style.underline); + this.hoverFont = gdi.Font(font.Name, font.Size, font.Style | FontStyle.underline); this.link_dimensions = this.updateDimensions(); } @@ -866,7 +1200,7 @@ class Hyperlink { * Sets the width of the container the hyperlink will be placed in. * If hyperlink width is smaller than the container, it will be truncated. * If the the xOffset is negative, the position will be adjusted as the container width changes. - * @param {number} w The width. + * @param {number} w - The width. */ setContainerWidth(w) { if (this.x_offset < 0) { @@ -884,7 +1218,9 @@ class Hyperlink { getWidth() { try { return Math.ceil(this.link_dimensions.Width); - } catch (e) {} + } catch (e) { + return null; + } } /** @@ -901,18 +1237,7 @@ class Hyperlink { measureStringScratchImg.ReleaseGraphics(gr); return dimensions; } catch (e) { - // Probably some invalid parameters on init - } - } - - /** - * Updates the hyperlink state. - */ - repaint() { - try { - window.RepaintRect(this.x, this.y, this.w, this.h); - } catch (e) { - // Probably already redrawing + return null; // Probably some invalid parameters on init } } @@ -925,37 +1250,37 @@ class Hyperlink { try { const handle_list = fb.GetQueryItems(fb.GetLibraryItems(), query); if (handle_list.Count) { - playlistHistory.ignorePlaylistMutations = true; - const pl = plman.FindOrCreatePlaylist('Search', true); - plman.UndoBackup(pl); + pl.history.ignorePlaylistMutations = true; + const plist = plman.FindOrCreatePlaylist('Search', true); + plman.UndoBackup(plist); handle_list.Sort(); const index = fb.IsPlaying ? handle_list.BSearch(fb.GetNowPlaying()) : -1; - if (pl === plman.PlayingPlaylist && plman.GetPlayingItemLocation().PlaylistIndex === pl && index !== -1) { + if (plist === plman.PlayingPlaylist && plman.GetPlayingItemLocation().PlaylistIndex === pl && index !== -1) { // Remove everything in playlist except currently playing song - plman.ClearPlaylistSelection(pl); - plman.SetPlaylistSelection(pl, [plman.GetPlayingItemLocation().PlaylistItemIndex], true); - plman.RemovePlaylistSelection(pl, true); - plman.ClearPlaylistSelection(pl); + plman.ClearPlaylistSelection(plist); + plman.SetPlaylistSelection(plist, [plman.GetPlayingItemLocation().PlaylistItemIndex], true); + plman.RemovePlaylistSelection(plist, true); + plman.ClearPlaylistSelection(plist); handle_list.RemoveById(index); } else { // Nothing playing or Search playlist is not active - plman.ClearPlaylist(pl); + plman.ClearPlaylist(plist); } - plman.InsertPlaylistItems(pl, 0, handle_list); - plman.SortByFormat(pl, settings.playlistSortDefault); - plman.ActivePlaylist = pl; - playlistHistory.ignorePlaylistMutations = false; + plman.InsertPlaylistItems(plist, 0, handle_list); + plman.SortByFormat(plist, grCfg.settings.playlistSortDefault); + plman.ActivePlaylist = plist; + pl.history.ignorePlaylistMutations = false; return true; } return false; } catch (e) { - playlistHistory.ignorePlaylistMutations = false; + pl.history.ignorePlaylistMutations = false; console.log(`Could not successfully execute: ${query}`); } }; @@ -964,7 +1289,7 @@ class Hyperlink { let query; switch (this.type) { case 'update': RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/releases'); break; - case 'date': query = pref.showPlaylistFullDate ? `"${tf.date}" IS ${this.text}` : `"$year(%date%)" IS ${this.text}`; break; + case 'date': query = grSet.showPlaylistFullDate ? `"${grTF.date}" IS ${this.text}` : `"$year(%date%)" IS ${this.text}`; break; case 'artist': query = `Artist HAS "${this.text.replace(/"/g, '')}" OR Album Artist HAS "${this.text.replace(/"/g, '')}" OR ARTISTFILTER HAS "${this.text.replace(/"/g, '')}"`; break; case 'album': query = `Album HAS "${this.text.replace(/"/g, '')}"`; break; case 'label': query = `Label HAS "${this.text.replace(/"/g, '')}" OR Publisher HAS "${this.text.replace(/"/g, '')}"`; break; @@ -981,22 +1306,24 @@ class Hyperlink { } /** - * Draws the hyperlink. When drawing in a playlist, we draw from the y-offset instead of y, because the playlist scrolls. - * @param {GdiGraphics} gr - * @param {*} color The color of the hyperlink. + * Updates the hyperlink state. */ - draw(gr, color) { - const font = this.state === HyperlinkStates.Hovered ? this.hoverFont : this.font; - DrawString(gr, this.text, font, color, this.x, this.inPlaylist ? this.y_offset : this.y, this.w + SCALE(1), this.h, g_string_format.trim_ellipsis_char); + repaint() { + try { + window.RepaintRect(this.x, this.y, this.w, this.h); + } catch (e) { + // Probably already redrawing + } } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Sets mouse hover state for every hyperlink not created in Playlist. - * @param {Object} hyperlink The hyperlink object. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {object} hyperlink - The hyperlink object. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ on_mouse_move(hyperlink, x, y) { @@ -1016,13 +1343,14 @@ class Hyperlink { /** * Checks if the mouse is within the boundaries of a hyperlink. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ trace(x, y) { return (this.x <= x) && (x <= this.x + this.w) && (this.y <= y) && (y <= this.y + this.h); } + // #endregion } @@ -1030,42 +1358,88 @@ class Hyperlink { // * DETAILS TIMELINE * // ////////////////////////// /** - * Creates the timeline above the metadata grid in Details. + * A class that creates the timeline above the metadata grid in Details. */ class Timeline { /** - * @param {number} height The height of the timeline. + * Creates the `Timeline` instance. + * @param {number} height - The height of the timeline. */ constructor(height) { - this.marginLeft = SCALE(pref.layout !== 'default' ? 20 : 40); + /** @private @type {number} */ + this.marginLeft = SCALE(grSet.layout !== 'default' ? 20 : 40); + /** @private @type {number} */ this.x = this.marginLeft; + /** @private @type {number} */ this.y = 0; - this.w = albumArtSize.x - 1; + /** @private @type {number} */ + this.w = grm.ui.albumArtSize.x - 1; + /** @private @type {number} */ this.h = height; + /** @private @type {number} */ this.playCol = RGBA(255, 255, 255, 150); - - /** @private */ this.firstPlayedPercent = 0.33; - /** @private */ this.lastPlayedPercent = 0.66; - /** @private */ this.playedTimesPercents = []; - /** @private */ this.playedTimes = []; + /** @private @type {number} */ + this.firstPlayedPercent = 0.33; + /** @private @type {number} */ + this.lastPlayedPercent = 0.66; + /** @private @type {number[]} */ + this.playedTimesPercents = []; + /** @private @type {number[]} */ + this.playedTimes = []; // Recalculated in setSize - /** @private */ this.lineWidth = RES_4K ? 3 : 2; - /** @private */ this.extraLeftSpace = SCALE(3); // Add a little space to the left so songs that were played a long time ago show more in the "added" stage - /** @private */ this.drawWidth = Math.floor(this.w - this.extraLeftSpace - 1 - this.lineWidth / 2); // Area that the timeline percents can be drawn in - /** @private */ this.leeway = (1 / this.drawWidth) * (this.lineWidth + SCALE(2)) / 2; // Percent of timeline that we use to determine if mouse is over a playline. Equals half line with + 1 or 2 pixels on either side + /** @private @type {number} */ + this.lineWidth = RES._4K ? 3 : 2; + /** @private @type {number} */ + this.extraLeftSpace = SCALE(3); + /** @private @type {number} */ + this.drawWidth = Math.floor(this.w - this.extraLeftSpace - 1 - this.lineWidth / 2); + /** @private @type {number} */ + this.leeway = (1 / this.drawWidth) * (this.lineWidth + SCALE(2)) / 2; + /** @private @type {string} */ this.tooltipText = ''; } - // * METHODS * // + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the timeline above the metadata grid in Details. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + draw(gr) { + if (!this.addedCol && !this.playedCol && !this.unplayedCol) return; + + gr.SetSmoothingMode(SmoothingMode.None); // Disable smoothing + gr.FillSolidRect(this.marginLeft, this.y, this.drawWidth + this.extraLeftSpace + this.lineWidth, this.h, this.addedCol); + if (grSet.theme.startsWith('custom')) { + gr.DrawRect(this.x - 2, this.y - 2, this.w + 3, this.h + 3, 1, grCol.timelineFrame); + } + + if (this.firstPlayedPercent >= 0 && this.lastPlayedPercent >= 0) { + const x1 = Math.floor(this.drawWidth * this.firstPlayedPercent) + this.extraLeftSpace; + const x2 = Math.floor(this.drawWidth * this.lastPlayedPercent) + this.extraLeftSpace; + gr.FillSolidRect(x1 + this.marginLeft, this.y, this.drawWidth - x1 + this.extraLeftSpace, this.h, this.playedCol); + gr.FillSolidRect(x2 + this.marginLeft, this.y, this.drawWidth - x2 + this.extraLeftSpace + this.lineWidth, this.h, this.unplayedCol); + } + for (let i = 0; i < this.playedTimesPercents.length; i++) { + const x = Math.floor(this.drawWidth * this.playedTimesPercents[i]) + this.marginLeft + this.extraLeftSpace; + if (!Number.isNaN(x) && x <= this.w + this.marginLeft * 2) { + const linePos = Math.max(this.marginLeft, Math.min(x, x)); + gr.DrawLine(linePos, this.y, linePos, this.y + this.h, this.lineWidth, this.playCol); + } else { + // console.log('Played Times Error! ratio: ' + this.playedTimesPercents[i], 'x: ' + x); + } + } + gr.SetSmoothingMode(SmoothingMode.AntiAlias); + } /** * Sets the width and position of the timeline. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} width The width of the timeline. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} width - The width of the timeline. */ setSize(x, y, width) { if (this.x === x && this.y === y && this.w === width) { @@ -1077,7 +1451,7 @@ class Timeline { this.w = width; // Recalculate these values - this.lineWidth = RES_4K ? 3 : 2; + this.lineWidth = RES._4K ? 3 : 2; this.extraLeftSpace = SCALE(3); // Add a little space to the left so songs that were played a long time ago show more in the "added" stage this.drawWidth = Math.floor(this.w - this.extraLeftSpace - 1 - this.lineWidth / 2); this.leeway = (1 / this.drawWidth) * (this.lineWidth + SCALE(2)) / 2; @@ -1085,7 +1459,7 @@ class Timeline { /** * Sets the height of the timeline. - * @param {number} height The height of the timeline. + * @param {number} height - The height of the timeline. */ setHeight(height) { this.h = height; @@ -1093,9 +1467,9 @@ class Timeline { /** * Sets the colors of the three timeline bars. - * @param {number} addedCol The color for the added bar. - * @param {number} playedCol The color for the played bar. - * @param {number} unplayedCol The color for the unplayed bar. + * @param {number} addedCol - The color for the added bar. + * @param {number} playedCol - The color for the played bar. + * @param {number} unplayedCol - The color for the unplayed bar. */ setColors(addedCol, playedCol, unplayedCol) { this.addedCol = addedCol; @@ -1105,10 +1479,10 @@ class Timeline { /** * Sets the first and last played percentages, as well as the played time ratios and values. - * @param {number} firstPlayed The percentage of the total play time that represents the first time the item was played. - * @param {number} lastPlayed The percentage of the total play time that represents the last time the item was played. - * @param {number} playedTimeRatios The percentage of time played for each playedTimesValues. - * @param {number} playedTimesValues Contains the actual played times for each interval. + * @param {number} firstPlayed - The percentage of the total play time that represents the first time the item was played. + * @param {number} lastPlayed - The percentage of the total play time that represents the last time the item was played. + * @param {number} playedTimeRatios - The percentage of time played for each playedTimesValues. + * @param {number} playedTimesValues - Contains the actual played times for each interval. * For example, if the intervals are divided into 5 parts, playedTimesValues would be an * array of 5 numbers representing the played times for each interval. */ @@ -1124,46 +1498,16 @@ class Timeline { */ clearTooltip() { this.tooltipText = ''; - tt.stop(); - } - - /** - * Draws the timeline above the metadata grid in Details. - * @param {GdiGraphics} gr - */ - draw(gr) { - if (!this.addedCol && !this.playedCol && !this.unplayedCol) return; - - gr.SetSmoothingMode(SmoothingMode.None); // Disable smoothing - gr.FillSolidRect(this.marginLeft, this.y, this.drawWidth + this.extraLeftSpace + this.lineWidth, this.h, this.addedCol); - if (pref.theme.startsWith('custom')) { - gr.DrawRect(this.x - 2, this.y - 2, this.w + 3, this.h + 3, 1, col.timelineFrame); - } - - if (this.firstPlayedPercent >= 0 && this.lastPlayedPercent >= 0) { - const x1 = Math.floor(this.drawWidth * this.firstPlayedPercent) + this.extraLeftSpace; - const x2 = Math.floor(this.drawWidth * this.lastPlayedPercent) + this.extraLeftSpace; - gr.FillSolidRect(x1 + this.marginLeft, this.y, this.drawWidth - x1 + this.extraLeftSpace, this.h, this.playedCol); - gr.FillSolidRect(x2 + this.marginLeft, this.y, this.drawWidth - x2 + this.extraLeftSpace + this.lineWidth, this.h, this.unplayedCol); - } - for (let i = 0; i < this.playedTimesPercents.length; i++) { - const x = Math.floor(this.drawWidth * this.playedTimesPercents[i]) + this.marginLeft + this.extraLeftSpace; - if (!Number.isNaN(x) && x <= this.w + this.marginLeft * 2) { - const linePos = Math.max(this.marginLeft, Math.min(x, x)); - gr.DrawLine(linePos, this.y, linePos, this.y + this.h, this.lineWidth, this.playCol); - } else { - // console.log('Played Times Error! ratio: ' + this.playedTimesPercents[i], 'x: ' + x); - } - } - gr.SetSmoothingMode(SmoothingMode.AntiAlias); + grm.ttip.stop(); } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Checks if the mouse is within the boundaries of the timeline. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { @@ -1174,7 +1518,7 @@ class Timeline { // * Workaround when using styled tooltips and fb.IsPaused while mouse hovering the timeline. // * Only in pause state, this needs to be somehow repainted to trigger displaying the tooltip. - } else if (inTimeline && pref.showStyledTooltips && fb.IsPaused) { + } else if (inTimeline && grSet.showStyledTooltips && fb.IsPaused) { window.RepaintRect(this.x, this.y, this.w, this.h); } @@ -1183,14 +1527,15 @@ class Timeline { /** * Displays the timeline tooltip with the date and time when the mouse is moved over an element. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ on_mouse_move(x, y, m) { - if (!pref.showTooltipTimeline || this.playedTimesPercents.length === 0) return; + if (!grSet.showTooltipTimeline || this.playedTimesPercents.length === 0) return; let tooltip = ''; const percent = ToFixed((x + this.x - this.marginLeft * 2 - this.extraLeftSpace) / this.drawWidth, 3); + const timezoneOffset = UpdateTimezoneOffset(); for (let i = 0; i < this.playedTimesPercents.length; i++) { if (Math.abs(percent - this.playedTimesPercents[i]) <= this.leeway) { @@ -1200,7 +1545,7 @@ class Timeline { } else if (percent < this.playedTimesPercents[i]) { if (!tooltip.length) { - const added = i === 0 ? DateDiff($Date('[%added%]'), this.playedTimes[0]) : DateDiff(new Date(this.playedTimes[i - 1]).toISOString(), this.playedTimes[i]); + const added = i === 0 ? DateDiff($Date('[%added%]'), this.playedTimes[0], timezoneOffset) : DateDiff(new Date(this.playedTimes[i - 1]).toISOString(), this.playedTimes[i], timezoneOffset); tooltip = added ? (i === 0 ? `First played after ${added}` : `No plays for ${added}`) : ''; } break; @@ -1209,11 +1554,12 @@ class Timeline { if (tooltip.length) { this.tooltipText = tooltip; - tt.showImmediate(tooltip); + grm.ttip.showImmediate(tooltip); } else { this.clearTooltip(); } } + // #endregion } @@ -1221,71 +1567,82 @@ class Timeline { // * JUMP SEARCH * // ///////////////////// /** - * Creates the jump search when using keystrokes, searches in the active Playlist first and if nothing found it tries in the Library. + * A class that creates the jump search when using keystrokes. + * Searches in the active Playlist first and when nothing found, it tries in the Library. */ class JumpSearch { - constructor() { + /** + * Creates the `JumpSearch` instance. + */ + constructor() { + /** @private @type {number} */ this.arc1 = 5; + /** @private @type {number} */ this.arc2 = 4; + /** @private @type {object} */ this.j = { - x: Math.round(pref.playlistLayout === 'full' || pref.layout !== 'default' ? ww * 0.5 : ww * 0.5 + ww * 0.25), - y: Math.round((wh + geo.topMenuHeight - geo.lowerBarHeight - playlist_geo.row_h * 1.5) / 2), + x: Math.round(grSet.playlistLayout === 'full' || grSet.layout !== 'default' ? grm.ui.ww * 0.5 : grm.ui.ww * 0.5 + grm.ui.ww * 0.25), + y: Math.round((grm.ui.wh + grm.ui.topMenuHeight - grm.ui.lowerBarHeight - pl.geo.row_h * 1.5) / 2), w: 50, h: 30 }; + /** @private @type {string} */ this.jSearch = ''; + /** @private @type {boolean} */ this.jump_search = true; + /** @type {{ [key: string]: number[] }} */ this.initials = null; } - // * METHODS * // - - /** - * Sets the vertical position of the jump search. - * @param {number} y The y-coordinate. - */ - setY(y) { - this.y = y; - } - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the jump search on the playlist panel. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { if (!this.jSearch) return; gr.SetSmoothingMode(4); - this.j.w = gr.CalcTextWidth(this.jSearch, ft.notification) + 25; - gr.FillRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, RGBtoRGBA(col.popupBg, 220)); + this.j.w = gr.CalcTextWidth(this.jSearch, grFont.notification) + 25; + gr.FillRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, RGBtoRGBA(grCol.popupBg, 220)); gr.DrawRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, 1, 0x64000000); gr.DrawRoundRect(this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w - 2, this.j.h - 2, this.arc2, this.arc2, 1, 0x28ffffff); - // gr.GdiDrawText(this.jSearch, ft.notification, RGB(0, 0, 0), this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w, this.j.h, panel.cc); // Drop shadow not needed - gr.GdiDrawText(this.jSearch, ft.notification, this.jump_search ? col.popupText : 0xffff4646, this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, panel.cc); + // gr.GdiDrawText(this.jSearch, grFont.notification, RGB(0, 0, 0), this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w, this.j.h, panel.cc); // Drop shadow not needed + gr.GdiDrawText(this.jSearch, grFont.notification, this.jump_search ? grCol.popupText : 0xffff4646, this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, lib.panel.cc); gr.SetSmoothingMode(0); } - // * CALLBACKS * // + /** + * Sets the vertical position of the jump search. + * @param {number} y - The y-coordinate. + */ + setY(y) { + this.y = y; + } + // #endregion + // * CALLBACKS * // + // #region CALLBACKS /** * Handles key pressed events and activates the jump search. - * @param {number} code The character code. + * @param {number} code - The character code. */ on_char(code) { - if (panel.search.active || utils.IsKeyPressed(VK_CONTROL)) return; + if (lib.panel.search.active || utils.IsKeyPressed(VK_CONTROL)) return; const text = String.fromCharCode(code); const playlistItems = plman.GetPlaylistItems(plman.ActivePlaylist); - const search = fb.TitleFormat(pref.jumpSearchComposerOnly ? '%composer%' : '$if2(%album artist%, %artist%)').EvalWithMetadbs(playlistItems); + const search = fb.TitleFormat(grSet.jumpSearchComposerOnly ? '%composer%' : '$if2(%album artist%, %artist%)').EvalWithMetadbs(playlistItems); let focusIndex = plman.GetPlaylistFocusItemIndex(plman.ActivePlaylist); let advance = false; let foundInPlaylist = false; let foundInLibrary = false; switch (code) { - case vk.back: + case lib.vk.back: this.jSearch = this.jSearch.substr(0, this.jSearch.length - 1); break; - case vk.enter: + case lib.vk.enter: this.jSearch = ''; return; default: @@ -1294,7 +1651,7 @@ class JumpSearch { } // * Playlist advance - if (focusIndex >= 0 && focusIndex < search.length && (displayPlaylist || displayLibrarySplit(true))) { + if (focusIndex >= 0 && focusIndex < search.length && (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true))) { const char = search[focusIndex].replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase(); if (char === text && AllEqual(this.jSearch)) { this.jSearch = this.jSearch.slice(0, 1); @@ -1302,9 +1659,9 @@ class JumpSearch { } } // * Library advance - else if (panel.pos >= 0 && panel.pos < pop.tree.length && !displayLibrarySplit(true)) { - const char = pop.tree[panel.pos].name.replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase(); - if (pop.tree[panel.pos].sel && char === text && AllEqual(this.jSearch)) { + else if (lib.panel.pos >= 0 && lib.panel.pos < lib.pop.tree.length && !grm.ui.displayLibrarySplit(true)) { + const char = lib.pop.tree[lib.panel.pos].name.replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase(); + if (lib.pop.tree[lib.panel.pos].sel && char === text && AllEqual(this.jSearch)) { this.jSearch = this.jSearch.slice(0, 1); advance = true; } @@ -1318,8 +1675,8 @@ class JumpSearch { if (!this.initials) { // reset in buildTree this.initials = {}; // * Playlist advance - if (displayPlaylist || displayLibrarySplit(true)) { - for (const [i, v] of playlistItems.Convert().entries()) { + if (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true)) { + for (const [i] of playlistItems.Convert().entries()) { const name = search[i].replace(/@!#.*?@!#/g, ''); init = name.charAt().toLowerCase(); if (cur !== init && !this.initials[init]) { @@ -1332,7 +1689,7 @@ class JumpSearch { } // * Library advance else { - for (const [i, v] of pop.tree.entries()) { + for (const [i, v] of lib.pop.tree.entries()) { if (!v.root) { const nm = v.name.replace(/@!#.*?@!#/g, ''); init = nm.charAt().toLowerCase(); @@ -1350,7 +1707,7 @@ class JumpSearch { this.jump_search = false; // * Playlist advance - if (focusIndex >= 0 && focusIndex < search.length && (displayPlaylist || displayLibrarySplit(true))) { + if (focusIndex >= 0 && focusIndex < search.length && (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true))) { this.matches = this.initials[text]; console.log('Playlist advance results', this.matches); // Debug this.ix = this.matches.indexOf(focusIndex); @@ -1360,66 +1717,66 @@ class JumpSearch { this.jump_search = true; } // * Library advance - else if (panel.pos >= 0 && panel.pos < pop.tree.length && !displayLibrarySplit(true)) { + else if (lib.panel.pos >= 0 && lib.panel.pos < lib.pop.tree.length && !grm.ui.displayLibrarySplit(true)) { this.matches = this.initials[text]; console.log('Library advance results', this.matches); // Debug, can remove this soon - this.ix = this.matches.indexOf(panel.pos); + this.ix = this.matches.indexOf(lib.panel.pos); this.ix++; if (this.ix >= this.matches.length) this.ix = 0; - panel.pos = this.matches[this.ix]; + lib.panel.pos = this.matches[this.ix]; this.jump_search = true; } // * Playlist advance - if (this.jump_search && (displayPlaylist || displayLibrarySplit(true))) { + if (this.jump_search && (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true))) { plman.ClearPlaylistSelection(plman.ActivePlaylist); plman.SetPlaylistFocusItem(plman.ActivePlaylist, focusIndex); plman.SetPlaylistSelectionSingle(plman.ActivePlaylist, focusIndex, true); window.Repaint(); } // * Library advance - else if (this.jump_search && !displayLibrarySplit(true)) { - pop.clearSelected(); - pop.sel_items = []; - pop.tree[panel.pos].sel = true; - pop.setPos(panel.pos); - pop.getTreeSel(); - lib.treeState(false, ppt.rememberTree); + else if (this.jump_search && !grm.ui.displayLibrarySplit(true)) { + lib.pop.clearSelected(); + lib.pop.sel_items = []; + lib.pop.tree[lib.panel.pos].sel = true; + lib.pop.setPos(lib.panel.pos); + lib.pop.getTreeSel(); + lib.lib.treeState(false, libSet.rememberTree); window.Repaint(); - if (panel.imgView) pop.showItem(panel.pos, 'focus'); + if (lib.panel.imgView) lib.pop.showItem(lib.panel.pos, 'focus'); else { - const row = (panel.pos * ui.row.h - sbar.scroll) / ui.row.h; - if (sbar.rows_drawn - row < 3 || row < 0) sbar.checkScroll((panel.pos + 3) * ui.row.h - sbar.rows_drawn * ui.row.h); + const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; + if (lib.sbar.rows_drawn - row < 3 || row < 0) lib.sbar.checkScroll((lib.panel.pos + 3) * lib.ui.row.h - lib.sbar.rows_drawn * lib.ui.row.h); } - if (ppt.libSource) { - if (pop.autoFill.key) pop.load(pop.sel_items, true, false, false, !ppt.sendToCur, false); - pop.track(pop.autoFill.key); - } else if (panel.pos >= 0 && panel.pos < pop.tree.length) pop.setPlaylistSelection(panel.pos, pop.tree[panel.pos]); + if (libSet.libSource) { + if (lib.pop.autoFill.key) lib.pop.load(lib.pop.sel_items, true, false, false, !libSet.sendToCur, false); + lib.pop.track(lib.pop.autoFill.key); + } else if (lib.panel.pos >= 0 && lib.panel.pos < lib.pop.tree.length) lib.pop.setPlaylistSelection(lib.panel.pos, lib.pop.tree[lib.panel.pos]); } else { window.Repaint(); } - timer.clear(timer.jsearch2); - timer.jsearch2.id = setTimeout(() => { + lib.timer.clear(lib.timer.jsearch2); + lib.timer.jsearch2.id = setTimeout(() => { this.jSearch = ''; window.Repaint(); - timer.jsearch2.id = null; + lib.timer.jsearch2.id = null; }, 2200); } break; case !advance: if (utils.IsKeyPressed(VK_TAB) || utils.IsKeyPressed(VK_CONTROL) || utils.IsKeyPressed(VK_ESCAPE) || utils.IsKeyPressed(VK_MULTIPLY) || utils.IsKeyPressed(VK_SUBTRACT)) return; - if (!panel.search.active) { + if (!lib.panel.search.active) { let pos = -1; - pop.clearSelected(); + lib.pop.clearSelected(); if (!this.jSearch) return; - pop.sel_items = []; + lib.pop.sel_items = []; this.jump_search = true; window.Repaint(); - timer.clear(timer.jsearch1); + lib.timer.clear(lib.timer.jsearch1); - timer.jsearch1.id = setTimeout(() => { + lib.timer.jsearch1.id = setTimeout(() => { // * First search in the Playlist playlistItems.Convert().some((v, i) => { const name = search[i].replace(/@!#.*?@!#/g, ''); @@ -1435,17 +1792,17 @@ class JumpSearch { return false; }); // * If no Playlist results found, try search query in the Library - if (!foundInPlaylist && pref.jumpSearchIncludeLibrary && pref.layout !== 'compact') { - pop.tree.some((v, i) => { + if (!foundInPlaylist && grSet.jumpSearchIncludeLibrary && grSet.layout !== 'compact') { + lib.pop.tree.some((v, i) => { const name = v.name.replace(/@!#.*?@!#/g, ''); - if (name !== panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() === this.jSearch.toLowerCase()) { + if (name !== lib.panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() === this.jSearch.toLowerCase()) { foundInPlaylist = false; foundInLibrary = true; pos = i; v.sel = true; - pop.setPos(pos); - if (pop.autoFill.key) pop.getTreeSel(); - lib.treeState(false, ppt.rememberTree); + lib.pop.setPos(pos); + if (lib.pop.autoFill.key) lib.pop.getTreeSel(); + lib.lib.treeState(false, libSet.rememberTree); console.log(`Jumpsearch: "${name}" found in Library`); // Debug, can remove this soon return true; } @@ -1461,31 +1818,31 @@ class JumpSearch { window.Repaint(); if (foundInPlaylist) { - displayPlaylist = true; - displayLibrary = pref.libraryLayout === 'split' && displayPlaylist; - displayBiography = false; - pref.displayLyrics = false; - initButtonState(); + grm.ui.displayPlaylist = true; + grm.ui.displayLibrary = grSet.libraryLayout === 'split' && grm.ui.displayPlaylist; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + grm.button.initButtonState(); } - else if (foundInLibrary && pref.jumpSearchIncludeLibrary) { - displayPlaylist = pref.libraryLayout === 'split' && displayPlaylist; - displayLibrary = true; - displayBiography = false; - pref.displayLyrics = false; - pop.showItem(pos, 'focus'); + else if (foundInLibrary && grSet.jumpSearchIncludeLibrary) { + grm.ui.displayPlaylist = grSet.libraryLayout === 'split' && grm.ui.displayPlaylist; + grm.ui.displayLibrary = true; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + lib.pop.showItem(pos, 'focus'); this.jSearch = ''; // Reset to avoid conflict with other query - initButtonState(); + grm.button.initButtonState(); } - timer.jsearch1.id = null; + lib.timer.jsearch1.id = null; }, 500); - timer.clear(timer.jsearch2); + lib.timer.clear(lib.timer.jsearch2); - timer.jsearch2.id = setTimeout(() => { + lib.timer.jsearch2.id = setTimeout(() => { this.jSearch = ''; window.Repaint(); - timer.jsearch2.id = null; + lib.timer.jsearch2.id = null; }, 1200); } } @@ -1495,12 +1852,13 @@ class JumpSearch { * Sets the size and position of the jump search and updates them on window resizing. */ on_size() { - this.j.x = Math.round(pref.playlistLayout === 'full' || pref.layout !== 'default' ? ww * 0.5 : ww * 0.5 + ww * 0.25); - this.j.h = Math.round(playlist_geo.row_h * 1.5); - this.j.y = Math.round((wh + geo.topMenuHeight - geo.lowerBarHeight - this.j.h) / 2); + this.j.x = Math.round(grSet.playlistLayout === 'full' || grSet.layout !== 'default' ? grm.ui.ww * 0.5 : grm.ui.ww * 0.5 + grm.ui.ww * 0.25); + this.j.h = Math.round(pl.geo.row_h * 1.5); + this.j.y = Math.round((grm.ui.wh + grm.ui.topMenuHeight - grm.ui.lowerBarHeight - this.j.h) / 2); this.arc1 = Math.min(5, this.j.h / 2); this.arc2 = Math.min(4, (this.j.h - 2) / 2); } + // #endregion } @@ -1508,68 +1866,77 @@ class JumpSearch { // * PAUSE BUTTON * // ////////////////////// /** - * Creates a pause button on the album art when playback is being paused. + * A class that creates a pause button on the album art when playback is being paused. */ class PauseButton { + /** + * Creates the `PauseButton` instance. + */ constructor() { + /** @private @type {number} */ this.xCenter = 0; + /** @private @type {number} */ this.yCenter = 0; + /** @private @type {number} */ this.top = 0; + /** @private @type {number} */ this.left = 0; } - // * METHODS * // + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the pause button on the album art cover. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + draw(gr) { + const pauseBorderWidth = SCALE(2); + const halfBorderWidth = Math.floor(pauseBorderWidth / 2); + + gr.SetSmoothingMode(SmoothingMode.AntiAlias); // Smooth edges + + gr.FillRoundRect(this.left, this.top, grm.ui.pauseSize, grm.ui.pauseSize, + 0.1 * grm.ui.pauseSize, 0.1 * grm.ui.pauseSize, RGBA(0, 0, 0, 150)); + gr.DrawRoundRect(this.left + halfBorderWidth, this.top + halfBorderWidth, grm.ui.pauseSize - pauseBorderWidth, grm.ui.pauseSize - pauseBorderWidth, + 0.1 * grm.ui.pauseSize, 0.1 * grm.ui.pauseSize, pauseBorderWidth, RGBA(128, 128, 128, 60)); + gr.FillRoundRect(this.left + 0.26 * grm.ui.pauseSize, this.top + 0.25 * grm.ui.pauseSize, + 0.12 * grm.ui.pauseSize, 0.5 * grm.ui.pauseSize, 2, 2, RGBA(255, 255, 255, 160)); + gr.FillRoundRect(this.left + 0.62 * grm.ui.pauseSize, this.top + 0.25 * grm.ui.pauseSize, + 0.12 * grm.ui.pauseSize, 0.5 * grm.ui.pauseSize, 2, 2, RGBA(255, 255, 255, 160)); + } /** * Sets the coordinates of the center point of the pause button. - * @param {number} xCenter The centered x-coordinate. - * @param {number} yCenter The centered y-coordinate. + * @param {number} xCenter - The centered x-coordinate. + * @param {number} yCenter - The centered y-coordinate. */ setCoords(xCenter, yCenter) { this.xCenter = xCenter; this.yCenter = yCenter; - this.top = Math.round(this.yCenter - geo.pauseSize / 2); - this.left = Math.round(this.xCenter - geo.pauseSize / 2); + this.top = Math.round(this.yCenter - grm.ui.pauseSize / 2); + this.left = Math.round(this.xCenter - grm.ui.pauseSize / 2); } /** * Updates the pause button state. */ repaint() { - window.RepaintRect(this.left - 1, this.top - 1, geo.pauseSize + 2, geo.pauseSize + 2); - } - - /** - * Draws the pause button on the album art cover. - * @param {GdiGraphics} gr - */ - draw(gr) { - const pauseBorderWidth = SCALE(2); - const halfBorderWidth = Math.floor(pauseBorderWidth / 2); - - gr.SetSmoothingMode(SmoothingMode.AntiAlias); // Smooth edges - - gr.FillRoundRect(this.left, this.top, geo.pauseSize, geo.pauseSize, - 0.1 * geo.pauseSize, 0.1 * geo.pauseSize, RGBA(0, 0, 0, 150)); - gr.DrawRoundRect(this.left + halfBorderWidth, this.top + halfBorderWidth, geo.pauseSize - pauseBorderWidth, geo.pauseSize - pauseBorderWidth, - 0.1 * geo.pauseSize, 0.1 * geo.pauseSize, pauseBorderWidth, RGBA(128, 128, 128, 60)); - gr.FillRoundRect(this.left + 0.26 * geo.pauseSize, this.top + 0.25 * geo.pauseSize, - 0.12 * geo.pauseSize, 0.5 * geo.pauseSize, 2, 2, RGBA(255, 255, 255, 160)); - gr.FillRoundRect(this.left + 0.62 * geo.pauseSize, this.top + 0.25 * geo.pauseSize, - 0.12 * geo.pauseSize, 0.5 * geo.pauseSize, 2, 2, RGBA(255, 255, 255, 160)); + window.RepaintRect(this.left - 1, this.top - 1, grm.ui.pauseSize + 2, grm.ui.pauseSize + 2); } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Checks if the mouse is within the boundaries of the pause button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { - return (x >= this.left && y >= this.top && x < this.left + geo.pauseSize + 1 && y <= this.top + geo.pauseSize + 1); + return (x >= this.left && y >= this.top && x < this.left + grm.ui.pauseSize + 1 && y <= this.top + grm.ui.pauseSize + 1); } + // #endregion } @@ -1577,39 +1944,54 @@ class PauseButton { // * VOLUME BUTTON * // /////////////////////// /** - * Creates basic volume controls. + * A class that creates and manages basic volume controls. */ class Volume { /** - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. + * Creates the `Volume` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. */ constructor(x, y, w, h) { + /** @public @type {number} */ this.x = x; + /** @public @type {number} */ this.y = y; + /** @public @type {number} */ this.w = w; + /** @public @type {number} */ this.h = h; + /** @private @type {number} */ this.mx = 0; + /** @private @type {number} */ this.my = 0; + /** @private @type {number} */ this.clickX = 0; + /** @private @type {number} */ this.clickY = 0; + /** @private @type {boolean} */ this.drag = false; + /** @private @type {number} */ this.dragVol = 0; - this.tt = new TooltipHandler(); + /** @private @type {TooltipHandler} */ + this.tooltipHandler = new TooltipHandler(); /** * Calculates the decibel (dB) value of the given volume. - * @param {number} volume A value between 0 and 1 where 1 is full volume. + * @param {number} volume - A value between 0 and 1 where 1 is full volume. * @returns {number} A decibel value between -100 and 0, to pass to fb.Volume. 0 is max volume, -100 is min. + * @private */ - this.toDb = (volume) => (50 * Math.log(0.99 * volume + 0.01)) / Math.LN10; + this.toDecibel = (volume) => (50 * Math.log(0.99 * volume + 0.01)) / Math.LN10; } + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Calculates the size of the fill portion of the volume bar based on current volume. - * @param {string} type Either 'h' or 'w' for vertical or horizontal volume bars. + * @param {string} type - Either 'h' or 'w' for vertical or horizontal volume bars. * @returns {number} The calculated size based on the type parameter. */ fillSize(type) { @@ -1618,11 +2000,11 @@ class Volume { /** * Checks if the given coordinates are within the boundaries of the volume button on left mouse down click. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ - lbtn_down(x, y) { + lbtnDown(x, y) { if (this.trace(x, y)) { this.clickX = x; this.clickY = y; @@ -1635,11 +2017,11 @@ class Volume { /** * Checks if the left mouse click is within the boundaries of the volume bar and adjusts the volume accordingly. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ - lbtn_up(x, y) { + lbtnUp(x, y) { this.clickX = 0; this.clickY = 0; if (this.drag) { @@ -1665,8 +2047,8 @@ class Volume { /** * Handles mouse move events on the volume bar, i.e when dragging the bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ move(x, y) { @@ -1680,29 +2062,28 @@ class Volume { if (this.trace(x, y) || this.drag) { if (this.drag) { x -= this.x; - const maxAreaExtraWidth = 0; // Give a little bigger target area to select -0.00dB - const pos = (x < maxAreaExtraWidth) ? - 0 : - (x > this.w) ? - 1 : (x - maxAreaExtraWidth) / (this.w - maxAreaExtraWidth); + const maxAreaExtraWidth = 0; // Give a little bigger target area to select -0.00dB + const pos = + (x < maxAreaExtraWidth) ? 0 : + (x > this.w) ? 1 : + (x - maxAreaExtraWidth) / (this.w - maxAreaExtraWidth); - this.dragVol = this.toDb(pos); + this.dragVol = this.toDecibel(pos); fb.Volume = this.dragVol; } - return true; } this.drag = false; - if (pref.showTooltipVolume) { - this.tt.stop(); + if (grSet.showTooltipVolume) { + this.tooltipHandler.stop(); } return false; } /** * Checks if the mouse is within the boundaries of the volume bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ trace(x, y) { @@ -1712,7 +2093,8 @@ class Volume { /** * Handles mouse wheel events and adjusts the volume. - * @param {number} scrollAmt The amount of scrolling that has occurred. + * @param {number} scrollAmt - The amount of scrolling that has occurred. + * @returns {boolean} True if the mouse is over the component and the volume was adjusted, false otherwise. */ wheel(scrollAmt) { if (!this.trace(this.mx, this.my)) { @@ -1727,72 +2109,65 @@ class Volume { return true; } + // #endregion } /** - * Creates the volume button in the lower bar next to its other playback transport buttons. + * A class that creates the volume button in the lower bar next to its playback transport buttons. */ -class VolumeBtn { +class VolumeButton { + /** + * Creates the `Volume` instance. + */ constructor() { // * Calculate all transport buttons width - const showPlaybackOrderBtn = pref[`showPlaybackOrderBtn_${pref.layout}`]; - const showReloadBtn = pref[`showReloadBtn_${pref.layout}`]; - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; - const transportBtnSize = pref[`transportButtonSize_${pref.layout}`]; - const transportBtnSpacing = pref[`transportButtonSpacing_${pref.layout}`]; + const showPlaybackOrderBtn = grSet[`showPlaybackOrderBtn_${grSet.layout}`]; + const showReloadBtn = grSet[`showReloadBtn_${grSet.layout}`]; + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; + const transportBtnSize = grSet[`transportButtonSize_${grSet.layout}`]; + const transportBtnSpacing = grSet[`transportButtonSpacing_${grSet.layout}`]; const buttonSize = SCALE(transportBtnSize); const buttonSpacing = SCALE(transportBtnSpacing); const buttonCount = 4 + (showPlaybackOrderBtn ? 1 : 0) + (showReloadBtn ? 1 : 0) + (showVolumeBtn ? 1 : 0); - const volumeBarWidth = Math.ceil((ww - (buttonSize * buttonCount + buttonSpacing * buttonCount)) / 2 - SCALE(40)); + const volumeBarWidth = Math.ceil((window.Width - (buttonSize * buttonCount + buttonSpacing * buttonCount)) / 2 - SCALE(40)); + /** @public @type {number} */ this.x = 0; + /** @public @type {number} */ this.y = 0; - this.w = SCALE(ww < 600 ? volumeBarWidth : 100); + /** @public @type {number} */ + this.w = SCALE(window.Width < 600 ? volumeBarWidth : 100); + /** @public @type {number} */ this.h = SCALE(12); + /** @private @type {number} */ this.inThisPadding = this.w * 0.5; // * Runtime state + /** @private @type {boolean} */ this.mouseInPanel = false; - this.displayVolumeBar = !pref.autoHideVolumeBar; + /** @private @type {boolean} */ + this.displayVolumeBar = !grSet.autoHideVolumeBar; // * Objects - /** @type {Volume} */ + /** @private @type {Volume} */ this.volumeBar = undefined; /** * Calculates the corresponding percentage value from decibel. - * @param {number} volume A value between 0 and 1 where 1 is full volume. + * @param {number} volume - A value between 0 and 1 where 1 is full volume. * @returns {number} A percentage value between 0% and 100%, to pass to fb.Volume. + * @private */ this.toPercent = (volume) => (10 ** (volume / 50) - 0.01) / 0.99; } - // * METHODS * // - - /** - * Sets the position of the volume bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} btnWidth The width. - */ - setPosition(x, y, btnWidth) { - const wh = window.Height; - const buttonSize_default = SCALE(pref.transportButtonSize_default); - const buttonSize_artwork = SCALE(pref.transportButtonSize_artwork); - const buttonSize_compact = SCALE(pref.transportButtonSize_compact); - const center_default = Math.floor(buttonSize_default / 2 + SCALE(4)); - const center_artwork = Math.floor(buttonSize_artwork / 2 + SCALE(4)); - const center_compact = Math.floor(buttonSize_compact / 2 + SCALE(4)); - this.x = x + (pref[`transportButtonSize_${pref.layout}`] * SCALE(1.25)); - this.y = y + (pref.layout === 'compact' ? center_compact : pref.layout === 'artwork' ? center_artwork : center_default) - this.h; - this.volumeBar = new Volume(this.x, this.y, this.w, Math.min(wh - this.y, this.h)); - } - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the volume bar. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { if (!this.displayVolumeBar) return; @@ -1803,57 +2178,63 @@ class VolumeBtn { const arc = SCALE(3); const arcIsValid = fillWidth > arc * 3; // * Needed when dragging volume to prevent invalid arc value crash - gr.SetSmoothingMode(pref.styleVolumeBarDesign === 'rounded' ? SmoothingMode.AntiAlias : SmoothingMode.None); + gr.SetSmoothingMode(grSet.styleVolumeBarDesign === 'rounded' ? SmoothingMode.AntiAlias : SmoothingMode.None); // * Default background - if (pref.styleVolumeBarDesign === 'rounded' && pref.styleTransportButtons !== 'minimal') { - gr.FillRoundRect(x - SCALE(2), y + (RES_4K ? p + 1 : p), w + SCALE(2), h, SCALE(5), SCALE(5), col.volumeBar); - gr.DrawRoundRect(x - (RES_4K ? 5 : this.showReloadBtn ? 3 : 2), y + SCALE(1), w + (RES_4K ? 5 : 3), h + 2, SCALE(6), SCALE(6), 1, col.volumeBarFrame); + if (grSet.styleVolumeBarDesign === 'rounded' && grSet.styleTransportButtons !== 'minimal') { + gr.FillRoundRect(x - SCALE(2), y + (RES._4K ? p + 1 : p), w + SCALE(2), h, SCALE(5), SCALE(5), grCol.volumeBar); + gr.DrawRoundRect(x - (RES._4K ? 5 : this.showReloadBtn ? 3 : 2), y + SCALE(1), w + (RES._4K ? 5 : 3), h + 2, SCALE(6), SCALE(6), 1, grCol.volumeBarFrame); } - else if (pref.styleVolumeBarDesign !== 'rounded' && pref.styleTransportButtons !== 'minimal') { - gr.FillSolidRect(x - SCALE(2), y + (RES_4K ? p + 1 : p), w + SCALE(2), h, col.volumeBar); - gr.DrawRect(x - (RES_4K ? 5 : this.showReloadBtn ? 3 : 2), y + SCALE(1), w + (RES_4K ? 5 : 3), h + 1, 1, col.volumeBarFrame); + else if (grSet.styleVolumeBarDesign !== 'rounded' && grSet.styleTransportButtons !== 'minimal') { + gr.FillSolidRect(x - SCALE(2), y + (RES._4K ? p + 1 : p), w + SCALE(2), h, grCol.volumeBar); + gr.DrawRect(x - (RES._4K ? 5 : this.showReloadBtn ? 3 : 2), y + SCALE(1), w + (RES._4K ? 5 : 3), h + 1, 1, grCol.volumeBarFrame); } // * Style background - if ((pref.styleVolumeBar === 'bevel' || pref.styleVolumeBar === 'inner') && pref.styleTransportButtons !== 'minimal') { - if (pref.styleVolumeBarDesign === 'rounded') { - FillGradRoundRect(gr, x - SCALE(2), y + (RES_4K ? p + 1 : p) - (pref.styleVolumeBar === 'inner' ? 1 : 0), w + SCALE(5), h + SCALE(4), SCALE(6), SCALE(6), - pref.styleVolumeBar === 'inner' ? -89 : 89, pref.styleVolumeBar === 'inner' ? col.styleVolumeBar : 0, pref.styleVolumeBar === 'inner' ? 0 : col.styleVolumeBar, pref.styleVolumeBar === 'inner' ? 0 : 1); + if ((grSet.styleVolumeBar === 'bevel' || grSet.styleVolumeBar === 'inner') && grSet.styleTransportButtons !== 'minimal') { + if (grSet.styleVolumeBarDesign === 'rounded') { + FillGradRoundRect(gr, x - SCALE(2), y + (RES._4K ? p + 1 : p) - (grSet.styleVolumeBar === 'inner' ? 1 : 0), w + SCALE(5), h + SCALE(4), SCALE(6), SCALE(6), + grSet.styleVolumeBar === 'inner' ? -89 : 89, grSet.styleVolumeBar === 'inner' ? grCol.styleVolumeBar : 0, grSet.styleVolumeBar === 'inner' ? 0 : grCol.styleVolumeBar, grSet.styleVolumeBar === 'inner' ? 0 : 1); } else { - gr.FillGradRect(x - SCALE(2), y + (RES_4K ? p + (pref.styleVolumeBar === 'inner' ? 0 : 2) : p), w + SCALE(2), h, pref.styleVolumeBar === 'inner' ? -90 : 90, 0, col.styleVolumeBar); + gr.FillGradRect(x - SCALE(2), y + (RES._4K ? p + (grSet.styleVolumeBar === 'inner' ? 0 : 2) : p), w + SCALE(2), h, grSet.styleVolumeBar === 'inner' ? -90 : 90, 0, grCol.styleVolumeBar); } } // * Default fill - if (pref.styleVolumeBarDesign === 'rounded' && arcIsValid) { - gr.FillRoundRect(x + 1, y + (RES_4K ? 7 : 4), fillWidth - SCALE(3), h - SCALE(4), arc, arc, col.volumeBarFill); + if (grSet.styleVolumeBarDesign === 'rounded' && arcIsValid) { + gr.FillRoundRect(x + 1, y + (RES._4K ? 7 : 4), fillWidth - SCALE(3), h - SCALE(4), arc, arc, grCol.volumeBarFill); } else { - gr.FillSolidRect(x, y + (RES_4K ? 7 : 4), fillWidth - SCALE(2), h - SCALE(4), col.volumeBarFill); + gr.FillSolidRect(x, y + (RES._4K ? 7 : 4), fillWidth - SCALE(2), h - SCALE(4), grCol.volumeBarFill); } // * Style fill - if ((pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner') && arcIsValid) { - if (pref.styleVolumeBarDesign === 'rounded') { - FillGradRoundRect(gr, x + 1, y + (RES_4K ? 7 : 4), fillWidth - SCALE(0.5), h - SCALE(2), arc, arc, pref.styleVolumeBarFill === 'inner' ? -89 : 89, 0, col.styleVolumeBarFill, 1); + if ((grSet.styleVolumeBarFill === 'bevel' || grSet.styleVolumeBarFill === 'inner') && arcIsValid) { + if (grSet.styleVolumeBarDesign === 'rounded') { + FillGradRoundRect(gr, x + 1, y + (RES._4K ? 7 : 4), fillWidth - SCALE(0.5), h - SCALE(2), arc, arc, grSet.styleVolumeBarFill === 'inner' ? -89 : 89, 0, grCol.styleVolumeBarFill, 1); } else { - gr.FillGradRect(x, y + (RES_4K ? 7 : 4), fillWidth - SCALE(2), h - SCALE(3), pref.styleVolumeBarFill === 'inner' ? -90 : 90, pref.styleBlackAndWhite ? col.styleVolumeBarFill : 0, pref.styleBlackAndWhite ? 0 : col.styleVolumeBarFill); + gr.FillGradRect(x, y + (RES._4K ? 7 : 4), fillWidth - SCALE(2), h - SCALE(3), grSet.styleVolumeBarFill === 'inner' ? -90 : 90, grSet.styleBlackAndWhite ? grCol.styleVolumeBarFill : 0, grSet.styleBlackAndWhite ? 0 : grCol.styleVolumeBarFill); } } } /** - * Updates the volume bar state. + * Sets the position of the volume bar. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} btnWidth - The width. */ - repaint() { - if (!this.volumeBar) return; - - const xyPadding = SCALE(3); - const whPadding = xyPadding * 2; - window.RepaintRect(this.x - xyPadding, this.volumeBar.y, this.volumeBar.w + whPadding, this.volumeBar.h + whPadding); + setPosition(x, y, btnWidth) { + const buttonSize_default = SCALE(grSet.transportButtonSize_default); + const buttonSize_artwork = SCALE(grSet.transportButtonSize_artwork); + const buttonSize_compact = SCALE(grSet.transportButtonSize_compact); + const center_default = Math.floor(buttonSize_default / 2 + SCALE(4)); + const center_artwork = Math.floor(buttonSize_artwork / 2 + SCALE(4)); + const center_compact = Math.floor(buttonSize_compact / 2 + SCALE(4)); + this.x = x + (grSet[`transportButtonSize_${grSet.layout}`] * SCALE(1.25)); + this.y = y + (grSet.layout === 'compact' ? center_compact : grSet.layout === 'artwork' ? center_artwork : center_default) - this.h; + this.volumeBar = new Volume(this.x, this.y, this.w, Math.min(grm.ui.wh - this.y, this.h)); } /** * Shows or hides the volume bar and stops a tooltip if it is shown. - * @param {boolean} show Whether to show or hide the volume bar. - * @returns {boolean} If the `volumeBar` property is not defined, the function will return undefined. + * @param {boolean} show - Whether to show or hide the volume bar. */ showVolumeBar(show) { if (!this.volumeBar) return; @@ -1861,7 +2242,7 @@ class VolumeBtn { this.displayVolumeBar = show; this.repaint(); if (show) { - this.volumeBar.tt.stop(); + this.volumeBar.tooltipHandler.stop(); } } @@ -1872,12 +2253,24 @@ class VolumeBtn { this.showVolumeBar(!this.displayVolumeBar); } - // * CALLBACKS * // + /** + * Updates the volume bar state. + */ + repaint() { + if (!this.volumeBar) return; + + const xyPadding = SCALE(3); + const whPadding = xyPadding * 2; + window.RepaintRect(this.x - xyPadding, this.volumeBar.y, this.volumeBar.w + whPadding, this.volumeBar.h + whPadding); + } + // #endregion + // * CALLBACKS * // + // #region CALLBACKS /** * Checks if the mouse is within the boundaries of the volume button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { @@ -1892,36 +2285,38 @@ class VolumeBtn { } /** - * Handles left mouse button down click events and calls the lbtn_down method. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * Handles left mouse button down click events and calls the lbtnDown method. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. * @returns {boolean} True or false. */ on_mouse_lbtn_down(x, y, m) { - if (!this.volumeBar) return; + if (!this.volumeBar) return false; if (this.displayVolumeBar) { - return this.volumeBar.lbtn_down(x, y); + return this.volumeBar.lbtnDown(x, y); } return false; } /** - * Handles left mouse button up click events and calls the lbtn_up method. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * Handles left mouse button up click events and calls the lbtnUp method. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. * @returns {boolean} True or false. */ on_mouse_lbtn_up(x, y, m) { - if (!this.volumeBar) return; + if (!this.volumeBar) return false; - if (!pref.lockPlayerSize) qwr_utils.EnableSizing(m); + if (!grSet.lockPlayerSize) grm.utils.enableSizing(m); if (this.displayVolumeBar) { - return this.volumeBar.lbtn_up(x, y); + return this.volumeBar.lbtnUp(x, y); } + + return false; } /** @@ -1932,25 +2327,25 @@ class VolumeBtn { this.mouseInPanel = false; - if (this.displayVolumeBar && pref.autoHideVolumeBar) { + if (this.displayVolumeBar && grSet.autoHideVolumeBar) { this.showVolumeBar(false); this.repaint(); } - if (pref.autoHideVolumeBar) { + if (grSet.autoHideVolumeBar) { this.volumeBar.leave(); } } /** * Handles mouse movement events and performs actions related to the volume bar display. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. */ on_mouse_move(x, y, m) { if (!this.volumeBar || !this.displayVolumeBar) return; - qwr_utils.DisableSizing(m); + grm.utils.disableSizing(m); if (this.volumeBar.drag) { this.volumeBar.move(x, y); @@ -1962,21 +2357,21 @@ class VolumeBtn { if (this.displayVolumeBar) { if (this.mouseInThis(x, y)) { this.volumeBar.move(x, y); - } else if (pref.autoHideVolumeBar) { + } else if (grSet.autoHideVolumeBar) { this.showVolumeBar(false); this.repaint(); } const inVolumeBar = x > this.x && x <= this.x + this.w && y > this.y && y <= this.y + this.h; - const volTooltip = pref.showTooltipVolumeInPercent ? `${Math.ceil(this.toPercent(fb.Volume) * 100)} %` : `${Math.ceil(fb.Volume.toFixed(2))} dB`; - if (pref.showTooltipVolume && inVolumeBar) { - this.volumeBar.tt.showImmediate(volTooltip); + const volTooltip = grSet.showTooltipVolumeInPercent ? `${Math.ceil(this.toPercent(fb.Volume) * 100)} %` : `${Math.ceil(fb.Volume.toFixed(2))} dB`; + if (grSet.showTooltipVolume && inVolumeBar) { + this.volumeBar.tooltipHandler.showImmediate(volTooltip); } } } /** * Handles mouse wheel events and controls the volume on the volume bar. - * @param {number} step The wheel scroll direction. + * @param {number} step - The wheel scroll direction. * @returns {boolean} True or false. */ on_mouse_wheel(step) { @@ -1996,15 +2391,16 @@ class VolumeBtn { /** * Updates the volume bar and displays a tooltip with the current volume in either percentage or decibels. - * @param {float} val + * @param {number} val - The new volume value that triggered the update. */ on_volume_change(val) { if (this.displayVolumeBar) { this.repaint(); } - const volTooltip = pref.showTooltipVolumeInPercent ? `${Math.ceil(this.toPercent(fb.Volume) * 100)} %` : `${Math.ceil(fb.Volume.toFixed(2))} dB`; - if (pref.showTooltipVolume) this.volumeBar.tt.showImmediate(volTooltip); + const volTooltip = grSet.showTooltipVolumeInPercent ? `${Math.ceil(this.toPercent(fb.Volume) * 100)} %` : `${Math.ceil(fb.Volume.toFixed(2))} dB`; + if (grSet.showTooltipVolume) this.volumeBar.tooltipHandler.showImmediate(volTooltip); } + // #endregion } @@ -2012,173 +2408,178 @@ class VolumeBtn { // * PROGRESS BAR * // ////////////////////// /** - * Creates the progress bar in the lower bar when enabled, quick access via right click on lower bar. + * A class that creates the progress bar in the lower bar when enabled. + * Quick access via right click context menu on lower bar. */ class ProgressBar { /** - * @param {number} ww window.Width - * @param {number} wh window.Height + * Creates the `ProgressBar` instance. + * @param {number} ww - Window.Width. + * @param {number} wh - Window.Height. */ constructor(ww, wh) { - this.x = SCALE(pref.layout !== 'default' ? 20 : 40); - this.w = ww - SCALE(pref.layout !== 'default' ? 40 : 80); + /** @public @type {number} */ + this.x = SCALE(grSet.layout !== 'default' ? 20 : 40); + /** @public @type {number} */ + this.w = ww - SCALE(grSet.layout !== 'default' ? 40 : 80); + /** @public @type {number} */ this.y = 0; - this.h = geo.progBarHeight; + /** @public @type {number} */ + this.h = grm.ui.progressBarH; + /** @public @type {number} */ this.progressLength = 0; // Fixing jumpiness in progressBar + /** @public @type {boolean} */ this.progressMoved = false; // Playback position changed, so reset progressLength - this.drag = false; // Progress bar is being dragged - this.progressAlphaCol = undefined; - this.lastAccentCol = undefined; - } - - // * METHODS * // - - /** - * Sets the vertical progress bar position. - * @param {number} y The y-coordinate. - */ - setY(y) { - this.y = y; - } - - /** - * Sets the playback time of the progress bar. - * @param {number} x The x-coordinate. - * @private - */ - setPlaybackTime(x) { - let v = (x - this.x) / this.w; - v = (v < 0) ? 0 : (v < 1) ? v : 1; - if (fb.PlaybackTime !== v * fb.PlaybackLength) { - fb.PlaybackTime = v * fb.PlaybackLength; - } + /** @private @type {boolean} */ + this.drag = false; // Progress bar is being dragged } + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the progress bar with various progress bar styles. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { - if (!pref.showProgressBar_default && !pref.showProgressBar_artwork && !pref.showProgressBar_compact) return; + if (!grSet.showProgressBar_default && !grSet.showProgressBar_artwork && !grSet.showProgressBar_compact) return; - gr.SetSmoothingMode(pref.styleProgressBarDesign === 'rounded' ? SmoothingMode.AntiAlias : SmoothingMode.None); + gr.SetSmoothingMode(grSet.styleProgressBarDesign === 'rounded' ? SmoothingMode.AntiAlias : SmoothingMode.None); const arc = Math.min(this.w, this.h) / 2; const arcIsValid = this.h > arc; // * Needed when bg changes to prevent invalid arc value crash // * Progress bar background - if (pref.styleProgressBarDesign === 'rounded' && arcIsValid) { - gr.FillRoundRect(this.x, this.y, this.w, this.h, arc, arc, isStreaming && fb.IsPlaying ? col.progressBarStreaming : col.progressBar); - } else if (!['dots', 'thin'].includes(pref.styleProgressBarDesign)) { - gr.FillSolidRect(this.x, this.y, this.w, this.h, isStreaming && fb.IsPlaying ? col.progressBarStreaming : col.progressBar); + if (grSet.styleProgressBarDesign === 'rounded' && arcIsValid) { + gr.FillRoundRect(this.x, this.y, this.w, this.h, arc, arc, grm.ui.isStreaming && fb.IsPlaying ? grCol.progressBarStreaming : grCol.progressBar); + } else if (!['dots', 'thin'].includes(grSet.styleProgressBarDesign)) { + gr.FillSolidRect(this.x, this.y, this.w, this.h, grm.ui.isStreaming && fb.IsPlaying ? grCol.progressBarStreaming : grCol.progressBar); } - if (pref.styleDefault && (['blue', 'darkblue', 'red', 'cream', 'custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'].includes(pref.theme)) || - (pref.theme === 'cream' && (pref.styleAlternative || pref.styleAlternative2) && (!pref.styleBevel && !pref.styleBlend && !pref.styleBlend2 && pref.styleProgressBarDesign !== 'rounded')) && !pref.systemFirstLaunch) { - gr.DrawRect(this.x - 2, this.y - 2, this.w + 3, this.h + 3, 1, col.progressBarFrame); + if (grSet.styleDefault && (['blue', 'darkblue', 'red', 'cream', 'custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'].includes(grSet.theme)) || + (grSet.theme === 'cream' && (grSet.styleAlternative || grSet.styleAlternative2) && (!grSet.styleBevel && !grSet.styleBlend && !grSet.styleBlend2 && grSet.styleProgressBarDesign !== 'rounded')) && !grSet.systemFirstLaunch) { + gr.DrawRect(this.x - 2, this.y - 2, this.w + 3, this.h + 3, 1, grCol.progressBarFrame); } - if (!['dots', 'thin'].includes(pref.styleProgressBarDesign) && (pref.styleProgressBar === 'bevel' || pref.styleProgressBar === 'inner') && arcIsValid) { - if (pref.styleProgressBarDesign === 'rounded') { + if (!['dots', 'thin'].includes(grSet.styleProgressBarDesign) && (grSet.styleProgressBar === 'bevel' || grSet.styleProgressBar === 'inner') && arcIsValid) { + if (grSet.styleProgressBarDesign === 'rounded') { FillGradRoundRect(gr, this.x, this.y, this.w + SCALE(2), this.h + SCALE(2.5), arc, arc, - pref.styleProgressBar === 'inner' ? pref.styleBlackReborn && fb.IsPlaying ? 90 : -90 : pref.styleBlackReborn && fb.IsPlaying ? -90 : 90, 0, col.styleProgressBar, 1); + grSet.styleProgressBar === 'inner' ? grSet.styleBlackReborn && fb.IsPlaying ? 90 : -90 : grSet.styleBlackReborn && fb.IsPlaying ? -90 : 90, 0, grCol.styleProgressBar, 1); } else { - gr.FillGradRect(this.x, this.y, this.w, this.h, pref.styleProgressBar === 'inner' ? pref.styleBlackReborn && fb.IsPlaying ? 90 : -90 : pref.styleBlackReborn && fb.IsPlaying ? -90 : 90, 0, col.styleProgressBar); + gr.FillGradRect(this.x, this.y, this.w, this.h, grSet.styleProgressBar === 'inner' ? grSet.styleBlackReborn && fb.IsPlaying ? 90 : -90 : grSet.styleBlackReborn && fb.IsPlaying ? -90 : 90, 0, grCol.styleProgressBar); } - if (pref.styleProgressBarDesign === 'rounded') { // Smooth top and bottom line edges - gr.FillGradRect(this.x + SCALE(3), this.y - 0.5, SCALE(9), 1, 179, col.styleProgressBarLineTop, 0); // Top left - gr.FillGradRect(this.x + SCALE(3), this.y + this.h - 0.5, SCALE(9), 1, 179, col.styleProgressBarLineBottom, 0); // Bottom left - gr.FillGradRect(this.w + this.x - SCALE(12), this.y - 0.5, SCALE(9), 1, 179, 0, col.styleProgressBarLineTop); // Top right - gr.FillGradRect(this.w + this.x - SCALE(12), this.y + this.h - 0.5, SCALE(9), 1, 179, 0, col.styleProgressBarLineBottom); // Bottom right + if (grSet.styleProgressBarDesign === 'rounded') { // Smooth top and bottom line edges + gr.FillGradRect(this.x + SCALE(3), this.y - 0.5, SCALE(9), 1, 179, grCol.styleProgressBarLineTop, 0); // Top left + gr.FillGradRect(this.x + SCALE(3), this.y + this.h - 0.5, SCALE(9), 1, 179, grCol.styleProgressBarLineBottom, 0); // Bottom left + gr.FillGradRect(this.w + this.x - SCALE(12), this.y - 0.5, SCALE(9), 1, 179, 0, grCol.styleProgressBarLineTop); // Top right + gr.FillGradRect(this.w + this.x - SCALE(12), this.y + this.h - 0.5, SCALE(9), 1, 179, 0, grCol.styleProgressBarLineBottom); // Bottom right } - gr.DrawLine(this.x + (pref.styleProgressBarDesign === 'rounded' ? SCALE(12) : 0), this.y, this.x + this.w - (pref.styleProgressBarDesign === 'rounded' ? SCALE(12) : 1), this.y, 1, col.styleProgressBarLineTop); - gr.DrawLine(this.x + (pref.styleProgressBarDesign === 'rounded' ? SCALE(12) : 0), this.y + this.h, this.x + this.w - (pref.styleProgressBarDesign === 'rounded' ? SCALE(12) : 1), this.y + this.h, 1, col.styleProgressBarLineBottom); + gr.DrawLine(this.x + (grSet.styleProgressBarDesign === 'rounded' ? SCALE(12) : 0), this.y, this.x + this.w - (grSet.styleProgressBarDesign === 'rounded' ? SCALE(12) : 1), this.y, 1, grCol.styleProgressBarLineTop); + gr.DrawLine(this.x + (grSet.styleProgressBarDesign === 'rounded' ? SCALE(12) : 0), this.y + this.h, this.x + this.w - (grSet.styleProgressBarDesign === 'rounded' ? SCALE(12) : 1), this.y + this.h, 1, grCol.styleProgressBarLineBottom); } // * Progress bar fill if (!fb.PlaybackLength) return; - let progressStationary = false; /* In some cases the progress bar would move backwards at the end of a song while buffering/streaming was occurring. This created strange looking jitter so now the progress bar can only increase unless the user seeked in the track. */ if (this.progressMoved || Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)) > this.progressLength) { this.progressLength = Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)); - } else { - progressStationary = true; } this.progressMoved = false; const arcIsValid2 = this.progressLength > arc * 2; // * Needed when playback starts to prevent invalid arc value crash - if (pref.styleProgressBarDesign === 'default') { - gr.FillSolidRect(this.x, this.y, this.progressLength, this.h, col.progressBarFill); + if (grSet.styleProgressBarDesign === 'default') { + gr.FillSolidRect(this.x, this.y, this.progressLength, this.h, grCol.progressBarFill); } - else if (pref.styleProgressBarDesign === 'rounded' && arcIsValid2) { - gr.FillRoundRect(this.x, this.y, this.progressLength, this.h, arc, arc, col.progressBarFill); + else if (grSet.styleProgressBarDesign === 'rounded' && arcIsValid2) { + gr.FillRoundRect(this.x, this.y, this.progressLength, this.h, arc, arc, grCol.progressBarFill); } - else if (pref.styleProgressBarDesign === 'lines') { + else if (grSet.styleProgressBarDesign === 'lines') { let progressLine = 0; if (progressLine < this.progressLength) { - gr.FillSolidRect(this.x + this.progressLength, this.y, SCALE(2), geo.progBarHeight, col.progressBarFill); + gr.FillSolidRect(this.x + this.progressLength, this.y, SCALE(2), grm.ui.progressBarH, grCol.progressBarFill); } while (progressLine < this.progressLength) { - gr.DrawLine(this.x + progressLine + SCALE(2), this.y, this.x + progressLine + SCALE(2), this.y + this.h, SCALE(2), col.progressBarFill); + gr.DrawLine(this.x + progressLine + SCALE(2), this.y, this.x + progressLine + SCALE(2), this.y + this.h, SCALE(2), grCol.progressBarFill); progressLine += SCALE(4); } } - else if (pref.styleProgressBarDesign === 'blocks' && arcIsValid2) { + else if (grSet.styleProgressBarDesign === 'blocks' && arcIsValid2) { let progressLine = 0; while (progressLine < this.progressLength) { - gr.FillSolidRect(this.x + progressLine, this.y + SCALE(2), geo.progBarHeight, geo.progBarHeight - SCALE(4), col.progressBarFill); - progressLine += geo.progBarHeight + SCALE(2); + gr.FillSolidRect(this.x + progressLine, this.y + SCALE(2), grm.ui.progressBarH, grm.ui.progressBarH - SCALE(4), grCol.progressBarFill); + progressLine += grm.ui.progressBarH + SCALE(2); } - gr.FillSolidRect(this.x + this.progressLength, this.y + 1, geo.progBarHeight, geo.progBarHeight - 1, col.progressBar); - gr.FillGradRect(this.x + this.progressLength, this.y + 1, geo.progBarHeight, geo.progBarHeight - 1, pref.styleProgressBar === 'inner' ? pref.styleBlackReborn && fb.IsPlaying ? 88 : -88 : pref.styleBlackReborn && fb.IsPlaying ? -88 : 88, 0, col.styleProgressBar); + gr.FillSolidRect(this.x + this.progressLength, this.y + 1, grm.ui.progressBarH, grm.ui.progressBarH - 1, grCol.progressBar); + gr.FillGradRect(this.x + this.progressLength, this.y + 1, grm.ui.progressBarH, grm.ui.progressBarH - 1, grSet.styleProgressBar === 'inner' ? grSet.styleBlackReborn && fb.IsPlaying ? 88 : -88 : grSet.styleBlackReborn && fb.IsPlaying ? -88 : 88, 0, grCol.styleProgressBar); } - else if (pref.styleProgressBarDesign === 'dots') { + else if (grSet.styleProgressBarDesign === 'dots') { let progressLine = 0; while (progressLine < this.progressLength) { - gr.DrawLine(this.x + this.progressLength + SCALE(10), this.y + this.h * 0.5, this.x + this.w, this.y + this.h * 0.5, SCALE(1), col.progressBar); + gr.DrawLine(this.x + this.progressLength + SCALE(10), this.y + this.h * 0.5, this.x + this.w, this.y + this.h * 0.5, SCALE(1), grCol.progressBar); gr.SetSmoothingMode(SmoothingMode.AntiAlias); - gr.DrawEllipse(this.x + progressLine, this.y + this.h * 0.5 - SCALE(1), SCALE(2), SCALE(2), SCALE(2), col.progressBarFill); + gr.DrawEllipse(this.x + progressLine, this.y + this.h * 0.5 - SCALE(1), SCALE(2), SCALE(2), SCALE(2), grCol.progressBarFill); progressLine += SCALE(8); } - const posFix = RES_4K ? pref.layout !== 'default' ? 6 : 7 : 3; - gr.DrawEllipse(this.x + progressLine, this.y + this.h * 0.5 - geo.progBarHeight * 0.5 + SCALE(2), geo.progBarHeight - SCALE(4), geo.progBarHeight - SCALE(4), SCALE(2), col.progressBarFill); // Knob outline - gr.DrawEllipse(this.x + progressLine + posFix, this.y + this.h * 0.5 - SCALE(1), SCALE(2), SCALE(2), SCALE(2), col.transportIconHovered); // Knob inner + const posFix = RES._4K ? grSet.layout !== 'default' ? 6 : 7 : 3; + gr.DrawEllipse(this.x + progressLine, this.y + this.h * 0.5 - grm.ui.progressBarH * 0.5 + SCALE(2), grm.ui.progressBarH - SCALE(4), grm.ui.progressBarH - SCALE(4), SCALE(2), grCol.progressBarFill); // Knob outline + gr.DrawEllipse(this.x + progressLine + posFix, this.y + this.h * 0.5 - SCALE(1), SCALE(2), SCALE(2), SCALE(2), grCol.transportIconHovered); // Knob inner } - else if (pref.styleProgressBarDesign === 'thin') { - gr.DrawLine(this.x, this.y + this.h * 0.5, this.x + this.w, this.y + this.h * 0.5, SCALE(1), col.progressBar); + else if (grSet.styleProgressBarDesign === 'thin') { + gr.DrawLine(this.x, this.y + this.h * 0.5, this.x + this.w, this.y + this.h * 0.5, SCALE(1), grCol.progressBar); gr.SetSmoothingMode(SmoothingMode.AntiAlias); - gr.FillSolidRect(this.x, this.y + this.h * 0.5 - SCALE(2), this.progressLength, SCALE(4), col.progressBarFill); - gr.FillSolidRect(this.x + this.progressLength, this.y + this.h * 0.5 - SCALE(3), SCALE(6), SCALE(6), col.progressBarFill); + gr.FillSolidRect(this.x, this.y + this.h * 0.5 - SCALE(2), this.progressLength, SCALE(4), grCol.progressBarFill); + gr.FillSolidRect(this.x + this.progressLength, this.y + this.h * 0.5 - SCALE(3), SCALE(6), SCALE(6), grCol.progressBarFill); } - if (!['dots', 'thin'].includes(pref.styleProgressBarDesign) && (pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner') && fb.IsPlaying && arcIsValid2) { - if (pref.styleProgressBarDesign === 'rounded') { - FillGradRoundRect(gr, this.x, this.y, this.progressLength + SCALE(2), this.h + SCALE(2.5), arc, arc, pref.styleProgressBarFill === 'inner' ? -88 : 88, col.styleProgressBarFill, 0); + if (!['dots', 'thin'].includes(grSet.styleProgressBarDesign) && (grSet.styleProgressBarFill === 'bevel' || grSet.styleProgressBarFill === 'inner') && fb.IsPlaying && arcIsValid2) { + if (grSet.styleProgressBarDesign === 'rounded') { + FillGradRoundRect(gr, this.x, this.y, this.progressLength + SCALE(2), this.h + SCALE(2.5), arc, arc, grSet.styleProgressBarFill === 'inner' ? -88 : 88, grCol.styleProgressBarFill, 0); } else { - gr.FillGradRect(this.x, this.y, this.progressLength, this.h, pref.styleProgressBarFill === 'inner' ? -90 : 89, 0, col.styleProgressBarFill); + gr.FillGradRect(this.x, this.y, this.progressLength, this.h, grSet.styleProgressBarFill === 'inner' ? -90 : 89, 0, grCol.styleProgressBarFill); } } - else if (pref.styleProgressBarFill === 'blend' && fb.IsPlaying && albumArt && blendedImg && arcIsValid2) { - if (pref.styleProgressBarDesign === 'rounded') { - FillBlendedRoundRect(gr, this.x, this.y, this.progressLength + SCALE(2), this.h + SCALE(2.5), arc, arc, 88, blendedImg, 0); + else if (grSet.styleProgressBarFill === 'blend' && fb.IsPlaying && grm.ui.albumArt && grCol.imgBlended && arcIsValid2) { + if (grSet.styleProgressBarDesign === 'rounded') { + FillBlendedRoundRect(gr, this.x, this.y, this.progressLength + SCALE(2), this.h + SCALE(2.5), arc, arc, 88, grCol.imgBlended, 0); } else { - gr.DrawImage(blendedImg, this.x, this.y, this.progressLength, this.h, 0, this.h, blendedImg.Width, blendedImg.Height); + gr.DrawImage(grCol.imgBlended, this.x, this.y, this.progressLength, this.h, 0, this.h, grCol.imgBlended.Width, grCol.imgBlended.Height); } } } + /** + * Sets the vertical progress bar position. + * @param {number} y - The y-coordinate. + */ + setY(y) { + this.y = y; + } + + /** + * Sets the playback time of the progress bar. + * @param {number} x - The x-coordinate. + * @private + */ + setPlaybackTime(x) { + let v = (x - this.x) / this.w; + v = (v < 0) ? 0 : (v < 1) ? v : 1; + if (fb.PlaybackTime !== v * fb.PlaybackLength) { + fb.PlaybackTime = v * fb.PlaybackLength; + } + } + /** * Updates the progress bar state. */ repaint() { window.RepaintRect(this.x, this.y, this.w, this.h); } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Checks if the mouse is within the boundaries of the progress bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { @@ -2187,9 +2588,8 @@ class ProgressBar { /** * Handles left mouse button down click events and enables dragging. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ on_mouse_lbtn_down(x, y) { this.drag = true; @@ -2197,9 +2597,8 @@ class ProgressBar { /** * Handles left mouse button up click events and disables dragging and updates the playback time. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ on_mouse_lbtn_up(x, y) { this.drag = false; @@ -2210,8 +2609,8 @@ class ProgressBar { /** * Handles mouse movement events and updates the playback time based on the mouse movement if a drag event is occurring. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ on_mouse_move(x, y) { if (this.drag) { @@ -2221,14 +2620,17 @@ class ProgressBar { /** * Sets the size and position of the progress bar and updates them on window resizing. + * @param {number} w - The width of the window or element. + * @param {number} h - The height of the window or element. */ on_size(w, h) { - this.x = SCALE(pref.layout !== 'default' ? 20 : 40); + this.x = SCALE(grSet.layout !== 'default' ? 20 : 40); this.y = 0; - this.w = w - SCALE(pref.layout !== 'default' ? 40 : 80); - this.h = geo.progBarHeight; + this.w = w - SCALE(grSet.layout !== 'default' ? 40 : 80); + this.h = grm.ui.progressBarH; this.progressMoved = true; } + // #endregion } @@ -2236,90 +2638,148 @@ class ProgressBar { // * PEAKMETER BAR * // /////////////////////// /** - * Creates the peakmeter bar in the lower bar when enabled, quick access via right click on lower bar. + * A class that creates the peakmeter bar in the lower bar when enabled. + * Quick access via right click context menu on lower bar. */ class PeakmeterBar { /** - * @param {number} ww window.Width - * @param {number} wh window.Height + * Creates the `PeakmeterBar` instance. + * @param {number} ww - Window.Width. + * @param {number} wh - Window.Height. */ constructor(ww, wh) { - if (componentVUMeter) this.VUMeter = new ActiveXObject('VUMeter'); + if (Component.VUMeter) { + this.VUMeter = new ActiveXObject('VUMeter'); + } // * Geometry - Style Horizontal - this.x = SCALE(pref.layout !== 'default' ? 20 : 40); + /** @public @type {number} */ + this.x = SCALE(grSet.layout !== 'default' ? 20 : 40); + /** @public @type {number} */ this.y = 0; - this.w = ww - SCALE(pref.layout !== 'default' ? 40 : 80); + /** @public @type {number} */ + this.w = ww - SCALE(grSet.layout !== 'default' ? 40 : 80); + /** @public @type {number} */ this.w2 = 0; - this.h = geo.peakmeterBarHeight; - this.bar_h = pref.layout !== 'default' ? SCALE(2) : SCALE(4); + /** @public @type {number} */ + this.h = grm.ui.peakmeterBarH; + /** @private @type {number} */ + this.bar_h = grSet.layout !== 'default' ? SCALE(2) : SCALE(4); + /** @private @type {number} */ this.offset = 0; + /** @private @type {number} */ this.middleOffset = 0; + /** @private @type {number} */ this.middle_w = 0; // * Top + /** @private @type {number} */ this.outerLeft_w = 0; + /** @private @type {number} */ this.outerLeft_w_old = 0; + /** @private @type {number} */ this.outerLeftAnim_w = 0; + /** @private @type {number} */ this.outerLeftAnim_x = 0; + /** @private @type {number} */ this.outerLeft_k = 0; + /** @private @type {number} */ this.mainLeft_x = 0; + /** @private @type {number} */ this.mainLeftAnim_x = 0; + /** @private @type {number} */ this.mainLeftAnim2_x = 0; + /** @private @type {number} */ this.mainLeft_k = 0; + /** @private @type {number} */ this.mainLeft2_k = 0; // * Bottom + /** @private @type {number} */ this.outerRight_w = 0; + /** @private @type {number} */ this.outerRight_w_old = 0; + /** @private @type {number} */ this.outerRightAnim_w = 0; + /** @private @type {number} */ this.outerRightAnim_x = 0; + /** @private @type {number} */ this.outerRight_k = 0; + /** @private @type {number} */ this.mainRight_x = 0; + /** @private @type {number} */ this.mainRightAnim_x = 0; + /** @private @type {number} */ this.mainRightAnim2_x = 0; + /** @private @type {number} */ this.mainRight_k = 0; + /** @private @type {number} */ this.mainRight2_k = 0; // * Progress bar state + /** @public @type {number} */ this.progressLength = 0; + /** @public @type {boolean} */ this.progressMoved = false; + /** @private @type {boolean} */ this.drag = false; // * Mouse events + /** @private @type {number} */ this.pos_x = 0; + /** @private @type {number} */ this.pos_y = 0; + /** @private @type {boolean} */ this.on_mouse = false; + /** @private @type {boolean} */ this.wheel = false; // * Text - this.textFont = gdi.Font('Segoe UI', RES_4K ? 16 : 9, 1); + /** @private @type {GdiFont} */ + this.textFont = gdi.Font('Segoe UI', RES._4K ? 16 : 9, 1); + /** @private @type {number} */ this.textWidth = 0; + /** @private @type {number} */ this.textHeight = 0; + /** @private @type {string} */ this.tooltipText = ''; + /** @private @type {number} */ this.tooltipTimer = null; // * Volume - this.toDb = (Level) => Math.round(2000 * Math.log(Level) / Math.LN10) / 100; + /** + * Calculates the decibel (dB) value of the given volume. + * @type {Function} + * @private + */ + this.toDecibel = (Level) => Math.round(2000 * Math.log(Level) / Math.LN10) / 100; + /** @private @type {number[]} */ this.db_middle = [-100, -95, -90, -85, -80, -75, -70, -65, -62.5, -60, -57.5, -55, -52.5, -50, -47.5, -45, -42.5, -40, -37.5, -35, -32.5, -30, -27.5, -25, -22.5]; + /** @private @type {number[]} */ this.db = [-20, -17.5, -15, -12.5, -10, -7.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, 0, 0.1, 1, 1.5, 2, 2.5, 3, 3.5, 5]; + /** @private @type {number[]} */ this.db_vert = - pref.peakmeterBarVertDbRange === 220 ? [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2] : - pref.peakmeterBarVertDbRange === 215 ? [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2] : - pref.peakmeterBarVertDbRange === 210 ? [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2] : - pref.peakmeterBarVertDbRange === 320 ? [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3] : - pref.peakmeterBarVertDbRange === 315 ? [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3] : - pref.peakmeterBarVertDbRange === 310 ? [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3] : - pref.peakmeterBarVertDbRange === 520 ? [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] : - pref.peakmeterBarVertDbRange === 515 ? [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] : - pref.peakmeterBarVertDbRange === 510 ? [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] : ''; - + grSet.peakmeterBarVertDbRange === 220 ? [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2] : + grSet.peakmeterBarVertDbRange === 215 ? [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2] : + grSet.peakmeterBarVertDbRange === 210 ? [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2] : + grSet.peakmeterBarVertDbRange === 320 ? [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3] : + grSet.peakmeterBarVertDbRange === 315 ? [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3] : + grSet.peakmeterBarVertDbRange === 310 ? [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3] : + grSet.peakmeterBarVertDbRange === 520 ? [-20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] : + grSet.peakmeterBarVertDbRange === 515 ? [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] : + grSet.peakmeterBarVertDbRange === 510 ? [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] : ''; + + /** @private @type {number} */ this.points_middle = this.db_middle.length; + /** @private @type {number} */ this.points = this.db.length; + /** @private @type {number} */ this.points_vert = this.db_vert.length; + /** @private @type {number[]} */ this.leftPeaks_s = []; + /** @private @type {number[]} */ this.rightPeaks_s = []; for (let i = 0; i <= this.points_vert; i++) { @@ -2328,262 +2788,139 @@ class PeakmeterBar { } // * Geometry - Style Vertical - this.vertBar_offset = ((this.w / this.points_vert) + ((pref.peakmeterBarVertSize === 'min' ? 2 : pref.peakmeterBarVertSize) / this.points_vert * 0.5)) * 0.5; - this.vertBar_w = pref.peakmeterBarVertSize === 'min' ? Math.ceil(this.vertBar_offset * 0.1 * 0.5) : this.vertBar_offset - pref.peakmeterBarVertSize * 0.5; + /** @private @type {number} */ + this.vertBar_offset = ((this.w / this.points_vert) + ((grSet.peakmeterBarVertSize === 'min' ? 2 : grSet.peakmeterBarVertSize) / this.points_vert * 0.5)) * 0.5; + /** @private @type {number} */ + this.vertBar_w = grSet.peakmeterBarVertSize === 'min' ? Math.ceil(this.vertBar_offset * 0.1 * 0.5) : this.vertBar_offset - grSet.peakmeterBarVertSize * 0.5; + /** @private @type {number} */ this.vertBar_h = 2; + /** @private @type {number} */ this.vertLeft_x = this.x; + /** @private @type {number} */ this.vertRight_x = this.vertLeft_x + this.vertBar_offset * this.points_vert; // * Colors + /** @private @type {number} */ this.separator = 0; for (let i = 0; i <= this.db.length; i++) { if (this.db[i] === 0) this.separator = i; } + /** @private @type {number} */ this.sep1 = this.separator; + /** @private @type {number} */ this.sep2 = this.points - this.sep1; + this.setColors(fb.GetNowPlaying()); } - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** - * Sets all vertical peakmeter bar positions. - * Bars are ordered from top to bottom. - * @param {number} y The y-coordinate. + * Draws the peakmeter bar in various peakmeter bar designs. + * @param {GdiGraphics} gr - The GDI graphics object. */ - setY(y) { - this.y = y; - this.overLeft_y = this.y; - this.outerLeft_y = this.overLeft_y + this.bar_h * 0.5; - this.mainLeft_y = this.outerLeft_y + this.bar_h; - this.middleLeft_y = this.mainLeft_y + this.bar_h + SCALE(1); - this.middleRight_y = this.middleLeft_y + this.bar_h * 0.5; - this.mainRight_y = this.middleRight_y + this.bar_h * 0.5 + SCALE(1); - this.outerRight_y = this.mainRight_y + this.bar_h; - this.overRight_y = this.outerRight_y + this.bar_h; - this.text_y = this.outerRight_y + this.bar_h * 2; + draw(gr) { + if (grSet.peakmeterBarDesign === 'horizontal') { + this.drawPeakmeterBarHorizontal(gr); + } + else if (grSet.peakmeterBarDesign === 'horizontal_center') { + this.drawPeakmeterBarCenter(gr); + } + else if (grSet.peakmeterBarDesign === 'vertical') { + this.drawPeakmeterBarVertical(gr); + } + if (grSet.peakmeterBarInfo) { + this.drawPeakmeterBarInfo(gr); + } } /** - * Monitors volume levels and peaks and sets horizontal or vertical animations based on peakmeterBarDesign. + * Draws the peakmeter bar in horizontal design. + * @param {GdiGraphics} gr - The GDI graphics object. */ - setAnimation() { - // * Set and monitor volume level/peaks from VUMeter - this.leftLevel = this.toDb(this.VUMeter.LeftLevel); - this.leftPeak = this.toDb(this.VUMeter.LeftPeak); - this.rightLevel = this.toDb(this.VUMeter.RightLevel); - this.rightPeak = this.toDb(this.VUMeter.RightPeak); + drawPeakmeterBarHorizontal(gr) { + // * Progress Bar + if (grSet.peakmeterBarProgBar && fb.PlaybackLength) { + if (this.progressMoved || Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)) > this.progressLength) { + this.progressLength = Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)); + } + this.progressMoved = false; - // * Debug stuff - DebugLog('LEFT PEAKS: ', this.leftPeak, ' RIGHT PEAKS: ', this.rightPeak); - DebugLog('LEFT LEVEL: ', this.leftLevel, ' RIGHT LEVEL: ', this.rightLevel, '\n\n'); + gr.FillSolidRect(this.x, this.middleLeft_y, this.w, this.bar_h, grCol.peakmeterBarProg); + gr.FillSolidRect(this.x, this.middleLeft_y, this.progressLength, this.bar_h, grCol.peakmeterBarProgFill); + } + // * Middle bars + else if (grSet.peakmeterBarMiddleBars) { + for (let i = 0; i <= this.points_middle; i++) { + if (this.leftPeak > this.db_middle[i]) { + gr.FillSolidRect(this.x + i * this.middleOffset, this.middleLeft_y, this.middle_w, this.bar_h * 0.5, grCol.peakmeterBarProgFill); + } + if (this.rightPeak > this.db_middle[i]) { + gr.FillSolidRect(this.x + i * this.middleOffset, this.middleRight_y, this.middle_w, this.bar_h * 0.5, grCol.peakmeterBarProgFill); + } + } + } + // * Grid + if (grSet.peakmeterBarGrid) { + gr.FillSolidRect(this.x, this.y, this.w, this.bar_h, grCol.peakmeterBarProg); + gr.FillSolidRect(this.x, this.outerRight_y, this.w, this.bar_h, grCol.peakmeterBarProg); + } - // * Set horizontal animation - if (pref.peakmeterBarDesign === 'horizontal' || pref.peakmeterBarDesign === 'horizontal_center') { - // * Main left middle peaks - if (this.mainLeftAnim_x <= this.mainLeft_x) { - this.mainLeftAnim_x = this.mainLeft_x; - this.mainLeftAnim2_x = this.mainLeft_x; - this.mainLeft_k = 0; - this.mainLeft2_k = 0; - }; + for (let i = 0; i <= this.points; i++) { + // * MAIN BARS * // + if (this.leftPeak > this.db[i]) { + if (grSet.peakmeterBarMainBars) { + // * Main left bars + gr.FillSolidRect(this.x + i * this.offset, this.mainLeft_y, this.w2, this.bar_h, this.color[i]); + } - this.mainLeft_k = this.mainLeft_k + 0.3 ** 2; - this.mainLeftAnim_x = this.mainLeftAnim_x - this.mainLeft_k; - this.mainLeft2_k = this.mainLeft2_k + 1.1 ** 2; - this.mainLeftAnim2_x = this.mainLeftAnim2_x + this.mainLeft2_k; + // * Main left middle peaks + if (this.leftPeak < this.db[i + 1]) { + this.mainLeft_x = i * this.offset; + } + if (grSet.peakmeterBarMainPeaks) { + gr.FillSolidRect(this.x + this.mainLeftAnim_x + this.offset, this.mainLeft_y, this.w2 * 0.66, this.bar_h, this.color[Math.round(this.mainLeftAnim_x / this.offset)]); - // * Main right middle peaks - if (this.mainRightAnim_x <= this.mainRight_x) { - this.mainRightAnim_x = this.mainRight_x; - this.mainRightAnim2_x = this.mainRight_x; - this.mainRight_k = 0; - this.mainRight2_k = 0; - }; + // * Main left top peaks + const x = Clamp(this.x + this.mainLeftAnim2_x + this.offset + this.w2 * 0.66, this.x, this.x + this.w - this.w2 * 0.33); // Don't extend right edge of bar + const w = x > this.w + this.w2 * 0.5 ? 0 : this.w2 * 0.33; // Don't extend right edge of bar + gr.FillSolidRect(x, this.mainLeft_y, w, this.bar_h, this.color[Math.round(this.mainLeftAnim_x / this.offset)]); + } + } + if (this.rightPeak > this.db[i]) { + if (grSet.peakmeterBarMainBars) { + // * Main right bars + gr.FillSolidRect(this.x + i * this.offset, this.mainRight_y, this.w2, this.bar_h, this.color[i]); + } - this.mainRight_k = this.mainRight_k + 0.3 ** 2; - this.mainRightAnim_x = this.mainRightAnim_x - this.mainRight_k; - this.mainRight2_k = this.mainRight2_k + 1.1 ** 2; - this.mainRightAnim2_x = this.mainRightAnim2_x + this.mainRight2_k; + // * Main right middle peaks + if (this.rightPeak < this.db[i + 1]) { + this.mainRight_x = i * this.offset; + } + if (grSet.peakmeterBarMainPeaks) { + gr.FillSolidRect(this.x + this.mainRightAnim_x + this.offset, this.mainRight_y, this.w2 * 0.66, this.bar_h, this.color[Math.round(this.mainRightAnim_x / this.offset)]); - // * Outer left peaks - if (this.outerLeftAnim_x <= this.outerLeft_w) { - this.outerLeftAnim_x = this.outerLeft_w; - this.outerLeft_k = 0; - this.outerLeftAnim_w = this.outerLeft_w - this.outerLeft_w_old < 1 ? this.outerLeftAnim_w : this.outerLeft_w - this.outerLeft_w_old + 10; - } else { - this.outerLeft_w_old = this.outerLeft_w; - }; + // * Main right top peaks + const x = Clamp(this.x + this.mainRightAnim2_x + this.offset + this.w2 * 0.66, this.x, this.x + this.w - this.w2 * 0.33); // Don't extend right edge of bar + const w = x >= this.w + this.w2 * 0.5 ? 0 : this.w2 * 0.33; // Don't extend right edge of bar + gr.FillSolidRect(x, this.mainRight_y, w, this.bar_h, this.color[Math.round(this.mainRightAnim_x / this.offset)]); + } + } - this.outerLeft_k = this.outerLeft_k + 0.3 ** 2; - this.outerLeftAnim_x = this.outerLeftAnim_x - this.outerLeft_k; - this.outerLeftAnim_w = this.outerLeftAnim_w - this.outerLeft_k * 2; - - // * Outer right peaks - if (this.outerRightAnim_x <= this.outerRight_w) { - this.outerRightAnim_x = this.outerRight_w; - this.outerRight_k = 0; - this.outerRightAnim_w = this.outerRight_w - this.outerRight_w_old < 1 ? this.outerRightAnim_w : this.outerRight_w - this.outerRight_w_old + 10; - } else { - this.outerRight_w_old = this.outerRight_w; - }; - - this.outerRight_k = this.outerRight_k + 0.3 ** 2; - this.outerRightAnim_x = this.outerRightAnim_x - this.outerRight_k; - this.outerRightAnim_w = this.outerRightAnim_w - this.outerRight_k * 2; - } - // * Set vertical animation - else if (pref.peakmeterBarDesign === 'vertical') { - for (let j = 0; j < this.leftPeaks_s.length; j++) this.leftPeaks_s[j] = this.leftPeaks_s[j] < this.h ? this.leftPeaks_s[j] + 2 : this.h; - for (let j = 0; j < this.rightPeaks_s.length; j++) this.rightPeaks_s[j] = this.rightPeaks_s[j] < this.h ? this.rightPeaks_s[j] + 2 : this.h; - } - } - - /** - * Sets the peakmeter bar colors. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ - setColors(metadb) { - let img = gdi.CreateImage(1, 1); - const g = img.GetGraphics(); - if (img) img.ReleaseGraphics(g); - if (metadb) img = utils.GetAlbumArtV2(metadb, 0); - - if (metadb && img) { - this.colors = JSON.parse(img.GetColourSchemeJSON(4)); - this.c1 = col.peakmeterBarFillMiddle; // this.colors[1].col; - this.c2 = col.peakmeterBarFillTop; // this.colors[2].col; - this.c3 = col.peakmeterBarFillBack; // this.colors[3].col; - } else { - this.setDefaultColors(); - } - - this.color = []; - this.combinedColor1 = []; - this.combinedColor2 = []; - this.color1 = [this.c2, this.c3]; - this.color2 = [this.c3, this.c1]; - - for (let j = 0; j < this.sep1; j++) this.combinedColor1.push(CombineColors(this.color1[0], this.color1[1], j / this.sep1)); - for (let j = 0; j < this.sep2; j++) this.combinedColor2.push(CombineColors(this.color2[0], this.color2[1], j / this.sep2)); - - this.color = this.combinedColor1.concat(this.combinedColor2); - } - - /** - * Sets the default peakmeter bar colors. - */ - setDefaultColors() { - this.c1 = col.peakmeterBarFillMiddle; // RGB(0, 200, 255); - this.c2 = col.peakmeterBarFillTop; // RGB(255, 255, 0); - this.c3 = col.peakmeterBarFillBack; // RGB(230, 230, 30); - this.color1 = [this.c3, this.c1]; - this.color2 = [this.c2, this.c3]; - } - - /** - * Sets the playback time of the progress bar. - * @param {number} x The x-coordinate. - * @private - */ - setPlaybackTime(x) { - let v = (x - this.x) / this.w; - v = (v < 0) ? 0 : (v < 1) ? v : 1; - if (fb.PlaybackTime !== v * fb.PlaybackLength) { - fb.PlaybackTime = v * fb.PlaybackLength; - } - } - - /** - * Draws the peakmeter bar in horizontal design. - * @param {GdiGraphics} gr - */ - drawPeakmeterBarHorizontal(gr) { - // * Progress Bar - if (pref.peakmeterBarProgBar && fb.PlaybackLength) { - let progressStationary = false; - if (this.progressMoved || Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)) > this.progressLength) { - this.progressLength = Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)); - } else { - progressStationary = true; - } - this.progressMoved = false; - - gr.FillSolidRect(this.x, this.middleLeft_y, this.w, this.bar_h, col.peakmeterBarProg); - gr.FillSolidRect(this.x, this.middleLeft_y, this.progressLength, this.bar_h, col.peakmeterBarProgFill); - } - // * Middle bars - else if (pref.peakmeterBarMiddleBars) { - for (let i = 0; i <= this.points_middle; i++) { - if (this.leftPeak > this.db_middle[i]) { - gr.FillSolidRect(this.x + i * this.middleOffset, this.middleLeft_y, this.middle_w, this.bar_h * 0.5, col.peakmeterBarProgFill); - } - if (this.rightPeak > this.db_middle[i]) { - gr.FillSolidRect(this.x + i * this.middleOffset, this.middleRight_y, this.middle_w, this.bar_h * 0.5, col.peakmeterBarProgFill); - } - } - } - // * Grid - if (pref.peakmeterBarGrid) { - gr.FillSolidRect(this.x, this.y, this.w, this.bar_h, col.peakmeterBarProg); - gr.FillSolidRect(this.x, this.outerRight_y, this.w, this.bar_h, col.peakmeterBarProg); - } - - for (let i = 0; i <= this.points; i++) { - // * MAIN BARS * // - if (this.leftPeak > this.db[i]) { - if (pref.peakmeterBarMainBars) { - // * Main left bars - gr.FillSolidRect(this.x + i * this.offset, this.mainLeft_y, this.w2, this.bar_h, this.color[i]); - } - - // * Main left middle peaks - if (this.leftPeak < this.db[i + 1]) { - this.mainLeft_x = i * this.offset; - }; - if (pref.peakmeterBarMainPeaks) { - gr.FillSolidRect(this.x + this.mainLeftAnim_x + this.offset, this.mainLeft_y, this.w2 * 0.66, this.bar_h, this.color[Math.round(this.mainLeftAnim_x / this.offset)]); - - // * Main left top peaks - const x = Clamp(this.x + this.mainLeftAnim2_x + this.offset + this.w2 * 0.66, this.x, this.x + this.w - this.w2 * 0.33); // Don't extend right edge of bar - const w = x > this.w + this.w2 * 0.5 ? 0 : this.w2 * 0.33; // Don't extend right edge of bar - gr.FillSolidRect(x, this.mainLeft_y, w, this.bar_h, this.color[Math.round(this.mainLeftAnim_x / this.offset)]); - } - } - if (this.rightPeak > this.db[i]) { - if (pref.peakmeterBarMainBars) { - // * Main right bars - gr.FillSolidRect(this.x + i * this.offset, this.mainRight_y, this.w2, this.bar_h, this.color[i]); - } - - // * Main right middle peaks - if (this.rightPeak < this.db[i + 1]) { - this.mainRight_x = i * this.offset; - }; - if (pref.peakmeterBarMainPeaks) { - gr.FillSolidRect(this.x + this.mainRightAnim_x + this.offset, this.mainRight_y, this.w2 * 0.66, this.bar_h, this.color[Math.round(this.mainRightAnim_x / this.offset)]); - - // * Main right top peaks - const x = Clamp(this.x + this.mainRightAnim2_x + this.offset + this.w2 * 0.66, this.x, this.x + this.w - this.w2 * 0.33); // Don't extend right edge of bar - const w = x >= this.w + this.w2 * 0.5 ? 0 : this.w2 * 0.33; // Don't extend right edge of bar - gr.FillSolidRect(x, this.mainRight_y, w, this.bar_h, this.color[Math.round(this.mainRightAnim_x / this.offset)]); - } - } - - // * OUTER BARS * // - if (this.leftLevel > this.db[i]) { - // * Outer left bars - if (this.leftLevel < this.db[i + 1]) { - this.outerLeft_w = i * this.offset + this.offset / Math.abs(this.db[i + 1] - this.db[i]) * Math.abs(this.leftLevel - this.db[i]) - this.x; - } - if (pref.peakmeterBarOuterBars) { - gr.FillSolidRect(this.x, this.outerLeft_y, this.outerLeft_w, this.bar_h, this.color[1]); - } + // * OUTER BARS * // + if (this.leftLevel > this.db[i]) { + // * Outer left bars + if (this.leftLevel < this.db[i + 1]) { + this.outerLeft_w = i * this.offset + this.offset / Math.abs(this.db[i + 1] - this.db[i]) * Math.abs(this.leftLevel - this.db[i]) - this.x; + } + if (grSet.peakmeterBarOuterBars) { + gr.FillSolidRect(this.x, this.outerLeft_y, this.outerLeft_w, this.bar_h, this.color[1]); + } // * Outer left peaks - if (pref.peakmeterBarOuterPeaks) { + if (grSet.peakmeterBarOuterPeaks) { const x = Clamp(this.x + this.outerLeftAnim_x, this.x, this.x + this.w - this.outerLeftAnim_w); // Don't extend right edge of bar gr.FillSolidRect(x, this.outerLeft_y, this.outerLeftAnim_w <= 0 ? 2 : this.outerLeftAnim_w, this.bar_h, this.color[1]); } @@ -2593,19 +2930,19 @@ class PeakmeterBar { if (this.rightLevel < this.db[i + 1]) { this.outerRight_w = i * this.offset + this.offset / Math.abs(this.db[i + 1] - this.db[i]) * Math.abs(this.rightLevel - this.db[i]) - this.x; } - if (pref.peakmeterBarOuterBars) { + if (grSet.peakmeterBarOuterBars) { gr.FillSolidRect(this.x, this.outerRight_y, this.outerRight_w, this.bar_h, this.color[1]); } // * Outer right peaks - if (pref.peakmeterBarOuterPeaks) { + if (grSet.peakmeterBarOuterPeaks) { const x = Clamp(this.x + this.outerRightAnim_x, this.x, this.x + this.w - this.outerRightAnim_w); // Don't extend right edge of bar gr.FillSolidRect(x, this.outerRight_y, this.outerRightAnim_w <= 0 ? 2 : this.outerRightAnim_w, this.bar_h, this.color[1]); } } // * OUTER OVER BARS * // - if (pref.peakmeterBarOverBars) { + if (grSet.peakmeterBarOverBars) { const overLeft = this.outerLeftAnim_x + this.outerLeftAnim_w - this.w; const overRight = this.outerRightAnim_x + this.outerRightAnim_w - this.w; const xLeft = this.w - overLeft - this.x; @@ -2622,46 +2959,43 @@ class PeakmeterBar { /** * Draws the peakmeter bar in horizontal center design. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ drawPeakmeterBarCenter(gr) { // * Progress Bar - if (pref.peakmeterBarProgBar && fb.PlaybackLength) { - let progressStationary = false; + if (grSet.peakmeterBarProgBar && fb.PlaybackLength) { if (this.progressMoved || Math.floor(this.w * 0.5 * (fb.PlaybackTime / fb.PlaybackLength)) > this.progressLength) { this.progressLength = Math.floor(this.w * 0.5 * (fb.PlaybackTime / fb.PlaybackLength)); - } else { - progressStationary = true; } this.progressMoved = false; - gr.FillSolidRect(this.x, this.middleLeft_y, this.w, this.bar_h, col.peakmeterBarProg); - gr.FillSolidRect(this.x + this.w * 0.5 - this.progressLength, this.middleLeft_y, this.progressLength, this.bar_h, col.peakmeterBarProgFill); - gr.FillSolidRect(this.x + this.w * 0.5, this.middleLeft_y, this.progressLength, this.bar_h, col.peakmeterBarProgFill); + gr.FillSolidRect(this.x, this.middleLeft_y, this.w, this.bar_h, grCol.peakmeterBarProg); + gr.FillSolidRect(this.x + this.w * 0.5 - this.progressLength, this.middleLeft_y, this.progressLength, this.bar_h, grCol.peakmeterBarProgFill); + gr.FillSolidRect(this.x + this.w * 0.5, this.middleLeft_y, this.progressLength, this.bar_h, grCol.peakmeterBarProgFill); } // * Middle bars - else if (pref.peakmeterBarMiddleBars) { + else if (grSet.peakmeterBarMiddleBars) { for (let i = 0; i <= this.points_middle; i++) { if (this.leftPeak > this.db_middle[i]) { - gr.FillSolidRect(this.x * 0.5 + this.w * 0.5 - i * this.middleOffset + 1, this.middleLeft_y, this.middle_w, this.bar_h * 0.5, col.peakmeterBarProgFill); - gr.FillSolidRect(this.x + this.w * 0.5 + i * this.middleOffset - 1, this.middleLeft_y, this.middle_w, this.bar_h * 0.5, col.peakmeterBarProgFill); + gr.FillSolidRect(this.x * 0.5 + this.w * 0.5 - i * this.middleOffset + 1, this.middleLeft_y, this.middle_w, this.bar_h * 0.5, grCol.peakmeterBarProgFill); + gr.FillSolidRect(this.x + this.w * 0.5 + i * this.middleOffset - 1, this.middleLeft_y, this.middle_w, this.bar_h * 0.5, grCol.peakmeterBarProgFill); } if (this.rightPeak > this.db_middle[i]) { - gr.FillSolidRect(this.x * 0.5 + this.w * 0.5 - i * this.middleOffset + 1, this.middleRight_y, this.middle_w, this.bar_h * 0.5, col.peakmeterBarProgFill); - gr.FillSolidRect(this.x + this.w * 0.5 + i * this.middleOffset - 1, this.middleRight_y, this.middle_w, this.bar_h * 0.5, col.peakmeterBarProgFill); + gr.FillSolidRect(this.x * 0.5 + this.w * 0.5 - i * this.middleOffset + 1, this.middleRight_y, this.middle_w, this.bar_h * 0.5, grCol.peakmeterBarProgFill); + gr.FillSolidRect(this.x + this.w * 0.5 + i * this.middleOffset - 1, this.middleRight_y, this.middle_w, this.bar_h * 0.5, grCol.peakmeterBarProgFill); } } } // * Grid - if (pref.peakmeterBarGrid) { - gr.FillSolidRect(this.x, this.y, this.w, this.bar_h, col.peakmeterBarProg); - gr.FillSolidRect(this.x, this.outerRight_y, this.w, this.bar_h, col.peakmeterBarProg); + if (grSet.peakmeterBarGrid) { + gr.FillSolidRect(this.x, this.y, this.w, this.bar_h, grCol.peakmeterBarProg); + gr.FillSolidRect(this.x, this.outerRight_y, this.w, this.bar_h, grCol.peakmeterBarProg); } for (let i = 0; i <= this.points; i++) { // * MAIN BARS * // if (this.leftPeak > this.db[i]) { - if (pref.peakmeterBarMainBars) { + if (grSet.peakmeterBarMainBars) { // * Main left bars const xLeft = this.x * 0.5 + this.w * 0.5 - i * this.offset + 1; const xRight = this.x + i * this.offset + this.w * 0.5 - 1; @@ -2672,8 +3006,8 @@ class PeakmeterBar { // * Main left middle peaks if (this.leftPeak < this.db[i + 1]) { this.mainLeft_x = i * this.offset; - }; - if (pref.peakmeterBarMainPeaks) { + } + if (grSet.peakmeterBarMainPeaks) { const xLeft = this.x * 0.5 + this.w * 0.5 - (this.mainLeftAnim_x + this.offset) + this.w2 * 0.33; const xRight = this.w * 0.5 + this.x + this.mainLeftAnim_x + this.offset; gr.FillSolidRect(xLeft, this.mainLeft_y, this.w2 * 0.66, this.bar_h, this.color[Math.round(this.mainLeftAnim_x / this.offset)]); @@ -2689,7 +3023,7 @@ class PeakmeterBar { } } if (this.rightPeak > this.db[i]) { - if (pref.peakmeterBarMainBars) { + if (grSet.peakmeterBarMainBars) { // * Main right bars const xLeft = this.x * 0.5 + this.w * 0.5 - i * this.offset + 1; const xRight = this.x + i * this.offset + this.w * 0.5 - 1; @@ -2700,8 +3034,8 @@ class PeakmeterBar { // * Main right middle peaks if (this.rightPeak < this.db[i + 1]) { this.mainRight_x = i * this.offset; - }; - if (pref.peakmeterBarMainPeaks) { + } + if (grSet.peakmeterBarMainPeaks) { const xLeft = this.x * 0.5 + this.w * 0.5 - (this.mainRightAnim_x + this.offset) + this.w2 * 0.33; const xRight = this.x + this.mainRightAnim_x + this.offset + this.w * 0.5; gr.FillSolidRect(xLeft, this.mainRight_y, this.w2 * 0.66, this.bar_h, this.color[Math.round(this.mainRightAnim_x / this.offset)]); @@ -2723,7 +3057,7 @@ class PeakmeterBar { if (this.leftLevel < this.db[i + 1]) { this.outerLeft_w = i * this.offset + this.offset / Math.abs(this.db[i + 1] - this.db[i]) * Math.abs(this.leftLevel - this.db[i]) - this.x; } - if (pref.peakmeterBarOuterBars) { + if (grSet.peakmeterBarOuterBars) { const xLeft = Clamp(this.x + this.w * 0.5 - this.outerLeft_w, this.x, this.w * 0.5); const xRight = this.x + this.w * 0.5; const w = Clamp(this.outerLeft_w, 0, this.w * 0.5); @@ -2732,7 +3066,7 @@ class PeakmeterBar { } // * Outer left peaks - if (pref.peakmeterBarOuterPeaks) { + if (grSet.peakmeterBarOuterPeaks) { const clamped_x = Clamp(this.x + this.outerLeftAnim_x, this.x, this.x + this.w * 0.5 - this.outerLeftAnim_w); // Don't extend left edge of bar const w = this.outerLeftAnim_w <= 0 ? 2 : this.outerLeftAnim_w; const xLeft = this.w * 0.5 + this.x * 2 - clamped_x - w; @@ -2746,7 +3080,7 @@ class PeakmeterBar { if (this.rightLevel < this.db[i + 1]) { this.outerRight_w = i * this.offset + this.offset / Math.abs(this.db[i + 1] - this.db[i]) * Math.abs(this.rightLevel - this.db[i]) - this.x; } - if (pref.peakmeterBarOuterBars) { + if (grSet.peakmeterBarOuterBars) { const xLeft = Clamp(this.x + this.w * 0.5 - this.outerRight_w, this.x, this.w * 0.5); const xRight = this.x + this.w * 0.5; const w = Clamp(this.outerRight_w, 0, this.w * 0.5); @@ -2755,7 +3089,7 @@ class PeakmeterBar { } // * Outer right peaks - if (pref.peakmeterBarOuterPeaks) { + if (grSet.peakmeterBarOuterPeaks) { const clamped_x = Clamp(this.x + this.outerRightAnim_x, this.x, this.x + this.w * 0.5 - this.outerRightAnim_w); // Don't extend right edge of bar const w = this.outerRightAnim_w <= 0 ? 2 : this.outerRightAnim_w; const xLeftPeaks = this.w * 0.5 + this.x * 2 - clamped_x - w; @@ -2766,7 +3100,7 @@ class PeakmeterBar { } // * OUTER OVER BARS * // - if (pref.peakmeterBarOverBars) { + if (grSet.peakmeterBarOverBars) { const overLeft = this.outerLeftAnim_x + this.outerLeftAnim_w - this.w * 0.5; const overRight = this.outerRightAnim_x + this.outerRightAnim_w - this.w * 0.5; @@ -2796,7 +3130,7 @@ class PeakmeterBar { /** * Draws the peakmeter bar in vertical design. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ drawPeakmeterBarVertical(gr) { for (let i = 0; i < this.points_vert; i++) { @@ -2808,12 +3142,12 @@ class PeakmeterBar { const x = this.vertLeft_x + this.vertBar_offset * i; const y = this.y + this.leftPeaks_s[i] - this.vertBar_h; const h = this.leftPeaks_s[i] <= this.vertBar_h ? this.h : this.h - this.leftPeaks_s[i]; - gr.FillSolidRect(x, y, this.vertBar_w, h, col.peakmeterBarVertFill); + gr.FillSolidRect(x, y, this.vertBar_w, h, grCol.peakmeterBarVertFill); } - if (pref.peakmeterBarVertPeaks && this.leftPeaks_s[i] >= 0) { + if (grSet.peakmeterBarVertPeaks && this.leftPeaks_s[i] >= 0) { const x = this.vertLeft_x + this.vertBar_offset * i; const y = this.y + this.leftPeaks_s[i] - this.vertBar_h; - gr.FillSolidRect(x, y, this.vertBar_w, this.vertBar_h, col.peakmeterBarVertFillPeaks); + gr.FillSolidRect(x, y, this.vertBar_w, this.vertBar_h, grCol.peakmeterBarVertFillPeaks); } // * Right Peaks @@ -2824,29 +3158,26 @@ class PeakmeterBar { const x = this.vertRight_x + this.vertBar_offset * i; const y = this.y + this.rightPeaks_s[i] - this.vertBar_h; const h = this.rightPeaks_s[i] <= this.vertBar_h ? this.h : this.h - this.rightPeaks_s[i]; - gr.FillSolidRect(x, y, this.vertBar_w, h, col.peakmeterBarVertFill); + gr.FillSolidRect(x, y, this.vertBar_w, h, grCol.peakmeterBarVertFill); } - if (pref.peakmeterBarVertPeaks && this.rightPeaks_s[i] >= 0) { + if (grSet.peakmeterBarVertPeaks && this.rightPeaks_s[i] >= 0) { const x = this.vertRight_x + this.vertBar_offset * i; const y = this.y + this.rightPeaks_s[i] - this.vertBar_h; - gr.FillSolidRect(x, y, this.vertBar_w, this.vertBar_h, col.peakmeterBarVertFillPeaks); + gr.FillSolidRect(x, y, this.vertBar_w, this.vertBar_h, grCol.peakmeterBarVertFillPeaks); } } // * Progress Bar - if (pref.peakmeterBarProgBar && fb.PlaybackLength) { - let progressStationary = false; + if (grSet.peakmeterBarProgBar && fb.PlaybackLength) { if (this.progressMoved || Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)) > this.progressLength) { this.progressLength = Math.floor(this.w * (fb.PlaybackTime / fb.PlaybackLength)); - } else { - progressStationary = true; } this.progressMoved = false; - gr.FillSolidRect(this.x, this.y + this.h - this.vertBar_h, this.w, Math.round(this.bar_h), col.peakmeterBarProg); - gr.FillSolidRect(this.x, this.y + this.h - this.vertBar_h, this.progressLength, Math.round(this.bar_h), col.peakmeterBarVertProgFill); + gr.FillSolidRect(this.x, this.y + this.h - this.vertBar_h, this.w, Math.round(this.bar_h), grCol.peakmeterBarProg); + gr.FillSolidRect(this.x, this.y + this.h - this.vertBar_h, this.progressLength, Math.round(this.bar_h), grCol.peakmeterBarVertProgFill); } - else if (pref.peakmeterBarVertBaseline) { - gr.FillSolidRect(this.x, this.y + this.h - this.vertBar_h, this.w, this.vertBar_h, col.peakmeterBarProg); + else if (grSet.peakmeterBarVertBaseline) { + gr.FillSolidRect(this.x, this.y + this.h - this.vertBar_h, this.w, this.vertBar_h, grCol.peakmeterBarProg); } this.setAnimation(); @@ -2854,11 +3185,11 @@ class PeakmeterBar { /** * Draws the peakmeter bar info. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ drawPeakmeterBarInfo(gr) { - const infoTextColor = col.lowerBarArtist; - if (pref.peakmeterBarDesign === 'horizontal') { + const infoTextColor = grCol.lowerBarArtist; + if (grSet.peakmeterBarDesign === 'horizontal') { for (let i = 0; i <= this.points; i = i + 2) { const text_w = gr.CalcTextWidth(this.db[i], this.textFont); if (i > 2) { @@ -2868,7 +3199,7 @@ class PeakmeterBar { const text_w = gr.CalcTextWidth('db', this.textFont); gr.GdiDrawText('db', this.textFont, infoTextColor, this.x + this.offset * 2 - text_w, this.text_y, this.w, this.h); } - else if (pref.peakmeterBarDesign === 'horizontal_center') { + else if (grSet.peakmeterBarDesign === 'horizontal_center') { for (let i = 0; i <= this.points; i = i + 2) { const textRight_w = gr.CalcTextWidth(this.db[i], this.textFont); const textLeft_w2 = gr.CalcTextWidth(`${this.db[this.points + 3 - i]}-`, this.textFont); @@ -2880,143 +3211,25 @@ class PeakmeterBar { const text_w = gr.CalcTextWidth('db', this.textFont); gr.GdiDrawText('db', this.textFont, infoTextColor, this.w * 0.5 + this.offset * 2 - text_w * 0.5, this.text_y, this.w, this.h); } - else if (pref.peakmeterBarDesign === 'vertical') { + else if (grSet.peakmeterBarDesign === 'vertical') { for (let i = 0; i <= this.points_vert; i++) { const textWidthLeft = gr.CalcTextWidth(`${this.db_vert[i]}--`, this.textFont); const textWidthRight = gr.CalcTextWidth(`${this.db_vert[this.points_vert - 1 - i]}--`, this.textFont); const textLeft_x = this.vertLeft_x + this.vertBar_offset * i - textWidthLeft / 2 + (this.vertBar_offset - this.vertBar_w); const textRight_x = this.vertRight_x + this.vertBar_offset * i - textWidthRight / 2 + (this.vertBar_offset - this.vertBar_w); - gr.GdiDrawText(this.db_vert[i] % 2 === 0 ? this.db_vert[i] : '', this.textFont, infoTextColor, textLeft_x, this.y, ww, wh); - gr.GdiDrawText(this.db_vert[this.points_vert - 1 - i] % 2 === 0 ? this.db_vert[this.points_vert - 1 - i] : '', this.textFont, infoTextColor, textRight_x, this.y, ww, wh); + gr.GdiDrawText(this.db_vert[i] % 2 === 0 ? this.db_vert[i] : '', this.textFont, infoTextColor, textLeft_x, this.y, grm.ui.ww, grm.ui.wh); + gr.GdiDrawText(this.db_vert[this.points_vert - 1 - i] % 2 === 0 ? this.db_vert[this.points_vert - 1 - i] : '', this.textFont, infoTextColor, textRight_x, this.y, grm.ui.ww, grm.ui.wh); } } } /** - * Draws the peakmeter bar in various peakmeter bar designs. - * @param {GdiGraphics} gr - */ - draw(gr) { - if (pref.peakmeterBarDesign === 'horizontal') { - this.drawPeakmeterBarHorizontal(gr); - } - else if (pref.peakmeterBarDesign === 'horizontal_center') { - this.drawPeakmeterBarCenter(gr); - } - else if (pref.peakmeterBarDesign === 'vertical') { - this.drawPeakmeterBarVertical(gr); - } - if (pref.peakmeterBarInfo) { - this.drawPeakmeterBarInfo(gr); - } - } - - // * CALLBACKS * // - - /** - * Checks if the mouse is within the boundaries of the peakmeter bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - mouseInThis(x, y) { - return (x >= this.x && y >= this.y && x < this.x + this.w && y <= this.y + this.h); - } - - /** - * Handles left mouse button down click events and enables dragging. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_down(x, y) { - this.drag = true; - } - - /** - * Handles left mouse button up click events and disables dragging and updates the playback time. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_up(x, y) { - this.drag = false; - if (this.on_mouse && this.mouseInThis(x, y)) { - this.setPlaybackTime(x); - } - } - - /** - * Handle mouse leave events. - */ - on_mouse_leave() { - this.drag = false; - this.on_mouse = false; - } - - /** - * Handles mouse movement events and updates the playback time based on the mouse movement if a drag event is occurring. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - on_mouse_move(x, y) { - this.on_mouse = true; - this.pos_x = x <= this.textWidth ? this.x + this.textWidth : this.x + x; - this.pos_y = y <= this.textHeight ? this.textHeight : y; - if (this.drag) { - this.setPlaybackTime(x); - } - } - - /** - * Handles mouse wheel events and controls the volume offset. - * @param {number} step The wheel scroll direction. - * @returns {boolean} True or false. - */ - on_mouse_wheel(step) { - this.wheel = true; - if (componentVUMeter) { - this.VUMeter.Offset = this.VUMeter.Offset + step; - this.tooltipText = `${Math.round(this.VUMeter.Offset)} db`; - tt.showImmediate(this.tooltipText); - } - if (this.tooltipTimer) { - clearTimeout(this.tooltipTimer); - } - this.tooltipTimer = setTimeout(() => { - this.wheel = false; - if (this.tooltipTimer) clearTimeout(this.tooltipTimer); - this.tooltipTimer = false; - this.tooltipText = ''; - tt.stop(); - }, 2000); - } - - /** - * Updates peakmeter bar colors when playing a new track. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @returns {boolean} True or false. - */ - on_playback_new_track(metadb) { - if (!metadb) return; - this.setColors(metadb); - } - - /** - * Sets the size and position of the peakmeter bar and updates them on window resizing. + * Sets all vertical peakmeter bar positions. + * Bars are ordered from top to bottom. + * @param {number} y - The y-coordinate. */ - on_size(w, h) { - this.x = SCALE(pref.layout !== 'default' ? 20 : 40); - this.y = 0; - this.w = w - SCALE(pref.layout !== 'default' ? 40 : 80); - this.h = geo.peakmeterBarHeight; - this.bar_h = pref.layout !== 'default' ? SCALE(2) : SCALE(4); - - this.offset = (pref.peakmeterBarDesign === 'horizontal_center' ? this.w * 0.5 : this.w) / this.points; - this.middleOffset = (pref.peakmeterBarDesign === 'horizontal_center' ? this.w * 0.5 : this.w) / this.points_middle; - this.middle_w = this.middleOffset - (pref.peakmeterBarGaps ? 1 : 0); - this.w2 = this.offset - (pref.peakmeterBarGaps ? 1 : 0); - + setY(y) { + this.y = y; this.overLeft_y = this.y; this.outerLeft_y = this.overLeft_y + this.bar_h * 0.5; this.mainLeft_y = this.outerLeft_y + this.bar_h; @@ -3026,139 +3239,773 @@ class PeakmeterBar { this.outerRight_y = this.mainRight_y + this.bar_h; this.overRight_y = this.outerRight_y + this.bar_h; this.text_y = this.outerRight_y + this.bar_h * 2; - - this.vertBar_offset = ((this.w / this.points_vert) + ((pref.peakmeterBarVertSize === 'min' ? 2 : pref.peakmeterBarVertSize) / this.points_vert * 0.5)) * 0.5; - this.vertBar_w = pref.peakmeterBarVertSize === 'min' ? Math.ceil(this.vertBar_offset * 0.1 * 0.5) : this.vertBar_offset - pref.peakmeterBarVertSize * 0.5; - this.vertBar_h = 2; - this.vertLeft_x = this.x; - this.vertRight_x = this.vertLeft_x + this.vertBar_offset * this.points_vert; - - this.progressMoved = true; - this.textFont = gdi.Font('Segoe UI', RES_4K ? 16 : 9, 1); } -} - -////////////////////// -// * WAVEFORM BAR * // -////////////////////// -/** - * Creates the waveform bar in the lower bar when enabled, quick access via right click on lower bar. - */ -class WaveformBar { /** - * @param {number} ww window.Width - * @param {number} wh window.Height + * Monitors volume levels and peaks and sets horizontal or vertical animations based on peakmeterBarDesign. */ - constructor(ww, wh) { - // * Dependencies - include(`${fb.ProfilePath}georgia-reborn\\externals\\Codepages.js`); - include(`${fb.ProfilePath}georgia-reborn\\externals\\lz-utf8\\lzutf8.js`); // For string compression + setAnimation() { + // * Set and monitor volume level/peaks from VUMeter + this.leftLevel = this.toDecibel(this.VUMeter.LeftLevel); + this.leftPeak = this.toDecibel(this.VUMeter.LeftPeak); + this.rightLevel = this.toDecibel(this.VUMeter.RightLevel); + this.rightPeak = this.toDecibel(this.VUMeter.RightPeak); + + // * Debug stuff + DebugLog('LEFT PEAKS: ', this.leftPeak, ' RIGHT PEAKS: ', this.rightPeak); + DebugLog('LEFT LEVEL: ', this.leftLevel, ' RIGHT LEVEL: ', this.rightLevel, '\n\n'); + + // * Set horizontal animation + if (grSet.peakmeterBarDesign === 'horizontal' || grSet.peakmeterBarDesign === 'horizontal_center') { + // * Main left middle peaks + if (this.mainLeftAnim_x <= this.mainLeft_x) { + this.mainLeftAnim_x = this.mainLeft_x; + this.mainLeftAnim2_x = this.mainLeft_x; + this.mainLeft_k = 0; + this.mainLeft2_k = 0; + }; + + this.mainLeft_k = this.mainLeft_k + 0.3 ** 2; + this.mainLeftAnim_x = this.mainLeftAnim_x - this.mainLeft_k; + this.mainLeft2_k = this.mainLeft2_k + 1.1 ** 2; + this.mainLeftAnim2_x = this.mainLeftAnim2_x + this.mainLeft2_k; + + // * Main right middle peaks + if (this.mainRightAnim_x <= this.mainRight_x) { + this.mainRightAnim_x = this.mainRight_x; + this.mainRightAnim2_x = this.mainRight_x; + this.mainRight_k = 0; + this.mainRight2_k = 0; + } + + this.mainRight_k = this.mainRight_k + 0.3 ** 2; + this.mainRightAnim_x = this.mainRightAnim_x - this.mainRight_k; + this.mainRight2_k = this.mainRight2_k + 1.1 ** 2; + this.mainRightAnim2_x = this.mainRightAnim2_x + this.mainRight2_k; + + // * Outer left peaks + if (this.outerLeftAnim_x <= this.outerLeft_w) { + this.outerLeftAnim_x = this.outerLeft_w; + this.outerLeft_k = 0; + this.outerLeftAnim_w = this.outerLeft_w - this.outerLeft_w_old < 1 ? this.outerLeftAnim_w : this.outerLeft_w - this.outerLeft_w_old + 10; + } else { + this.outerLeft_w_old = this.outerLeft_w; + } + + this.outerLeft_k = this.outerLeft_k + 0.3 ** 2; + this.outerLeftAnim_x = this.outerLeftAnim_x - this.outerLeft_k; + this.outerLeftAnim_w = this.outerLeftAnim_w - this.outerLeft_k * 2; + + // * Outer right peaks + if (this.outerRightAnim_x <= this.outerRight_w) { + this.outerRightAnim_x = this.outerRight_w; + this.outerRight_k = 0; + this.outerRightAnim_w = this.outerRight_w - this.outerRight_w_old < 1 ? this.outerRightAnim_w : this.outerRight_w - this.outerRight_w_old + 10; + } else { + this.outerRight_w_old = this.outerRight_w; + } + + this.outerRight_k = this.outerRight_k + 0.3 ** 2; + this.outerRightAnim_x = this.outerRightAnim_x - this.outerRight_k; + this.outerRightAnim_w = this.outerRightAnim_w - this.outerRight_k * 2; + } + // * Set vertical animation + else if (grSet.peakmeterBarDesign === 'vertical') { + for (let j = 0; j < this.leftPeaks_s.length; j++) this.leftPeaks_s[j] = this.leftPeaks_s[j] < this.h ? this.leftPeaks_s[j] + 2 : this.h; + for (let j = 0; j < this.rightPeaks_s.length; j++) this.rightPeaks_s[j] = this.rightPeaks_s[j] < this.h ? this.rightPeaks_s[j] + 2 : this.h; + } + } + + /** + * Sets the peakmeter bar colors. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + setColors(metadb) { + let img = gdi.CreateImage(1, 1); + const g = img.GetGraphics(); + if (img) img.ReleaseGraphics(g); + if (metadb) img = utils.GetAlbumArtV2(metadb, 0); + + if (metadb && img) { + this.colors = JSON.parse(img.GetColourSchemeJSON(4)); + this.c1 = grCol.peakmeterBarFillMiddle; // this.colors[1].col; + this.c2 = grCol.peakmeterBarFillTop; // this.colors[2].col; + this.c3 = grCol.peakmeterBarFillBack; // this.colors[3].col; + } else { + this.setDefaultColors(); + } + + this.color = []; + this.combinedColor1 = []; + this.combinedColor2 = []; + this.color1 = [this.c2, this.c3]; + this.color2 = [this.c3, this.c1]; + + for (let j = 0; j < this.sep1; j++) this.combinedColor1.push(CombineColors(this.color1[0], this.color1[1], j / this.sep1)); + for (let j = 0; j < this.sep2; j++) this.combinedColor2.push(CombineColors(this.color2[0], this.color2[1], j / this.sep2)); + + this.color = this.combinedColor1.concat(this.combinedColor2); + } + + /** + * Sets the default peakmeter bar colors. + */ + setDefaultColors() { + this.c1 = grCol.peakmeterBarFillMiddle; // RGB(0, 200, 255); + this.c2 = grCol.peakmeterBarFillTop; // RGB(255, 255, 0); + this.c3 = grCol.peakmeterBarFillBack; // RGB(230, 230, 30); + this.color1 = [this.c3, this.c1]; + this.color2 = [this.c2, this.c3]; + } + + /** + * Sets the playback time of the progress bar. + * @param {number} x - The x-coordinate. + * @private + */ + setPlaybackTime(x) { + let v = (x - this.x) / this.w; + v = (v < 0) ? 0 : (v < 1) ? v : 1; + if (fb.PlaybackTime !== v * fb.PlaybackLength) { + fb.PlaybackTime = v * fb.PlaybackLength; + } + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Checks if the mouse is within the boundaries of the peakmeter bar. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + mouseInThis(x, y) { + return (x >= this.x && y >= this.y && x < this.x + this.w && y <= this.y + this.h); + } + + /** + * Handles left mouse button down click events and enables dragging. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + on_mouse_lbtn_down(x, y) { + this.drag = true; + } + + /** + * Handles left mouse button up click events and disables dragging and updates the playback time. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + on_mouse_lbtn_up(x, y) { + this.drag = false; + if (this.on_mouse && this.mouseInThis(x, y)) { + this.setPlaybackTime(x); + } + } + + /** + * Handle mouse leave events. + */ + on_mouse_leave() { + this.drag = false; + this.on_mouse = false; + } + + /** + * Handles mouse movement events and updates the playback time based on the mouse movement if a drag event is occurring. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + on_mouse_move(x, y) { + this.on_mouse = true; + this.pos_x = x <= this.textWidth ? this.x + this.textWidth : this.x + x; + this.pos_y = y <= this.textHeight ? this.textHeight : y; + if (this.drag) { + this.setPlaybackTime(x); + } + } + + /** + * Handles mouse wheel events and controls the volume offset. + * @param {number} step - The wheel scroll direction. + */ + on_mouse_wheel(step) { + this.wheel = true; + if (Component.VUMeter) { + this.VUMeter.Offset = this.VUMeter.Offset + step; + this.tooltipText = `${Math.round(this.VUMeter.Offset)} db`; + grm.ttip.showImmediate(this.tooltipText); + } + if (this.tooltipTimer) { + clearTimeout(this.tooltipTimer); + } + this.tooltipTimer = setTimeout(() => { + this.wheel = false; + if (this.tooltipTimer) clearTimeout(this.tooltipTimer); + this.tooltipTimer = false; + this.tooltipText = ''; + grm.ttip.stop(); + }, 2000); + } + + /** + * Updates peakmeter bar colors when playing a new track. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + on_playback_new_track(metadb) { + if (!metadb) return; + this.setColors(metadb); + } + + /** + * Sets the size and position of the peakmeter bar and updates them on window resizing. + * @param {number} w - The width of the peakmeter bar. + * @param {number} h - The height of the peakmeter bar. + */ + on_size(w, h) { + this.x = SCALE(grSet.layout !== 'default' ? 20 : 40); + this.y = 0; + this.w = w - SCALE(grSet.layout !== 'default' ? 40 : 80); + this.h = grm.ui.peakmeterBarH; + this.bar_h = grSet.layout !== 'default' ? SCALE(2) : SCALE(4); + + this.offset = (grSet.peakmeterBarDesign === 'horizontal_center' ? this.w * 0.5 : this.w) / this.points; + this.middleOffset = (grSet.peakmeterBarDesign === 'horizontal_center' ? this.w * 0.5 : this.w) / this.points_middle; + this.middle_w = this.middleOffset - (grSet.peakmeterBarGaps ? 1 : 0); + this.w2 = this.offset - (grSet.peakmeterBarGaps ? 1 : 0); + + this.overLeft_y = this.y; + this.outerLeft_y = this.overLeft_y + this.bar_h * 0.5; + this.mainLeft_y = this.outerLeft_y + this.bar_h; + this.middleLeft_y = this.mainLeft_y + this.bar_h + SCALE(1); + this.middleRight_y = this.middleLeft_y + this.bar_h * 0.5; + this.mainRight_y = this.middleRight_y + this.bar_h * 0.5 + SCALE(1); + this.outerRight_y = this.mainRight_y + this.bar_h; + this.overRight_y = this.outerRight_y + this.bar_h; + this.text_y = this.outerRight_y + this.bar_h * 2; + + this.vertBar_offset = ((this.w / this.points_vert) + ((grSet.peakmeterBarVertSize === 'min' ? 2 : grSet.peakmeterBarVertSize) / this.points_vert * 0.5)) * 0.5; + this.vertBar_w = grSet.peakmeterBarVertSize === 'min' ? Math.ceil(this.vertBar_offset * 0.1 * 0.5) : this.vertBar_offset - grSet.peakmeterBarVertSize * 0.5; + this.vertBar_h = 2; + this.vertLeft_x = this.x; + this.vertRight_x = this.vertLeft_x + this.vertBar_offset * this.points_vert; + + this.progressMoved = true; + this.textFont = gdi.Font('Segoe UI', RES._4K ? 16 : 9, 1); + } + // #endregion +} + + +////////////////////// +// * WAVEFORM BAR * // +////////////////////// +/** + * A class that creates the waveform bar in the lower bar when enabled. + * Quick access via right click context menu on lower bar. + */ +class WaveformBar { + /** + * Creates the `WaveformBar` instance. + * @param {number} ww - Window.Width. + * @param {number} wh - Window.Height. + */ + constructor(ww, wh) { + // * Dependencies + include(`${fb.ProfilePath}georgia-reborn\\externals\\Codepages.js`); + include(`${fb.ProfilePath}georgia-reborn\\externals\\lz-utf8\\lzutf8.js`); // For string compression include(`${fb.ProfilePath}georgia-reborn\\externals\\lz-string\\lz-string.min.js`); // For string compression - const arch = detectWin64 ? '' : '_32'; - this.matchPattern = '$lower([%ALBUM ARTIST%]\\[%ALBUM%]\\%TRACKNUMBER% - %TITLE%)'; // Used to create folder path + const arch = Detect.Win64 ? '' : '_32'; + /** @private @type {string} Used to create folder path */ + this.matchPattern = '$lower([%ALBUM ARTIST%]\\[%ALBUM%]\\%TRACKNUMBER% - %TITLE%)'; + /** @private @type {boolean} */ this.debug = false; + /** @private @type {boolean} */ this.profile = false; + + /** + * The waveform bar analysis settings. + * @typedef {object} waveformBarAnalysis + * @property {string} binaryMode - Settings: ffprobe | audiowaveform | visualizer. + * @property {number} resolution - Pixels per second on audiowaveform, per sample on ffmpeg (higher values than 1 require resampling). Visualizer mode is adjusted via window width. + * @property {string} compressionMode - Settings: none | 'utf-8' (~50% compression) | 'utf-16' (~70% compression) 7zip (~80% compression). + * @property {boolean} autoAnalysis - Auto-analyze files. + * @property {boolean} autoDelete - Auto-deletes analysis files when unloading the script, present during play session to prevent recalculation. + * @property {boolean} visualizerFallback - Uses visualizer mode when file can not be processed (incompatible format). + * @property {boolean} visualizerFallbackAnalysis - Uses visualizer mode when analyzing file. + * @public + */ + /** @public @type {waveformBarAnalysis} */ this.analysis = { - binaryMode: pref.waveformBarMode, // ffprobe | audiowaveform | visualizer - resolution: 1, // Pixels per second on audiowaveform, per sample on ffmpeg (higher values than 1 require resampling). Visualizer mode is adjusted via window width. - compressionMode: 'utf-16', // none | utf-8 (~50% compression) | utf-16 (~70% compression) 7zip (~80% compression). + binaryMode: grSet.waveformBarMode, + resolution: 1, + compressionMode: 'utf-16', autoAnalysis: true, - autoDelete: pref.waveformBarAutoDelete, // Auto-deletes analysis files when unloading the script, present during play session to prevent recalculation. - visualizerFallback: true, // Uses visualizer mode when file can not be processed (incompatible format). - visualizerFallbackAnalysis: true // Uses visualizer mode when analyzing file + autoDelete: grSet.waveformBarAutoDelete, + visualizerFallback: true, + visualizerFallbackAnalysis: true }; + + /** + * The waveform bar binary settings. + * @typedef {object} waveformBarBinaries + * @property {string} ffprobe - The ffprobe binary to use. + * @property {string} audiowaveform - The audiowaveform binary to use. + * @property {string} visualizer - The visualizer binary to use. + * @public + */ + /** @public @type {waveformBarBinaries} */ this.binaries = { ffprobe: `${fb.ProfilePath}georgia-reborn\\externals\\ffprobe\\ffprobe.exe`, audiowaveform: `${fb.ProfilePath}georgia-reborn\\externals\\audiowaveform\\audiowaveform${arch}.exe`, visualizer: `${fb.ProfilePath}running` }; + + /** + * The waveform bar preset settings. + * @typedef {object} waveformBarPreset + * @property {string} analysisMode - The waveform bar analysis mode `rms_level`, `peak_level`, `rms_peak (only available when using ffprobe)`. + * @property {string} barDesign - The waveform bar design `waveform`, `bars`, `dots`, `halfbars`. + * @property {string} paintMode - The waveform bar paint mode `full`, `partial`. + * @property {boolean} animate - Whether to display animation or not. + * @property {boolean} useBPM - Whether to use synced BPM or not. + * @property {boolean} indicator - Whether to show waveform bar progress indicator or not. + * @property {boolean} prepaint - Whether to prepaint waveform bar progress or not. + * @property {number} prepaintFront - The prepaint waveform bar progress length. + * @property {boolean} invertHalfbars - Whether to invert waveform bar halfbars or not. + * @public + */ + /** @public @type {waveformBarPreset} */ this.preset = { - analysisMode: pref.waveformBarAnalysis, // rms_level | peak_level | rms_peak (only available when using ffprobe) - barDesign: pref.waveformBarDesign, // waveform | bars | dots | halfbars - paintMode: pref.waveformBarPaint, // full | partial - animate: pref.waveformBarAnimate, - useBPM: pref.waveformBarBPM, - indicator: pref.waveformBarIndicator, - prepaint: pref.waveformBarPrepaint, - prepaintFront: pref.waveformBarPrepaintFront, - invertHalfbars: pref.waveformBarInvertHalfbars + analysisMode: grSet.waveformBarAnalysis, + barDesign: grSet.waveformBarDesign, + paintMode: grSet.waveformBarPaint, + animate: grSet.waveformBarAnimate, + useBPM: grSet.waveformBarBPM, + indicator: grSet.waveformBarIndicator, + prepaint: grSet.waveformBarPrepaint, + prepaintFront: grSet.waveformBarPrepaintFront, + invertHalfbars: grSet.waveformBarInvertHalfbars }; + + /** + * The waveform bar ui settings. + * @typedef {object} waveformBarUI + * @property {number} sizeWave - The width size of drawn waveform. + * @property {number} sizeBars - The width size of drawn bars. + * @property {number} sizeDots - The width size of drawn dots. + * @property {number} sizeHalf - The width size of drawn halfbars. + * @property {number} sizeNormalizeWidth - The visualizer binary to use. + * @property {number} refreshRate - The refresh rate in ms when using animations for any type. 100 is smooth enough but the performance hit is high. + * @property {number} refreshRateVar - The refresh rate changes around the selected value to ensure code is running smoothly (for very low refresh rates). + * @public + */ + /** @public @type {waveformBarUI} */ this.ui = { - sizeWave: pref.waveformBarSizeWave, // Width size of drawn waveform - sizeBars: pref.waveformBarSizeBars, // Width size of drawn bars - sizeDots: pref.waveformBarSizeDots, // Width size of drawn dots - sizeHalf: pref.waveformBarSizeHalf, // Width size of drawn halfbars - sizeNormalizeWidth: pref.waveformBarSizeNormalize, - refreshRate: pref.waveformBarRefreshRate, // ms when using animations for any type. 100 is smooth enough but the performance hit is high. - refreshRateVar: pref.waveformBarRefreshRateVar // Changes refresh rate around the selected value to ensure code is running smoothly (for very low refresh rates). + sizeWave: grSet.waveformBarSizeWave, + sizeBars: grSet.waveformBarSizeBars, + sizeDots: grSet.waveformBarSizeDots, + sizeHalf: grSet.waveformBarSizeHalf, + sizeNormalizeWidth: grSet.waveformBarSizeNormalize, + refreshRate: grSet.waveformBarRefreshRate, + refreshRateVar: grSet.waveformBarRefreshRateVar }; // * Easy access - this.x = SCALE(pref.layout !== 'default' ? 20 : 40); + /** @public @type {number} */ + this.x = SCALE(grSet.layout !== 'default' ? 20 : 40); + /** @public @type {number} */ this.y = 0; - this.w = ww - SCALE(pref.layout !== 'default' ? 40 : 80); - this.h = geo.waveformBarHeight; + /** @public @type {number} */ + this.w = ww - SCALE(grSet.layout !== 'default' ? 40 : 80); + /** @public @type {number} */ + this.h = grm.ui.waveformBarH; // * Internals + /** @private @type {boolean} */ this.active = true; + /** @private @type {string} */ this.Tf = fb.TitleFormat(this.matchPattern); + /** @private @type {number} */ this.TfMaxStep = fb.TitleFormat('[%BPM%]'); + /** @private @type {string[]} */ this.cache = null; - this.cacheDir = `${fb.ProfilePath}cache\\waveform\\` + /** @private @type {string} */ + this.cacheDir = `${fb.ProfilePath}cache\\waveform\\`; + /** @private @type {string} */ this.codePage = convertCharsetToCodepage('UTF-8'); + /** @private @type {string} */ this.codePageV2 = convertCharsetToCodepage('UTF-16LE'); + /** @private @type {number} */ this.queueId = null; + /** @private @type {number} */ this.queueMs = 1000; + /** @private @type {string[]} */ this.current = []; + /** @private @type {number[]} */ this.offset = []; + /** @private @type {number} */ this.step = 0; // 0 - maxStep + /** @private @type {number} */ this.maxStep = 4; + /** @private @type {number} */ this.time = 0; + /** @private @type {number} */ this.ui.refreshRateOpt = this.ui.refreshRate; + /** @private @type {boolean} */ this.mouseDown = false; - this.isAllowedFile = true; // Set at checkAllowedFile() - this.isError = false; // Set at verifyData() after retrying analysis - this.isFallback = false; // For visualizerFallback, set at checkAllowedFile() - this.fallbackMode = { // For visualizerFallbackAnalysis + /** @private @type {boolean} Set at checkAllowedFile(). */ + this.isAllowedFile = true; + /** @private @type {boolean} Set at verifyData() after retrying analysis. */ + this.isError = false; + /** @private @type {boolean} For visualizerFallback, set at checkAllowedFile(). */ + this.isFallback = false; + + /** + * The waveform bar fallback mode settings for visualizerFallbackAnalysis. + * @typedef {object} waveformBarFallbackMode + * @property {boolean} paint - Indicates whether to use the paint fallback mode. + * @property {boolean} analysis - Indicates whether to use the analysis fallback mode. + * @public + */ + /** @private @type {waveformBarFallbackMode} */ + this.fallbackMode = { paint: false, analysis: false }; + + /** + * The waveform bar ffprobeMode settings. + * @typedef {object} waveformBarFFProbeMode + * @property {object} rms_level - The settings for RMS level mode. + * @property {string} rms_level.key - The key for the RMS level mode. + * @property {number} rms_level.pos - The position index for RMS level mode. + * @property {object} rms_peak - The settings for RMS peak mode. + * @property {string} rms_peak.key - The key for the RMS peak mode. + * @property {number} rms_peak.pos - The position index for RMS peak mode. + * @property {object} peak_level - The settings for peak level mode. + * @property {string} peak_level.key - The key for peak level mode. + * @property {number} peak_level.pos - The position index for peak level mode. + * @public + */ + /** @private @type {waveformBarFFProbeMode} */ this.ffprobeMode = { rms_level: { key: 'rms', pos: 1 }, rms_peak: { key: 'rmsPeak', pos: 2 }, peak_level: { key: 'peak', pos: 3 } }; + + /** + * The waveform bar compatible file settings. + * @typedef {object} waveformBarCompatibility + * @property {RegExp} ffprobe - A regular expression to test for file types compatible with ffprobe. + * @property {RegExp} audiowaveform - A regular expression to test for file types compatible with audiowaveform. + * @public + */ + /** @private @type {waveformBarCompatibility} */ this.compatibleFiles = { ffprobe: new RegExp('\\.(' + ['mp3', 'flac', 'wav', 'ogg', 'opus', 'aac', 'ac3', 'aiff', 'ape', 'wv', 'wma', 'spx', 'spc', 'snd', 'ogx', 'mp4', 'au', 'aac', '2sf', 'dff', 'shn', 'tak', 'tta', 'vgm', 'minincsf', 'la', 'hmi'] .join('|') + ')$', 'i'), audiowaveform: new RegExp('\\.(' + ['mp3', 'flac', 'wav', 'ogg', 'opus'] - .join('|') + ')$', 'i') - } + .join('|') + ')$', 'i') + }; + /** @private @type {FbProfiler} */ this.profilerPaint = new FbProfiler('paint'); - // * Check - this.checkConfig(); - this.defaultSteps(); - if (!IsFolder(this.cacheDir)) { _CreateFolder(this.cacheDir); } - + // * Helpers + /** + * Throttles the window repaint to improve performance by limiting the rate of repaint operations. + * This function is specifically tailored to repaint a defined rectangular area of the window. + * The repaint is controlled by the UI refresh rate. + * @param {boolean} [force=false] - If set to true, the repaint will be forced even if the window is not dirty. + * @private + */ this.throttlePaint = _Throttle((force = false) => window.RepaintRect( this.x - SCALE(2), this.y - this.h * 0.5 - SCALE(4), this.w + SCALE(4), this.h + SCALE(8), force), this.ui.refreshRate); + + /** + * Throttles the window repaint to improve performance by limiting the rate of repaint operations. + * This function allows for the specification of the rectangular area to be repainted. + * The repaint is controlled by the UI refresh rate. + * @param {number} x - The x-coordinate of the upper-left corner of the rectangle to repaint. + * @param {number} y - The y-coordinate of the upper-left corner of the rectangle to repaint. + * @param {number} w - The width of the rectangle to repaint. + * @param {number} h - The height of the rectangle to repaint. + * @param {boolean} [force=false] - If set to true, the repaint will be forced even if the window is not dirty. + * @private + */ this.throttlePaintRect = _Throttle((x, y, w, h, force = false) => window.RepaintRect( x - SCALE(2), y - h * 0.5 - SCALE(4), w + SCALE(4), h + SCALE(8), force), this.ui.refreshRate); + + // * Check + this.checkConfig(); + this.defaultSteps(); + if (!IsFolder(this.cacheDir)) { _CreateFolder(this.cacheDir); } } - // * METHODS * // + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the waveform bar with various waveform bar styles. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + draw(gr) { + this.profilerPaint.Reset(); + if (!fb.IsPlaying) { this.reset(); } // In case paint has been delayed after playback has stopped... + const frames = this.current.length; + const prepaint = this.preset.paintMode === 'partial' && this.preset.prepaint; + const visualizer = this.analysis.binaryMode === 'visualizer' || this.isFallback || this.fallbackMode.paint; + const currX = this.x + this.w * ((fb.PlaybackTime / fb.PlaybackLength) || 0); + const currPosColor = grCol.waveformBarIndicator; + + if (frames !== 0) { + const barW = this.w / frames; + const minPointDiff = 0.5; // in px + const timeConstant = fb.PlaybackLength / frames; + const past = [{ x: 0, y: 1 }, { x: 0, y: -1 }]; + let current; + let n = 0; + + gr.SetSmoothingMode(SmoothingMode.AntiAlias); + + for (const frame of this.current) { // [peak] + current = timeConstant * n; + const isPrepaint = current > this.time; + const isPrepaintAllowed = (current - this.time) < this.preset.prepaintFront; + const scale = frame; + const x = this.x + barW * n; + + if (this.preset.paintMode === 'partial' && !prepaint && isPrepaint) { break; } + else if (prepaint && isPrepaint && !isPrepaintAllowed) { break; } + if (!this.offset[n]) { this.offset.push(0); } + + // if (isPrepaint && prepaint && !paintedBg) { // ! WHY DO I NEED THIS WHEN PREPAINT IS ACTIVE? + // gr.FillSolidRect(currX, this.y, this.w, this.h, grCol.bg); + // paintedBg = true; + // } + + // Ensure points don't overlap too much without normalization + if (past.every((p) => (p.y !== Math.sign(scale) && this.preset.barDesign !== 'halfbars') || (p.y === Math.sign(scale) || this.preset.barDesign === 'halfbars') && (x - p.x) >= minPointDiff)) { + if (this.preset.barDesign === 'waveform') { + const sizeWave = this.ui.sizeWave; + const scaledSize = this.h / 2 * scale; + this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? -Math.sign(scale) * Math.random() * scaledSize / 10 * this.step / this.maxStep : 0); // Add movement when pre-painting + const rand = Math.sign(scale) * this.offset[n]; + const y = scaledSize > 0 ? Math.max(scaledSize + rand, 1) : Math.min(scaledSize + rand, -1); + const colorBack = prepaint && isPrepaint ? ShadeColor(grCol.waveformBarFillBack, 40) : grCol.waveformBarFillBack; // Back + const colorFront = prepaint && isPrepaint ? ShadeColor(grCol.waveformBarFillFront, 20) : grCol.waveformBarFillFront; // Front + let z = visualizer ? Math.abs(y) : y; + + if (z > 0) { // * Top + if (colorFront !== colorBack) { + gr.FillSolidRect(x, this.y - z, sizeWave, z / 2, colorBack); + gr.FillSolidRect(x, this.y - z / 2, sizeWave, z / 2, colorFront); + } else { + gr.FillSolidRect(x, this.y - z, sizeWave, z, colorBack); + } + } + z = visualizer ? -Math.abs(y) : y; + if (z < 0) { // * Bottom + if (colorFront !== colorBack) { + gr.FillSolidRect(x, this.y - z / 2, sizeWave, -z / 2, colorBack); + gr.FillSolidRect(x, this.y, sizeWave, -z / 2, colorFront); + } else { + gr.FillSolidRect(x, this.y, sizeWave, -z, colorBack); + } + } + } + else if (this.preset.barDesign === 'halfbars') { + const sizeHalf = grSet.waveformBarMode !== 'visualizer' ? this.ui.sizeHalf : barW * this.ui.sizeHalf * (visualizer ? 0.2 : 0.5); + const scaledSize = this.h / 2 * scale; + this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? -Math.sign(scale) * Math.random() * scaledSize / 10 * this.step / this.maxStep : 0); // Add movement when pre-painting + const rand = Math.sign(scale) * this.offset[n]; + let y = scaledSize > 0 ? Math.max(scaledSize + rand, 1) : Math.min(scaledSize + rand, -1); + if (this.preset.invertHalfbars) y = Math.abs(y); + let colorBack = prepaint && isPrepaint ? grCol.waveformBarFillPreBack : grCol.waveformBarFillBack; // Back + let colorFront = prepaint && isPrepaint ? grCol.waveformBarFillPreFront : grCol.waveformBarFillFront; // Front + const x = this.x + barW * n; + + // * Current position + if ((this.preset.indicator || this.mouseDown) && this.analysis.binaryMode !== 'ffprobe' && (x <= currX && x >= currX - 2 * barW)) { + colorBack = colorFront = currPosColor; + } + if (y > 0) { + if (colorFront !== colorBack) { + gr.FillSolidRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, y, colorBack); + gr.FillSolidRect(x, this.y - y + this.h * 0.5, sizeHalf, y, colorFront); + } else { + gr.FillSolidRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, 2 * y, colorBack); + } + // if (colorFront !== colorBack) { // ! Does not look good with DrawRect + // gr.DrawRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, y, 1, colorBack); + // gr.DrawRect(x, this.y - y + this.h * 0.5, sizeHalf, y, 1, colorFront); + // } else { + // gr.DrawRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, 2 * y, 1, colorBack); + // } + } + } + else if (this.preset.barDesign === 'bars') { + const sizeBars = barW * this.ui.sizeBars; + const scaledSize = this.h / 2 * scale; + this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? -Math.sign(scale) * Math.random() * scaledSize / 10 * this.step / this.maxStep : 0); // Add movement when pre-painting + const rand = Math.sign(scale) * this.offset[n]; + const y = scaledSize > 0 ? Math.max(scaledSize + rand, 1) : Math.min(scaledSize + rand, -1); + let colorBack = prepaint && isPrepaint ? ShadeColor(grCol.waveformBarFillBack, 40) : grCol.waveformBarFillBack; // Back + let colorFront = prepaint && isPrepaint ? ShadeColor(grCol.waveformBarFillFront, 20) : grCol.waveformBarFillFront; // Front + const x = this.x + barW * n; + + // * Current position + if ((this.preset.indicator || this.mouseDown) && this.analysis.binaryMode !== 'ffprobe' && (x <= currX && x >= currX - 2 * barW)) { + colorBack = colorFront = currPosColor; + } + let z = visualizer ? Math.abs(y) : y; + if (z > 0) { // * Top + if (colorFront !== colorBack) { + gr.DrawRect(x, this.y - z, sizeBars, z / 2, 1, colorBack); + gr.DrawRect(x, this.y - z / 2, sizeBars, z / 2, 1, colorFront); + } else { + gr.DrawRect(x, this.y - z, sizeBars, z, 1, colorBack); + } + } + z = visualizer ? -Math.abs(y) : y; + if (z < 0) { // * Bottom + if (colorFront !== colorBack) { + gr.DrawRect(x, this.y - z / 2, sizeBars, -z / 2, 1, colorBack); + gr.DrawRect(x, this.y, sizeBars, -z / 2, 1, colorFront); + } else { + gr.DrawRect(x, this.y, sizeBars, -z, 1, colorBack); + } + } + } + else if (this.preset.barDesign === 'dots') { + const scaledSize = this.h / 2 * scale; + const y = scaledSize > 0 ? Math.max(scaledSize, 1) : Math.min(scaledSize, -1); + const colorBack = prepaint && isPrepaint ? ShadeColor(grCol.waveformBarFillBack, 40) : grCol.waveformBarFillBack; // Back + const colorFront = prepaint && isPrepaint ? ShadeColor(grCol.waveformBarFillFront, 20) : grCol.waveformBarFillFront; // Front + this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? Math.random() * Math.abs(this.step / this.maxStep) : 0); // Add movement when pre-painting + const rand = this.offset[n]; + const step = Math.max(this.h / 80, 5) + (rand || 1) // Point density + const circleSize = Math.max(step / 25, 1) * this.ui.sizeDots; + + // Split waveform in two, and then half each for highlighting. If colors match, the same amount of dots are painted anyway. + const sign = Math.sign(y); + let yCalc = this.y; + let bottom = this.y - y / 2; + + while (sign * (yCalc - bottom) > 0) { + gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorFront); + yCalc += (-sign) * step; + } + bottom += -y / 2; + + while (sign * (yCalc - bottom) > 0) { + gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorBack); + yCalc += (-sign) * step; + } + + if (visualizer) { + const sign = -Math.sign(y); + let yCalc = this.y; + let bottom = this.y + y / 2; + while (sign * (yCalc - bottom) > 0) { + gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorFront); + yCalc += (-sign) * step; + } + bottom += +y / 2; + + while (sign * (yCalc - bottom) > 0) { + gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorBack); + yCalc += (-sign) * step; + } + } + } + past.shift(); + past.push({ x, y: Math.sign(scale) }); + } + n++; + } + + // * Progress line + if (this.preset.indicator || this.mouseDown) { + gr.SetSmoothingMode(0); + const minBarW = Math.round(Math.max(barW, SCALE(1))); + if (this.analysis.binaryMode === 'ffprobe') { + gr.DrawLine(currX, this.y - this.h * 0.5, currX, this.y + this.h * 0.5, minBarW, currPosColor); + } else if (this.preset.barDesign === 'waveform' || this.preset.barDesign === 'dots') { + gr.DrawLine(currX, this.y - this.h * 0.5, currX, this.y + this.h * 0.5, minBarW, currPosColor); + } + } + } + else if (fb.IsPlaying) { + const DT_CENTER = DrawText.VCenter | DrawText.Center | DrawText.EndEllipsis | DrawText.CalcRect | DrawText.NoPrefix; + const updatedNowpBg = pl.col.row_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing + const bgColor = grSet.theme === 'reborn' ? pl.col.row_nowplaying_bg : grCol.transportEllipseBg; + const textColor = pl.col.header_artist_normal; + + if (updatedNowpBg) { + gr.FillSolidRect(this.x, this.y - this.h * 0.5, this.w, this.h, bgColor); // * Waveform bar background + if (!this.isAllowedFile && !this.isFallback && this.analysis.binaryMode !== 'visualizer') { + gr.GdiDrawText('Incompatible file format', grFont.lowerBarWave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); + } else if (!this.analysis.autoAnalysis) { + gr.GdiDrawText('Waveform bar file not found', grFont.lowerBarWave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); + } else if (this.isError) { + gr.GdiDrawText('Waveform bar file can not be analyzed', grFont.lowerBarWave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); + } else if (this.active) { + gr.GdiDrawText('Loading', grFont.lowerBarWave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); + } + } + } + + // * Incrementally draw animation on small steps + if (prepaint && this.preset.animate || visualizer) { + if (this.step >= this.maxStep) { + this.step = -this.step; + } else { + if (this.step === 0) { this.offset = []; } + this.step++; + } + } + // * Animate smoothly, repaint by zone when possible. Only when not paused! + if (fb.IsPlaying && !fb.IsPaused) { + if (visualizer) { + this.throttlePaint(); + } + else if ((this.preset.paintMode === 'partial' || this.preset.indicator) && frames) { + const widerModesScale = (this.preset.waveMode === 'bars' || this.preset.waveMode === 'halfbars' ? 2 : 1); + const barW = Math.ceil(Math.max(this.w / frames, SCALE(2))) * widerModesScale; + const timeConstant = fb.PlaybackLength / frames; + const prePaintW = Math.min( + prepaint && this.preset.prepaintFront !== Infinity || this.preset.animate + ? this.preset.prepaintFront === Infinity && this.preset.animate + ? Infinity + : this.preset.prepaintFront / timeConstant * barW + barW + : 2.5 * barW, + this.w - currX + barW + ); + this.throttlePaintRect(currX - barW - SCALE(40), this.y, prePaintW + SCALE(40) * 2, this.h); + } + if (this.ui.refreshRateVar) { + if (this.profilerPaint.Time > this.ui.refreshRate) { + this.updateConfig({ ui: { refreshRate: this.ui.refreshRate + 50 } }); + } + else if (this.profilerPaint.Time < this.ui.refreshRate && this.profilerPaint.Time >= this.ui.refreshRateOpt) { + this.updateConfig({ ui: { refreshRate: this.ui.refreshRate - 25 } }); + } + } + } + } /** * Sets the vertical waveform bar position. - * @param {number} y The y-coordinate. + * @param {number} y - The y-coordinate. */ setY(y) { this.y = y; @@ -3166,15 +4013,16 @@ class WaveformBar { /** * Checks if the current file is allowed to be played, i.e not corrupted. - * @param {Object} handle The current file handle. + * @param {object} handle - The current file handle. */ checkAllowedFile(handle = fb.GetNowPlaying()) { this.isAllowedFile = this.analysis.binaryMode !== 'visualizer' && handle.SubSong === 0 && this.compatibleFiles[this.analysis.binaryMode].test(handle.Path); this.isFallback = !this.isAllowedFile && this.analysis.visualizerFallback; - }; + } /** * Checks the configuration for validity and throws an error if not, called from the constructor. + * @throws {Error} Throws an error if the binary mode is not recognized or path is not set. */ checkConfig() { if (!Object.prototype.hasOwnProperty.call(this.binaries, this.analysis.binaryMode)) { @@ -3186,11 +4034,11 @@ class WaveformBar { if (this.preset.prepaintFront <= 0 || this.preset.prepaintFront === null) { this.preset.prepaintFront = Infinity; } - }; + } /** * Updates the config and ensures the UI is being updated properly after changing settings. - * @param {Object} newConfig + * @param {object} newConfig - The new configuration object with settings to be applied. */ updateConfig(newConfig) { if (newConfig) { @@ -3234,23 +4082,23 @@ class WaveformBar { } else { this.throttlePaint(); } - }; + } /** * Updates the waveform bar with the current track information, playback time and size. - * @param {boolean} current Whether the current track has changed or not. + * @param {boolean} current - Whether the current track has changed or not. */ updateBar(current) { if (!current) this.on_playback_new_track(fb.GetNowPlaying()); this.on_playback_time(fb.PlaybackTime); - this.on_size(ww, wh); + this.on_size(grm.ui.ww, grm.ui.wh); } /** * Gets ffprobe for Windows or Linux. */ getFFprobe() { - const url = detectWin64 ? + const url = Detect.Win64 ? 'https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip' : 'https://github.com/sudo-nautilus/FFmpeg-Builds-Win32/releases/download/latest/ffmpeg-master-latest-win32-gpl.zip'; RunCmd(url); @@ -3259,8 +4107,8 @@ class WaveformBar { /** * Gets the paths to the waveform bar cache folder and file. - * @param {Object} handle The handle of the track. - * @returns {Object} The paths to the waveform bar cache folder and file. + * @param {object} handle - The handle of the track. + * @returns {object} The paths to the waveform bar cache folder and file. */ getPaths(handle) { const id = SanitizePath(this.Tf.EvalWithMetadb(handle)); // Ensures paths are valid! @@ -3268,12 +4116,12 @@ class WaveformBar { const waveformBarFolder = this.cacheDir + id.replace(fileName, ''); const waveformBarFile = this.cacheDir + id; return { waveformBarFolder, waveformBarFile }; - }; + } /** * Sets the max step based on the BPM of the track. - * @param {Object} handle The handle of the track. - * @returns {Number} The max steps. + * @param {object} handle - The handle of the track. + * @returns {number} The max steps. */ bpmSteps(handle = fb.GetNowPlaying()) { // Don't allow anything faster than 2 steps or slower than 10 (scaled to 200 ms refresh rate) and consider setting tracks having 100 BPM as default. @@ -3281,20 +4129,20 @@ class WaveformBar { const BPM = Number(this.TfMaxStep.EvalWithMetadb(handle)); this.maxStep = Math.round(Math.min(Math.max(200 / (BPM || 100) * 2, 2), 10) * (200 / this.ui.refreshRate) ** (1 / 2)); return this.maxStep; - }; + } /** * Sets the max step to a default value. - * @returns {Number} The max steps. + * @returns {number} The max steps. */ defaultSteps() { this.maxStep = Math.round(4 * (200 / this.ui.refreshRate) ** (1 / 2)); return this.maxStep; - }; + } /** * Normalizes points to ensure all points are on the same scale to prevent distortion of the waveform. - * @param {boolean} normalizeWidth + * @param {boolean} normalizeWidth - If `true`, adjusts the number of frames to match the window size. */ normalizePoints(normalizeWidth = false) { if (!this.current.length) return; @@ -3441,13 +4289,13 @@ class WaveformBar { } } } - }; + } /** * Analyzes data of the given handle and saves the results in the waveform bar cache directory. - * @param {FbMetadbHandle} handle The handle to analyze. - * @param {string} waveformBarFolder The folder where the waveform bar data should be saved. - * @param {string} waveformBarFile The name of the waveform bar file. + * @param {FbMetadbHandle} handle - The handle to analyze. + * @param {string} waveformBarFolder - The folder where the waveform bar data should be saved. + * @param {string} waveformBarFile - The name of the waveform bar file. * @returns {Promise} A promise that resolves when the analysis is finished. */ async analyzeData(handle, waveformBarFolder, waveformBarFile) { @@ -3582,13 +4430,13 @@ class WaveformBar { console.log(`${this.analysis.binaryMode}: failed analyzing the file -> ${handle.Path}`); } } - }; + } /** * Generates data for the visualizer. - * @param {FbMetadbHandle} handle The handle to analyze. - * @param {string} preset The preset to use for the visualizer. - * @param {boolean} variableLen Whether the length of the data should be variable. + * @param {FbMetadbHandle} handle - The handle to analyze. + * @param {string} preset - The preset to use for the visualizer. + * @param {boolean} variableLen - Whether the length of the data should be variable. * @returns {Array} The data for the visualizer bar. */ visualizerData(handle, preset = 'classic spectrum analyzer', variableLen = false) { @@ -3598,24 +4446,21 @@ class WaveformBar { const data = []; - switch (preset) { - case 'classic spectrum analyzer': { - const third = Math.round(samples / 3); - const half = Math.round(samples / 2); - for (let i = 0; i < third; i++) { - const val = (Math.random() * i) / third; - data.push(val); - } - for (let i = third; i < half; i++) { - const val = (Math.random() * i) / third; - data.push(val); - } - for (const frame of [...data].reverse()) data.push(frame) - break; + if (preset === 'classic spectrum analyzer') { + const third = Math.round(samples / 3); + const half = Math.round(samples / 2); + for (let i = 0; i < third; i++) { + const val = (Math.random() * i) / third; + data.push(val); } + for (let i = third; i < half; i++) { + const val = (Math.random() * i) / third; + data.push(val); + } + for (const frame of [...data].reverse()) data.push(frame); } return data; - }; + } /** * Checks if the processed data is valid. @@ -3630,19 +4475,19 @@ class WaveformBar { return (len === 4 || len === 5); }) : this.current.every((frame) => (frame >= -128 && frame <= 127)); - }; + } /** * Verifies if the processed data is valid. - * @param {FbMetadbHandle} handle The handle to analyze. - * @param {string} file The file to analyze. - * @param {boolean} isRetry Whether the data is being retried. + * @param {FbMetadbHandle} handle - The handle to analyze. + * @param {string} file - The file to analyze. + * @param {boolean} isRetry - Whether the data is being retried. * @returns {boolean} True if the data is valid. */ verifyData(handle, file, isRetry = false) { if (!this.isDataValid()) { if (isRetry) { - console.log('File was not sucessfully analyzed after retrying.'); + console.log('File was not successfully analyzed after retrying.'); if (file) _DeleteFile(file); this.isAllowedFile = false; this.isFallback = this.analysis.visualizerFallback; @@ -3656,14 +4501,14 @@ class WaveformBar { return false; } return true; - }; + } /** * Deletes the waveform bar cache diretory with its processed data. */ removeData() { DeleteFolder(this.cacheDir); - }; + } /** * Resets the state of the waveform bar. @@ -3681,7 +4526,7 @@ class WaveformBar { this.fallbackMode.paint = this.fallbackMode.analysis = false; this.resetAnimation(); if (this.queueId) clearTimeout(this.queueId); - }; + } /** * Resets the state of the waveform bar animation. @@ -3690,11 +4535,11 @@ class WaveformBar { this.step = 0; this.offset = []; this.defaultSteps(); - }; + } /** * This method is currently not used. - * @param {boolean} enable + * @param {boolean} [enable] - If true, activates the component; if false, deactivates it. */ switch(enable = !this.active) { const wasActive = this.active; @@ -3711,270 +4556,30 @@ class WaveformBar { this.on_playback_stop(-1); } } - }; + } /** - * Draws the waveform bar with various waveform bar styles. - * @param {GdiGraphics} gr + * Checks if the mouse is within the boundaries of the waveform bar. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. */ - draw(gr) { - this.profilerPaint.Reset(); - if (!fb.IsPlaying) { this.reset(); } // In case paint has been delayed after playback has stopped... - const frames = this.current.length; - const prepaint = this.preset.paintMode === 'partial' && this.preset.prepaint; - const visualizer = this.analysis.binaryMode === 'visualizer' || this.isFallback || this.fallbackMode.paint; - const currX = this.x + this.w * ((fb.PlaybackTime / fb.PlaybackLength) || 0); - const currPosColor = col.waveformBarIndicator; - - if (frames !== 0) { - const barW = this.w / frames; - const barBgW = this.w / 100; - const minPointDiff = 0.5; // in px - const timeConstant = fb.PlaybackLength / frames; - const past = [{ x: 0, y: 1 }, { x: 0, y: -1 }]; - let current; - let n = 0; - - gr.SetSmoothingMode(SmoothingMode.AntiAlias); - - for (const frame of this.current) { // [peak] - current = timeConstant * n; - const isPrepaint = current > this.time; - const isPrepaintAllowed = (current - this.time) < this.preset.prepaintFront; - const scale = frame; - const x = this.x + barW * n; - - if (this.preset.paintMode === 'partial' && !prepaint && isPrepaint) { break; } - else if (prepaint && isPrepaint && !isPrepaintAllowed) { break; } - if (!this.offset[n]) { this.offset.push(0); } - - // if (isPrepaint && prepaint && !paintedBg) { // ! WHY DO I NEED THIS WHEN PREPAINT IS ACTIVE? - // gr.FillSolidRect(currX, this.y, this.w, this.h, col.bg); - // paintedBg = true; - // } - - // Ensure points don't overlap too much without normalization - if (past.every((p) => (p.y !== Math.sign(scale) && this.preset.barDesign !== 'halfbars') || (p.y === Math.sign(scale) || this.preset.barDesign === 'halfbars') && (x - p.x) >= minPointDiff)) { - if (this.preset.barDesign === 'waveform') { - const sizeWave = this.ui.sizeWave; - const scaledSize = this.h / 2 * scale; - this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? -Math.sign(scale) * Math.random() * scaledSize / 10 * this.step / this.maxStep : 0); // Add movement when pre-painting - const rand = Math.sign(scale) * this.offset[n]; - const y = scaledSize > 0 ? Math.max(scaledSize + rand, 1) : Math.min(scaledSize + rand, -1); - const colorBack = prepaint && isPrepaint ? ShadeColor(col.waveformBarFillBack, 40) : col.waveformBarFillBack; // Back - const colorFront = prepaint && isPrepaint ? ShadeColor(col.waveformBarFillFront, 20) : col.waveformBarFillFront; // Front - let z = visualizer ? Math.abs(y) : y; - - if (z > 0) { // * Top - if (colorFront !== colorBack) { - gr.FillSolidRect(x, this.y - z, sizeWave, z / 2, colorBack); - gr.FillSolidRect(x, this.y - z / 2, sizeWave, z / 2, colorFront); - } else { - gr.FillSolidRect(x, this.y - z, sizeWave, z, colorBack); - } - } - z = visualizer ? -Math.abs(y) : y; - if (z < 0) { // * Bottom - if (colorFront !== colorBack) { - gr.FillSolidRect(x, this.y - z / 2, sizeWave, -z / 2, colorBack); - gr.FillSolidRect(x, this.y, sizeWave, -z / 2, colorFront); - } else { - gr.FillSolidRect(x, this.y, sizeWave, -z, colorBack); - } - } - } - else if (this.preset.barDesign === 'halfbars') { - const sizeHalf = pref.waveformBarMode !== 'visualizer' ? this.ui.sizeHalf : barW * this.ui.sizeHalf * (visualizer ? 0.2 : 0.5); - const scaledSize = this.h / 2 * scale; - this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? -Math.sign(scale) * Math.random() * scaledSize / 10 * this.step / this.maxStep : 0); // Add movement when pre-painting - const rand = Math.sign(scale) * this.offset[n]; - let y = scaledSize > 0 ? Math.max(scaledSize + rand, 1) : Math.min(scaledSize + rand, -1); - if (this.preset.invertHalfbars) y = Math.abs(y); - let colorBack = prepaint && isPrepaint ? col.waveformBarFillPreBack : col.waveformBarFillBack; // Back - let colorFront = prepaint && isPrepaint ? col.waveformBarFillPreFront : col.waveformBarFillFront; // Front - const x = this.x + barW * n; - - // * Current position - if ((this.preset.indicator || this.mouseDown) && this.analysis.binaryMode !== 'ffprobe' && (x <= currX && x >= currX - 2 * barW)) { - colorBack = colorFront = currPosColor; - } - if (y > 0) { - if (colorFront !== colorBack) { - gr.FillSolidRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, y, colorBack); - gr.FillSolidRect(x, this.y - y + this.h * 0.5, sizeHalf, y, colorFront); - } else { - gr.FillSolidRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, 2 * y, colorBack); - } - // if (colorFront !== colorBack) { // ! Does not look good with DrawRect - // gr.DrawRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, y, 1, colorBack); - // gr.DrawRect(x, this.y - y + this.h * 0.5, sizeHalf, y, 1, colorFront); - // } else { - // gr.DrawRect(x, this.y - 2 * y + this.h * 0.5, sizeHalf, 2 * y, 1, colorBack); - // } - } - } - else if (this.preset.barDesign === 'bars') { - const sizeBars = barW * this.ui.sizeBars; - const scaledSize = this.h / 2 * scale; - this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? -Math.sign(scale) * Math.random() * scaledSize / 10 * this.step / this.maxStep : 0); // Add movement when pre-painting - const rand = Math.sign(scale) * this.offset[n]; - const y = scaledSize > 0 ? Math.max(scaledSize + rand, 1) : Math.min(scaledSize + rand, -1); - let colorBack = prepaint && isPrepaint ? ShadeColor(col.waveformBarFillBack, 40) : col.waveformBarFillBack; // Back - let colorFront = prepaint && isPrepaint ? ShadeColor(col.waveformBarFillFront, 20) : col.waveformBarFillFront; // Front - const x = this.x + barW * n; - - // * Current position - if ((this.preset.indicator || this.mouseDown) && this.analysis.binaryMode !== 'ffprobe' && (x <= currX && x >= currX - 2 * barW)) { - colorBack = colorFront = currPosColor; - } - let z = visualizer ? Math.abs(y) : y; - if (z > 0) { // * Top - if (colorFront !== colorBack) { - gr.DrawRect(x, this.y - z, sizeBars, z / 2, 1, colorBack); - gr.DrawRect(x, this.y - z / 2, sizeBars, z / 2, 1, colorFront); - } else { - gr.DrawRect(x, this.y - z, sizeBars, z, 1, colorBack); - } - } - z = visualizer ? -Math.abs(y) : y; - if (z < 0) { // * Bottom - if (colorFront !== colorBack) { - gr.DrawRect(x, this.y - z / 2, sizeBars, -z / 2, 1, colorBack); - gr.DrawRect(x, this.y, sizeBars, -z / 2, 1, colorFront); - } else { - gr.DrawRect(x, this.y, sizeBars, -z, 1, colorBack); - } - } - } - else if (this.preset.barDesign === 'dots') { - const scaledSize = this.h / 2 * scale; - const y = scaledSize > 0 ? Math.max(scaledSize, 1) : Math.min(scaledSize, -1); - const colorBack = prepaint && isPrepaint ? ShadeColor(col.waveformBarFillBack, 40) : col.waveformBarFillBack; // Back - const colorFront = prepaint && isPrepaint ? ShadeColor(col.waveformBarFillFront, 20) : col.waveformBarFillFront; // Front - this.offset[n] += (prepaint && isPrepaint && this.preset.animate || visualizer ? Math.random() * Math.abs(this.step / this.maxStep) : 0); // Add movement when pre-painting - const rand = this.offset[n]; - const step = Math.max(this.h / 80, 5) + (rand || 1) // Point density - const circleSize = Math.max(step / 25, 1) * this.ui.sizeDots; - - // Split waveform in two, and then half each for highlighting. If colors match, the same amount of dots are painted anyway. - const sign = Math.sign(y); - let yCalc = this.y; - let bottom = this.y - y / 2; - - while (sign * (yCalc - bottom) > 0) { - gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorFront); - yCalc += (-sign) * step; - } - bottom += -y / 2; - - while (sign * (yCalc - bottom) > 0) { - gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorBack); - yCalc += (-sign) * step; - } - - if (visualizer) { - const sign = -Math.sign(y); - let yCalc = this.y; - let bottom = this.y + y / 2; - while (sign * (yCalc - bottom) > 0) { - gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorFront); - yCalc += (-sign) * step; - } - bottom += +y / 2; - - while (sign * (yCalc - bottom) > 0) { - gr.DrawEllipse(x, yCalc, circleSize, circleSize, 1, colorBack); - yCalc += (-sign) * step; - } - } - } - past.shift(); - past.push({ x, y: Math.sign(scale) }); - } - n++; - } - - // * Progress line - if (this.preset.indicator || this.mouseDown) { - gr.SetSmoothingMode(0); - const minBarW = Math.round(Math.max(barW, SCALE(1))); - if (this.analysis.binaryMode === 'ffprobe') { - gr.DrawLine(currX, this.y - this.h * 0.5, currX, this.y + this.h * 0.5, minBarW, currPosColor); - } else if (this.preset.barDesign === 'waveform' || this.preset.barDesign === 'dots') { - gr.DrawLine(currX, this.y - this.h * 0.5, currX, this.y + this.h * 0.5, minBarW, currPosColor); - } - } - } - else if (fb.IsPlaying) { - const DT_CENTER = DrawText.VCenter | DrawText.Center | DrawText.EndEllipsis | DrawText.CalcRect | DrawText.NoPrefix; - const updatedNowpBg = g_pl_colors.row_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing - const bgColor = pref.theme === 'reborn' ? g_pl_colors.row_nowplaying_bg : col.transportEllipseBg; - const textColor = g_pl_colors.header_artist_normal; - - if (updatedNowpBg) { - gr.FillSolidRect(this.x, this.y - this.h * 0.5, this.w, this.h, bgColor); // * Waveform bar background - if (!this.isAllowedFile && !this.isFallback && this.analysis.binaryMode !== 'visualizer') { - gr.GdiDrawText('Incompatible file format', ft.lower_bar_wave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); - } else if (!this.analysis.autoAnalysis) { - gr.GdiDrawText('Waveform bar file not found', ft.lower_bar_wave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); - } else if (this.isError) { - gr.GdiDrawText('Waveform bar file can not be analyzed', ft.lower_bar_wave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); - } else if (this.active) { - gr.GdiDrawText('Loading', ft.lower_bar_wave, textColor, this.x, this.y - this.h * 0.5, this.w, this.h, DT_CENTER); - } - } - } - - // * Incrementally draw animation on small steps - if (prepaint && this.preset.animate || visualizer) { - if (this.step >= this.maxStep) { - this.step = -this.step; - } else { - if (this.step === 0) { this.offset = []; } - this.step++; - } - } - // * Animate smoothly, repaint by zone when possible. Only when not paused! - if (fb.IsPlaying && !fb.IsPaused) { - if (visualizer) { - this.throttlePaint(); - } - else if ((this.preset.paintMode === 'partial' || this.preset.indicator) && frames) { - const widerModesScale = (this.preset.waveMode === 'bars' || this.preset.waveMode === 'halfbars' ? 2 : 1); - const barW = Math.ceil(Math.max(this.w / frames, SCALE(2))) * widerModesScale; - const timeConstant = fb.PlaybackLength / frames; - const prePaintW = Math.min( - prepaint && this.preset.prepaintFront !== Infinity || this.preset.animate - ? this.preset.prepaintFront === Infinity && this.preset.animate - ? Infinity - : this.preset.prepaintFront / timeConstant * barW + barW - : 2.5 * barW, - this.w - currX + barW - ); - this.throttlePaintRect(currX - barW - SCALE(40), this.y, prePaintW + SCALE(40) * 2, this.h); - } - if (this.ui.refreshRateVar) { - if (this.profilerPaint.Time > this.ui.refreshRate) { - this.updateConfig({ ui: { refreshRate: this.ui.refreshRate + 50 } }); - } - else if (this.profilerPaint.Time < this.ui.refreshRate && this.profilerPaint.Time >= this.ui.refreshRateOpt) { - this.updateConfig({ ui: { refreshRate: this.ui.refreshRate - 25 } }); - } - } - } - }; + trace(x, y) { + return (x >= this.x && y >= this.y && x <= this.x + this.w && y <= this.y + this.h); + } + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Handles left mouse button up click events and disables dragging and updates the playback time. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. * @returns {boolean} True or false. */ on_mouse_lbtn_up(x, y, mask) { - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar)) return; + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar)) return false; this.mouseDown = false; if (!this.trace(x, y)) { return false; } const handle = fb.GetSelection(); @@ -3991,27 +4596,28 @@ class WaveformBar { } } return false; - }; + } /** * Handles mouse movement events on the waveform bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. */ on_mouse_move(x, y, mask) { - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar)) return; + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar)) return; if (mask === MK_LBUTTON && this.on_mouse_lbtn_up(x, y, mask)) { this.mouseDown = true; } - }; + } /** * Resets the current waveform and processes new data for the new current playing track. - * @param {FbMetadbHandle} handle The handle of the new track. + * @param {FbMetadbHandle} handle - The handle of the new track. + * @param {boolean} [isRetry] - The flag indicating whether the method call is a retry attempt. */ async on_playback_new_track(handle = fb.GetNowPlaying(), isRetry = false) { - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar) || !this.active) { return; } + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar) || !this.active) { return; } this.reset(); if (handle) { this.checkAllowedFile(handle); @@ -4081,36 +4687,35 @@ class WaveformBar { if (fb.IsPlaying) { this.time = fb.PlaybackTime; } // And paint this.throttlePaint(); - }; + } /** * Queues the `on_playback_new_track` event to be fired after a given delay. * This is useful for debouncing the event, so that it is only fired once after a series of track changes. - * @param {number} ms The delay in milliseconds. */ on_playback_new_track_queue() { if (this.queueId) clearTimeout(this.queueId); this.queueId = setTimeout(() => { this.on_playback_new_track(...arguments) // Arguments points to the first non arrow func }, this.queueMs); - }; + } /** * Resets the waveform bar on playback stop. - * @param {number} reason The type of playback stop. + * @param {number} reason - The type of playback stop. */ on_playback_stop(reason = -1) { // -1 Invoked by JS | 0 Invoked by user | 1 End of file | 2 Starting another track | 3 Fb2k is shutting down - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar) || reason !== -1 && !this.active) { return; } + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar) || reason !== -1 && !this.active) { return; } this.reset(); if (reason !== 2) { this.throttlePaint(); } - }; + } /** * Updates the waveform bar with throttled repaints. - * @param {number} time The current playback time. + * @param {number} time - The current playback time. */ on_playback_time(time) { - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar) || !this.active) { return; } + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar) || !this.active) { return; } this.time = time; if (this.cache === this.current) { // Paint only once if there is no animation if (this.preset.paintMode === 'full' && !this.preset.indicator && this.analysis.binaryMode !== 'visualizer') { @@ -4140,36 +4745,27 @@ class WaveformBar { ); this.throttlePaintRect(currX - barW - SCALE(40), this.y, prePaintW + SCALE(40) * 2, this.h); } - }; + } /** * Handles the waveform bar state when reloading the theme. */ on_script_unload() { - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar)) return; + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar)) return; if (this.analysis.autoDelete) this.removeData(); - }; + } /** * Sets the size and position of the waveform bar and updates them on window resizing. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} w - The width of the waveform bar. + * @param {number} h - The height of the waveform bar. */ on_size(w, h) { - if (['progressbar', 'peakmeterbar'].includes(pref.seekbar)) return; - this.x = SCALE(pref.layout !== 'default' ? 20 : 40); + if (['progressbar', 'peakmeterbar'].includes(grSet.seekbar)) return; + this.x = SCALE(grSet.layout !== 'default' ? 20 : 40); this.y = 0; - this.w = w - SCALE(pref.layout !== 'default' ? 40 : 80); - this.h = geo.waveformBarHeight; - }; - - /** - * Checks if the mouse is within the boundaries of the waveform bar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - trace(x, y) { - return (x >= this.x && y >= this.y && x <= this.x + this.w && y <= this.y + this.h); - }; + this.w = w - SCALE(grSet.layout !== 'default' ? 40 : 80); + this.h = grm.ui.waveformBarH; + } + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-main-functions.js b/profile/georgia-reborn/scripts/Base/gr-main-functions.js deleted file mode 100644 index 82190537..00000000 --- a/profile/georgia-reborn/scripts/Base/gr-main-functions.js +++ /dev/null @@ -1,3289 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Main Functions * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -/////////////////////////////// -// * MAIN - INITIALIZATION * // -/////////////////////////////// -/** - * Clears all now playing related UI strings. - */ -function clearUIVariables() { - const showLowerBarVersion = pref[`showLowerBarVersion_${pref.layout}`]; - return { - artist: '', - tracknum: $(showLowerBarVersion ? pref.layout !== 'default' ? settings.stoppedString1acr : settings.stoppedString1 : ' ', undefined, true), - title_lower: showLowerBarVersion ? ` ${$(settings.stoppedString2, undefined, true)}` : ' ', - year: '', - grid: [], - time: showLowerBarVersion || updateAvailable ? lowerBarStoppedTime : ' ' - }; -} - - -/** - * Initializes the theme on startup or reload. - */ -function initMain() { - // * Init variables - console.log('initMain()'); - loadingTheme = true; - str = clearUIVariables(); - ww = window.Width; - wh = window.Height; - - // * Init components - artCache = new ArtCache(15); - g_tooltip_timer = new TooltipTimer(); - tt = new TooltipHandler(); - playlistHistory = new PlaylistHistory(10); - topMenu = new ButtonEventHandler(); - customMenu = new BaseControl(); - pauseBtn = new PauseButton(); - jumpSearch = new JumpSearch(ww, wh); - progressBar = new ProgressBar(ww, wh); - waveformBar = new WaveformBar(ww, wh); - peakmeterBar = new PeakmeterBar(ww, wh); - - // * Layout safety check - if (!['default', 'artwork', 'compact'].includes(pref.layout)) { - window.SetProperty('Georgia-ReBORN - 05. Layout', 'default'); - pref.layout = 'default'; - display.layoutDefault(); - } - - // * Do auto-delete cache if enabled - if (pref.libraryAutoDelete) DeleteLibraryCache(); - if (pref.biographyAutoDelete) DeleteBiographyCache(); - if (pref.lyricsAutoDelete) DeleteLyrics(); - if (pref.waveformBarAutoDelete) DeleteWaveformBarCache(); - - lastAlbumFolder = ''; - lastPlaybackOrder = fb.PlaybackOrder; - displayPanelOnStartup(); - setThemeColors(); - themeColorSet = true; - - if (pref.asyncThemePreloader) { - on_size(); // Needed when loading async, otherwise just needed in fb.IsPlaying conditional - } - - setGeometry(); - - if (fb.IsPlaying && fb.GetNowPlaying()) { - on_playback_new_track(fb.GetNowPlaying()); - } - - // * Workaround so we can use the Edit menu or run fb.RunMainMenuCommand("Edit/Something...") - // * when the panel has focus and a dedicated playlist viewer doesn't. - plman.SetActivePlaylistContext(); // Once on startup - - // * Init panels - if (!libraryInitialized) { - initLibraryPanel(); - setLibrarySize(); - setTimeout(() => { - lib.initialise(); - initLibraryLayout(); - }, 1); - } - if (!biographyInitialized) { - initBiographyPanel(); - setBiographySize(); - } - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - - if (!pref.lyricsRememberPanelState) { - pref.displayLyrics = false; - } - else if (pref.displayLyrics && pref.lyricsLayout === 'full') { - displayPlaylist = !displayPlaylist; - resizeArtwork(true); - } - if (pref.displayLyrics) { - displayLyrics(); - } - - // * Init colors - if (pref.theme === 'random' && pref.randomThemeAutoColor !== 'off') { - getRandomThemeAutoColor(); - } - - initThemeFull = true; - initCustomTheme(); - initTheme(); - DebugLog('\n>>> initTheme -> initMain <<<\n'); - loadingTheme = false; - - // * Restore backup workaround to successfully restore playlist files after foobar installation - if (pref.restoreBackupPlaylist) { - setTimeout(() => { - RestoreBackupPlaylist(); - }, !loadingTheme); - } - - // * Hide loading screen - setTimeout(() => { - loadingThemeComplete = true; - window.Repaint(); - }, 100); -} - - -/** - * Initializes size and position when noAlbumArtStub is being displayed. - */ -function initNoAlbumArtSize() { - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; - - albumArtSize.x = - pref.layout === 'default' && displayCustomThemeMenu && !displayPlaylist && !displayLibrary && !displayBiography ? ww * 0.3 : - pref.layout === 'default' && !displayCustomThemeMenu && !displayPlaylist && !displayLibrary && !displayBiography || - pref.layout === 'artwork' && displayPlaylist ? ww : - pref.panelWidthAuto ? - pref.albumArtAlign === 'left' ? 0 : - pref.albumArtAlign === 'leftMargin' ? ww / wh > 1.8 ? SCALE(40) : 0 : - pref.albumArtAlign === 'center' ? Math.floor(ww * 0.25 - noAlbumArtSize * 0.5) : - Math.floor(ww * 0.5 - noAlbumArtSize) : - 0; - - albumArtSize.y = geo.topMenuHeight; - - albumArtSize.w = - pref.panelWidthAuto && noAlbumArtStub ? !fb.IsPlaying ? 0 : noAlbumArtSize : - ww * 0.5; - - albumArtSize.h = noAlbumArtSize; -} - - -/** - * Initializes everything necessary in all panels without the need of a reload. - */ -function initPanels() { - // * Update Main - createFonts(); - setGeometry(); - str.timeline = new Timeline(geo.timelineHeight); - str.metadata_grid_tt = new MetadataGridTooltip(geo.metadataGridTooltipHeight); - str.lowerBar_tt = new LowerBarTooltip(); - jumpSearch = new JumpSearch(ww, wh); - volumeBtn = new VolumeBtn(); - progressBar = new ProgressBar(ww, wh); - peakmeterBar = new PeakmeterBar(ww, wh); - peakmeterBar.on_size(ww, wh); - waveformBar = new WaveformBar(ww, wh); - waveformBar.updateBar(); - createButtonImages(); - createButtonObjects(ww, wh); - resizeArtwork(true); - initButtonState(); - - if (fb.GetNowPlaying()) { - on_metadb_changed(); // Refresh panel - } - - setTimeout(() => { - // * Update Playlist - createPlaylistFonts(); - rescalePlaylist(true); - initPlaylist(); - playlist.on_size(ww, wh); - - // * Update Library - setLibrarySize(); - panel.tree.y = panel.search.h; - pop.createImages(); - panel.zoomReset(); - initLibraryLayout(); - - // * Update Biography - setBiographySize(); - uiBio.setSbar(); - butBio.createImages(); - butBio.resetZoom(); - initBiographyColors(); - initBiographyLayout(); - }, loadingThemeComplete); -} - - -/** - * Initializes size and position of the current panel when using pref.panelWidthAuto. - */ -function initPanelWidthAuto() { - resizeArtwork(true); - - if (displayLibrarySplit() || displayLibrary && pref.libraryLayout === 'full') return; - - if (displayPlaylist && (noAlbumArtStub || playlist.x !== albumArtSize.x + albumArtSize.w)) { - DebugLog('initPanelWidthAuto -> Playlist'); - playlist.on_size(ww, wh); - } - if (displayLibrary && (noAlbumArtStub || ui.x !== albumArtSize.x + albumArtSize.w)) { - DebugLog('initPanelWidthAuto -> Library'); - setLibrarySize(); - } - if (displayBiography && (noAlbumArtStub || uiBio.x + uiBio.w !== albumArtSize.x + albumArtSize.w)) { - DebugLog('initPanelWidthAuto -> Biography'); - setBiographySize(); - } -} - - -/** - * Initializes the theme when updating colors. - */ -function initTheme() { - const themeProfiler = timings.showDebugTiming && fb.CreateProfiler('initTheme'); - - const fullInit = - initThemeFull || pref.themeBrightness !== 'default' - || - ppt.theme !== 0 || pptBio.theme !== 0 - || - pref.theme === 'reborn' || pref.theme === 'random' - || - pref.styleBlackAndWhiteReborn || pref.styleBlackReborn; - - // * SETUP COLORS * // - setImageBrightness(); - if (pref.styleBlackAndWhiteReborn) initBlackAndWhiteReborn(); - if (pref.theme === 'random' && !isStreaming && !isPlayingCD) getRandomThemeColor(); - if (noAlbumArtStub || isStreaming || isPlayingCD) setNoAlbumArtColors(); - if ((pref.styleBlend || pref.styleBlend2 || pref.styleProgressBarFill === 'blend') && albumArt) setStyleBlend(); - if (pref.themeDayNightMode && !pref.themeSetupDay && !pref.themeSetupNight) initThemeDayNightState(); - setBackgroundColorDefinition(); - - // * INIT COLORS * // - initPlaylistColors(); - initLibraryColors(); - initBiographyColors(); - initMainColors(); - initStyleColors(); - initChronflowColors(); - - // * POST-INIT COLOR ADJUSTMENTS * // - themeColorAdjustments(); - if (!fullInit) return; - if (pref.themeBrightness !== 'default') adjustThemeBrightness(pref.themeBrightness); - if (pref.playlistRowHover) playlist.title_color_change(); - if (img.labels.overlayDark) ui.getItemColours(); // Refresh Library - txt.artCalc(); txt.albCalc(); // Refresh Biography - - // * UPDATE BUTTONS * // - playlist.initScrollbar(); - sbar.setCol(); // Library - pop.createImages(); // Library - but.createImages(); // Library - but.refresh(true); // Library - alb_scrollbar.setCol(); // Biography - art_scrollbar.setCol(); // Biography - butBio.createImages('all'); // Biography - imgBio.createImages(); // Biography - createButtonImages(); // Main - createButtonObjects(ww, wh); // Main - initButtonState(); // Main - - // * REFRESH * // - window.Repaint(); - - if (themeProfiler) themeProfiler.Print(); -} - - -/** - * Initializes the theme day and night state. - * - Aborts if `pref.themeDayNightMode` is falsy or if any custom GR theme tags are detected. - * - Restores the day or night theme based on `pref.themeDayNightTime` if the current theme does not match the expected day or night theme. - * - Sets an interval to check and update the theme every 10 minutes based on the time of day. - */ -function initThemeDayNightState() { - const customTheme = $('[%GR_THEME%]'); - const customStyle = $('[%GR_STYLE%]'); - const customPreset = $('[%GR_PRESET%]'); - - if (!pref.themeDayNightMode || customTheme || customStyle || customPreset) { - return; - } - - // * Restore day or night theme after custom GR theme tags usage - if (pref.theme !== pref.theme_day && pref.themeDayNightTime === 'day' || - pref.theme !== pref.theme_night && pref.themeDayNightTime === 'night') { - resetTheme(); - initThemeDayNightMode(new Date()); - initThemeFull = true; - return; - } - - // * Check every 10 minutes if it is day or night for the entire play session - if (!themeDayNightModeTimer) { - themeDayNightModeTimer = setInterval(() => { - initThemeDayNightMode(new Date()); - initThemeFull = true; - initTheme(); - DebugLog('\n>>> initTheme -> fetchNewArtwork -> on_playback_new_track -> themeDayNightModeTimer <<<\n'); - }, 600000); - } -} - - -/** - * Initializes %GR_THEME%, %GR_STYLE%, %GR_PRESET% tags in music files and sets them, used in on_playback_new_track. - */ -function initThemeTags() { - const customTheme = $('[%GR_THEME%]'); - const customStyle = $('[%GR_STYLE%]'); - const customPreset = $('[%GR_PRESET%]'); - - themePresetIndicator = false; - - // * Restore last theme state - if (pref.presetSelectMode === 'default' && themeRestoreState) { - DebugLog('\n>>> initThemeTags restore <<<\n'); - resetStyle('all'); - resetTheme(); - restoreThemeStylePreset(); // * Retore saved pref settings - if (pref.savedPreset !== false) setThemePreset(pref.savedPreset); - initStyleState(); - themeRestoreState = false; - } - - // * Skip also restore on next call - if (pref.theme === pref.savedTheme && !customTheme && !customStyle && !customPreset) { - DebugLog('\n>>> initThemeTags skipped <<<\n'); - restoreThemeStylePreset(true); // * Reset saved pref settings - themeRestoreState = false; - return; - } - - // * 1. Set preset - if (customPreset.length) { - DebugLog('\n>>> initThemeTags -> %GR_PRESET% loaded <<<'); - pref.preset = customPreset; - setThemePreset(customPreset); - themeRestoreState = true; - } - // * 2. Set theme - else if (customTheme.length) { - DebugLog('\n>>> initThemeTags -> %GR_THEME% loaded <<<'); - pref.theme = customTheme; - resetTheme(); - themeRestoreState = true; - } - // * 3. Set styles - if (customStyle.length && !customPreset.length) { - DebugLog('\n>>> initThemeTags -> %GR_STYLE% loaded <<<'); - resetStyle('all'); - for (const style of customStyle.split(/(?:,|;| )+/)) { - switch (style) { - case 'nighttime': pref.styleNighttime = true; break; - case 'bevel': pref.styleBevel = true; break; - case 'blend': pref.styleBlend = true; break; - case 'blend2': pref.styleBlend2 = true; break; - case 'gradient': pref.styleGradient = true; break; - case 'gradient2': pref.styleGradient2 = true; break; - case 'alternative': pref.styleAlternative = true; break; - case 'alternative2': pref.styleAlternative2 = true; break; - case 'blackAndWhite': pref.styleBlackAndWhite = true; break; - case 'blackAndWhite2': pref.styleBlackAndWhite2 = true; break; - case 'blackReborn': pref.styleBlackReborn = true; break; - case 'rebornWhite': pref.styleRebornWhite = true; break; - case 'rebornBlack': pref.styleRebornBlack = true; break; - case 'rebornFusion': pref.styleRebornFusion = true; break; - case 'rebornFusion2': pref.styleRebornFusion2 = true; break; - case 'randomPastel': pref.styleRandomPastel = true; break; - case 'randomDark': pref.styleRandomDark = true; break; - case 'rebornFusionAccent': pref.styleRebornFusionAccent = true; break; - case 'topMenuButtons=filled': pref.styleTopMenuButtons = 'filled'; break; - case 'topMenuButtons=bevel': pref.styleTopMenuButtons = 'bevel'; break; - case 'topMenuButtons=inner': pref.styleTopMenuButtons = 'inner'; break; - case 'topMenuButtons=emboss': pref.styleTopMenuButtons = 'emboss'; break; - case 'topMenuButtons=minimal': pref.styleTopMenuButtons = 'minimal'; break; - case 'transportButtons=bevel': pref.styleTransportButtons = 'bevel'; break; - case 'transportButtons=inner': pref.styleTransportButtons = 'inner'; break; - case 'transportButtons=emboss': pref.styleTransportButtons = 'emboss'; break; - case 'transportButtons=minimal': pref.styleTransportButtons = 'minimal'; break; - case 'progressBarDesign=rounded': pref.styleProgressBarDesign = 'rounded'; break; - case 'progressBarDesign=lines': pref.styleProgressBarDesign = 'lines'; break; - case 'progressBarDesign=blocks': pref.styleProgressBarDesign = 'blocks'; break; - case 'progressBarDesign=dots': pref.styleProgressBarDesign = 'dots'; break; - case 'progressBarDesign=thin': pref.styleProgressBarDesign = 'thin'; break; - case 'progressBarBg=bevel': pref.styleProgressBar = 'bevel'; break; - case 'progressBarBg=inner': pref.styleProgressBar = 'inner'; break; - case 'progressBarFill=bevel': pref.styleProgressBarFill = 'bevel'; break; - case 'progressBarFill=inner': pref.styleProgressBarFill = 'inner'; break; - case 'progressBarFill=blend': pref.styleProgressBarFill = 'blend'; break; - case 'volumeBarDesign=rounded': pref.styleVolumeBarDesign = 'rounded'; break; - case 'volumeBarBg=bevel': pref.styleVolumeBar = 'bevel'; break; - case 'volumeBarBg=inner': pref.styleVolumeBar = 'inner'; break; - case 'volumeBarFill=bevel': pref.styleVolumeBarFill = 'bevel'; break; - case 'volumeBarFill=inner': pref.styleVolumeBarFill = 'bevel'; break; - } - } - themeRestoreState = true; - } - - // * 4. Update theme - if (!customPreset.length) { // Prevent double initialization for theme presets to save performance, updateStyle() already handled in setThemePreset() - updateStyle(); - } -} - - -/** - * Initializes the custom themes to check if any are currently active. - */ -function initCustomTheme() { - const customThemes = { - custom01: customTheme01, - custom02: customTheme02, - custom03: customTheme03, - custom04: customTheme04, - custom05: customTheme05, - custom06: customTheme06, - custom07: customTheme07, - custom08: customTheme08, - custom09: customTheme09, - custom10: customTheme10 - }; - - if (pref.theme in customThemes) { - customColor = customThemes[pref.theme]; - } -} - - -/** - * Initializes styles to check if any are currently active, used in top menu Options > Style. - */ -function initStyleState() { - const styles = [ - pref.styleNighttime, - pref.styleBevel, - pref.styleBlend, - pref.styleBlend2, - pref.styleGradient, - pref.styleGradient2, - pref.styleAlternative, - pref.styleAlternative2, - pref.styleBlackAndWhite, - pref.styleBlackAndWhite2, - pref.styleBlackAndWhiteReborn, - pref.styleBlackReborn, - pref.styleRebornWhite, - pref.styleRebornBlack, - pref.styleRebornFusion, - pref.styleRebornFusion2, - pref.styleRebornFusionAccent, - pref.styleRandomPastel, - pref.styleRandomDark, - pref.styleTopMenuButtons !== 'default', - pref.styleTransportButtons !== 'default', - pref.styleProgressBarDesign !== 'default', - pref.styleProgressBar !== 'default', - pref.styleProgressBarFill !== 'default', - pref.styleVolumeBarDesign !== 'default', - pref.styleVolumeBar !== 'default', - pref.styleVolumeBarFill !== 'default' - ]; - - pref.styleDefault = !styles.some(style => style); -} - - -/** - * Resets the current player size, used in top menu Options > Player size. - */ -function resetPlayerSize() { - pref.playerSize_HD_small = false; - pref.playerSize_HD_normal = false; - pref.playerSize_HD_large = false; - pref.playerSize_QHD_small = false; - pref.playerSize_QHD_normal = false; - pref.playerSize_QHD_large = false; - pref.playerSize_4K_small = false; - pref.playerSize_4K_normal = false; - pref.playerSize_4K_large = false; -} - - -/** - * Resets the theme when changing to a different one, used in top menu Options > Theme. - */ -function resetTheme() { - initThemeFull = true; - - const invalidNighttimeStyle = pref.theme !== 'reborn' && pref.theme !== 'random' && !pref.theme.startsWith('custom') || pref.styleRebornWhite || pref.styleRebornBlack; - const invalidWhiteThemeStyle = pref.theme !== 'white' && (pref.styleBlackAndWhite || pref.styleBlackAndWhite2 || pref.styleBlackAndWhiteReborn); - const invalidBlackThemeStyle = pref.theme !== 'black' && pref.styleBlackReborn; - const invalidRebornThemeStyle = pref.theme !== 'reborn' && (pref.styleRebornWhite || pref.styleRebornBlack || pref.styleRebornFusion || pref.styleRebornFusion2 || pref.styleRebornFusionAccent); - const invalidGradientStyle = !['reborn', 'random', 'blue', 'darkblue', 'red'].includes(pref.theme) && !pref.theme.startsWith('custom') && (pref.styleGradient || pref.styleGradient2); - - // * Disable style nighttime for themes that do not support it - if (invalidNighttimeStyle) { - pref.styleNighttime = false; - initStyleState(); - } - - // * Reset themes that do not support specific styles to the default style - if (invalidWhiteThemeStyle || invalidBlackThemeStyle || invalidRebornThemeStyle || invalidGradientStyle) { - resetStyle('all'); - } - - getThemeColors(albumArt); - - // * Update default theme colors when nothing is playing or when changing themes - if (!fb.IsPlaying) setThemeColors(); -} - - -/** - * Resets all styles or grouped styles when changing styles. Used in top menu Options > Style. - * @param {string} group Specifies which group of styles to reset: - * - 'group_one' - * - 'group_two' - * - 'all' - * - 'all_theme_day_night' - */ -function resetStyle(group) { - const _day_night = pref.themeSetupDay ? '_day' : '_night'; - - if (group === 'group_one') { - pref.styleBlend = false; - pref.styleBlend2 = false; - pref.styleGradient = false; - pref.styleGradient2 = false; - } - else if (group === 'group_two') { - pref.styleAlternative = false; - pref.styleAlternative2 = false; - pref.styleBlackAndWhite = false; - pref.styleBlackAndWhite2 = false; - pref.styleBlackAndWhiteReborn = false; - pref.styleBlackReborn = false; - pref.styleRebornWhite = false; - pref.styleRebornBlack = false; - pref.styleRebornFusion = false; - pref.styleRebornFusion2 = false; - pref.styleRebornFusionAccent = false; - pref.styleRandomPastel = false; - pref.styleRandomDark = false; - } - else if (group === 'all') { - initThemeFull = true; - pref.styleDefault = true; - pref.styleNighttime = false; - pref.styleBevel = false; - pref.styleBlend = false; - pref.styleBlend2 = false; - pref.styleGradient = false; - pref.styleGradient2 = false; - pref.styleAlternative = false; - pref.styleAlternative2 = false; - pref.styleBlackAndWhite = false; - pref.styleBlackAndWhite2 = false; - pref.styleBlackAndWhiteReborn = false; - pref.styleBlackReborn = false; - pref.styleRebornWhite = false; - pref.styleRebornBlack = false; - pref.styleRebornFusion = false; - pref.styleRebornFusion2 = false; - pref.styleRebornFusionAccent = false; - pref.styleRandomPastel = false; - pref.styleRandomDark = false; - pref.styleRandomAutoColor = 'off'; - pref.styleTopMenuButtons = 'default'; - pref.styleTransportButtons = 'default'; - pref.styleProgressBarDesign = 'default'; - pref.styleProgressBar = 'default'; - pref.styleProgressBarFill = 'default'; - pref.styleVolumeBarDesign = 'default'; - pref.styleVolumeBar = 'default'; - pref.styleVolumeBarFill = 'default'; - pref.themeBrightness = 'default'; - } - else if (group === 'all_theme_day_night') { - initThemeFull = true; - pref.styleDefault = true; - pref[`styleNighttime${_day_night}`] = false; - pref[`styleBevel${_day_night}`] = false; - pref[`styleBlend${_day_night}`] = false; - pref[`styleBlend2${_day_night}`] = false; - pref[`styleGradient${_day_night}`] = false; - pref[`styleGradient2${_day_night}`] = false; - pref[`styleAlternative${_day_night}`] = false; - pref[`styleAlternative2${_day_night}`] = false; - pref[`styleBlackAndWhite${_day_night}`] = false; - pref[`styleBlackAndWhite2${_day_night}`] = false; - pref[`styleBlackAndWhiteReborn${_day_night}`] = false; - pref[`styleBlackReborn${_day_night}`] = false; - pref[`styleRebornWhite${_day_night}`] = false; - pref[`styleRebornBlack${_day_night}`] = false; - pref[`styleRebornFusion${_day_night}`] = false; - pref[`styleRebornFusion2${_day_night}`] = false; - pref[`styleRebornFusionAccent${_day_night}`] = false; - pref[`styleRandomPastel${_day_night}`] = false; - pref[`styleRandomDark${_day_night}`] = false; - pref[`styleRandomAutoColor${_day_night}`] = 'off'; - pref[`styleTopMenuButtons${_day_night}`] = 'default'; - pref[`styleTransportButtons${_day_night}`] = 'default'; - pref[`styleProgressBarDesign${_day_night}`] = 'default'; - pref[`styleProgressBar${_day_night}`] = 'default'; - pref[`styleProgressBarFill${_day_night}`] = 'default'; - pref[`styleVolumeBarDesign${_day_night}`] = 'default'; - pref[`styleVolumeBar${_day_night}`] = 'default'; - pref[`styleVolumeBarFill${_day_night}`] = 'default'; - pref[`themeBrightness${_day_night}`] = 'default'; - } -} - - -/** - * Restores theme, style, preset after custom %GR_THEME%, %GR_STYLE%, %GR_PRESET% usage or in theme sandbox. - * Used in initThemeTags() and theme sandbox options. - * @param {boolean} reset Determines whether to reset the theme style preset or restore it. - */ -function restoreThemeStylePreset(reset) { - if (reset) { - pref.savedTheme = pref.theme; - pref.savedStyleNighttime = pref.styleNighttime; - pref.savedStyleBevel = pref.styleBevel; - pref.savedStyleBlend = pref.styleBlend; - pref.savedStyleBlend2 = pref.styleBlend2; - pref.savedStyleGradient = pref.styleGradient; - pref.savedStyleGradient2 = pref.styleGradient2; - pref.savedStyleAlternative = pref.styleAlternative; - pref.savedStyleAlternative2 = pref.styleAlternative2; - pref.savedStyleBlackAndWhite = pref.styleBlackAndWhite; - pref.savedStyleBlackAndWhite2 = pref.styleBlackAndWhite2; - pref.savedStyleBlackAndWhiteReborn = pref.styleBlackAndWhiteReborn; - pref.savedStyleBlackReborn = pref.styleBlackReborn; - pref.savedStyleRebornWhite = pref.styleRebornWhite; - pref.savedStyleRebornBlack = pref.styleRebornBlack; - pref.savedStyleRebornFusion = pref.styleRebornFusion; - pref.savedStyleRebornFusion2 = pref.styleRebornFusion2; - pref.savedStyleRebornFusionAccent = pref.styleRebornFusionAccent; - pref.savedStyleRandomPastel = pref.styleRandomPastel; - pref.savedStyleRandomDark = pref.styleRandomDark; - pref.savedStyleRandomAutoColor = pref.styleRandomAutoColor; - pref.savedStyleTopMenuButtons = pref.styleTopMenuButtons; - pref.savedStyleTransportButtons = pref.styleTransportButtons; - pref.savedStyleProgressBarDesign = pref.styleProgressBarDesign; - pref.savedStyleProgressBar = pref.styleProgressBar; - pref.savedStyleProgressBarFill = pref.styleProgressBarFill; - pref.savedStyleVolumeBarDesign = pref.styleVolumeBarDesign; - pref.savedStyleVolumeBar = pref.styleVolumeBar; - pref.savedStyleVolumeBarFill = pref.styleVolumeBarFill; - pref.savedThemeBrightness = pref.themeBrightness; - pref.savedPreset = false; - } else { - pref.theme = pref.savedTheme; - pref.styleNighttime = pref.savedStyleNighttime; - pref.styleBevel = pref.savedStyleBevel; - pref.styleBlend = pref.savedStyleBlend; - pref.styleBlend2 = pref.savedStyleBlend2; - pref.styleGradient = pref.savedStyleGradient; - pref.styleGradient2 = pref.savedStyleGradient2; - pref.styleAlternative = pref.savedStyleAlternative; - pref.styleAlternative2 = pref.savedStyleAlternative2; - pref.styleBlackAndWhite = pref.savedStyleBlackAndWhite; - pref.styleBlackAndWhite2 = pref.savedStyleBlackAndWhite2; - pref.styleBlackAndWhiteReborn = pref.savedStyleBlackAndWhiteReborn; - pref.styleBlackReborn = pref.savedStyleBlackReborn; - pref.styleRebornWhite = pref.savedStyleRebornWhite; - pref.styleRebornBlack = pref.savedStyleRebornBlack; - pref.styleRebornFusion = pref.savedStyleRebornFusion; - pref.styleRebornFusion2 = pref.savedStyleRebornFusion2; - pref.styleRebornFusionAccent = pref.savedStyleRebornFusionAccent; - pref.styleRandomPastel = pref.savedStyleRandomPastel; - pref.styleRandomDark = pref.savedStyleRandomDark; - pref.styleRandomAutoColor = pref.savedStyleRandomAutoColor; - pref.styleTopMenuButtons = pref.savedStyleTopMenuButtons; - pref.styleTransportButtons = pref.savedStyleTransportButtons; - pref.styleProgressBarDesign = pref.savedStyleProgressBarDesign; - pref.styleProgressBar = pref.savedStyleProgressBar; - pref.styleProgressBarFill = pref.savedStyleProgressBarFill; - pref.styleVolumeBarDesign = pref.savedStyleVolumeBarDesign; - pref.styleVolumeBar = pref.savedStyleVolumeBar; - pref.styleVolumeBarFill = pref.savedStyleVolumeBarFill; - pref.themeBrightness = pref.savedThemeBrightness; - pref.preset = pref.savedPreset; - } -} - - -/** - * Sets the chosen style based by its current state. Used when changing styles in top menu Options > Style. - * @param {string} style The selected style. - * @param {boolean} state The state of the selected style will be either activated or deactivated. - */ -function setStyle(style, state) { - const _day_night = pref.themeSetupDay ? '_day' : '_night'; - - if (pref.themeSetupDay || pref.themeSetupNight) { - resetStyle('all_theme_day_night'); - } - - switch (style) { - case 'blend': resetStyle('group_one'); pref[`styleBlend${_day_night}`] = pref.styleBlend = state; break; - case 'blend2': resetStyle('group_one'); pref[`styleBlend2${_day_night}`] = pref.styleBlend2 = state; break; - case 'gradient': resetStyle('group_one'); pref[`styleGradient${_day_night}`] = pref.styleGradient = state; break; - case 'gradient2': resetStyle('group_one'); pref[`styleGradient2${_day_night}`] = pref.styleGradient2 = state; break; - case 'alternative': resetStyle('group_two'); pref[`styleAlternative${_day_night}`] = pref.styleAlternative = state; break; - case 'alternative2': resetStyle('group_two'); pref[`styleAlternative2${_day_night}`] = pref.styleAlternative2 = state; break; - case 'blackAndWhite': resetStyle('group_two'); pref[`styleBlackAndWhite${_day_night}`] = pref.styleBlackAndWhite = state; break; - case 'blackAndWhite2': resetStyle('group_two'); pref[`styleBlackAndWhite2${_day_night}`] = pref.styleBlackAndWhite2 = state; break; - case 'blackAndWhiteReborn': resetStyle('group_two'); pref[`styleBlackAndWhiteReborn${_day_night}`] = pref.styleBlackAndWhiteReborn = state; break; - case 'blackReborn': resetStyle('group_two'); pref[`styleBlackReborn${_day_night}`] = pref.styleBlackReborn = state; break; - case 'rebornWhite': resetStyle('group_two'); pref[`styleRebornWhite${_day_night}`] = pref.styleRebornWhite = state; pref[`themeBrightness${_day_night}`] = pref.themeBrightness = 'default'; break; - case 'rebornBlack': resetStyle('group_two'); pref[`styleRebornBlack${_day_night}`] = pref.styleRebornBlack = state; pref[`themeBrightness${_day_night}`] = pref.themeBrightness = 'default'; break; - case 'rebornFusion': resetStyle('group_two'); pref[`styleRebornFusion${_day_night}`] = pref.styleRebornFusion = state; break; - case 'rebornFusion2': resetStyle('group_two'); pref[`styleRebornFusion2${_day_night}`] = pref.styleRebornFusion2 = state; break; - case 'rebornFusionAccent': resetStyle('group_two'); pref[`styleRebornFusionAccent${_day_night}`] = pref.styleRebornFusionAccent = state; break; - case 'randomPastel': resetStyle('group_two'); pref[`styleRandomPastel${_day_night}`] = pref.styleRandomPastel = state; break; - case 'randomDark': resetStyle('group_two'); pref[`styleRandomDark${_day_night}`] = pref.styleRandomDark = state; break; - } -} - - -/** - * Sets a new random theme preset. - */ -function setRandomThemePreset() { - if (pref.presetSelectMode === 'theme') { - setThemePresetSelection(false, true); - } - if ((!['off', 'track'].includes(pref.presetAutoRandomMode) && pref.presetSelectMode === 'harmonic' || - pref.presetAutoRandomMode === 'dblclick' && pref.presetSelectMode === 'theme') && !doubleClicked) { - getRandomThemePreset(); - } -} - - -/** - * Activates or deactivates all theme presets selection, used in top menu Options > Preset > Select presets. - * @param {boolean} state The state of theme presets selection will be set to true or false. - * @param {boolean} presetSelectModeTheme The selection of theme specified presets. - */ -function setThemePresetSelection(state, presetSelectModeTheme) { - pref.presetSelectWhite = state; - pref.presetSelectBlack = state; - pref.presetSelectReborn = state; - pref.presetSelectRandom = state; - pref.presetSelectBlue = state; - pref.presetSelectDarkblue = state; - pref.presetSelectRed = state; - pref.presetSelectCream = state; - pref.presetSelectNblue = state; - pref.presetSelectNgreen = state; - pref.presetSelectNred = state; - pref.presetSelectNgold = state; - pref.presetSelectCustom = state; - - if (presetSelectModeTheme) { - switch (pref.savedTheme) { - case 'white': pref.presetSelectWhite = true; break; - case 'black': pref.presetSelectBlack = true; break; - case 'reborn': pref.presetSelectReborn = true; break; - case 'random': pref.presetSelectRandom = true; break; - case 'blue': pref.presetSelectBlue = true; break; - case 'darkblue': pref.presetSelectDarkblue = true; break; - case 'red': pref.presetSelectRed = true; break; - case 'cream': pref.presetSelectCream = true; break; - case 'nblue': pref.presetSelectNblue = true; break; - case 'ngreen': pref.presetSelectNgreen = true; break; - case 'nred': pref.presetSelectNred = true; break; - case 'ngold': pref.presetSelectNgold = true; break; - default: if (pref.theme.startsWith('custom')) pref.presetSelectCustom = true; break; - } - } -} - - -/** - * Sets the theme to a factory reset state, used on the very first foobar start after installation or when resetting the theme. - */ -async function systemFirstLaunch() { - if (!pref.systemFirstLaunch) return; - - await initMain(); - await setThemeSettings(); - await initMain(); - await display.autoDetectRes(); - - pref.systemFirstLaunch = false; -} - - -/** - * Updates the theme when changing styles, used in top menu Options > Style. - */ -function updateStyle() { - initThemeFull = true; - - if (['white', 'black', 'reborn', 'random'].includes(pref.theme)) { - // * Update col.primary for dynamic themes - if (fb.IsPlaying) { - getThemeColors(albumArt); - } else { - setThemeColors(); - } - } - - initTheme(); - DebugLog('\n>>> initTheme -> updateStyle <<<\n'); - if (pref.theme === 'random' && pref.randomThemeAutoColor !== 'off') getRandomThemeAutoColor(); - initStyleState(); - initThemePresetState(); - initButtonState(); -} - - -///////////////////////// -// * MAIN - CONTROLS * // -///////////////////////// -/** - * Displays the panel, mostly used for the custom menu. - * @param {string} panel The panel to display: - * - 'playlist' - * - 'details' - * - 'library' - * - 'biography' - * - 'lyrics' - */ -function displayPanel(panel) { - switch (panel) { - case 'playlist': displayPlaylist = true; displayDetails = false; displayLibrary = false; displayBiography = false; pref.displayLyrics = false; break; - case 'details': displayPlaylist = false; displayDetails = true; displayLibrary = false; displayBiography = false; pref.displayLyrics = false; break; - case 'library': displayPlaylist = false; displayDetails = false; displayLibrary = true; displayBiography = false; pref.displayLyrics = false; break; - case 'biography': displayPlaylist = false; displayDetails = false; displayLibrary = false; displayBiography = true; pref.displayLyrics = false; break; - case 'lyrics': displayPlaylist = true; displayDetails = false; displayLibrary = false; displayBiography = true; pref.displayLyrics = true; break; - } - resizeArtwork(true); - initButtonState(); -} - - -/** - * Displays the set panel ( Options > Player controls > Panel > Show panel on startup ) when starting foobar, used in initMain(). - */ -function displayPanelOnStartup() { - // * Added additional conditions to show Playlist and not Details in Compact layout if Playlist is not displayed on startup - // * while starting in Compact layout, this also fixes ugly switch from Default to Compact layout - if (pref.showPanelOnStartup === 'cover' && pref.layout === 'artwork') { - displayPlaylist = false; - } - else if (pref.showPanelOnStartup === 'playlist' || pref.layout === 'compact') { - if (pref.layout === 'artwork') displayPlaylistArtwork = true; - else displayPlaylist = true; - } - else if (pref.showPanelOnStartup === 'details' && pref.layout !== 'compact') { - displayPlaylist = pref.layout === 'artwork'; - } - else if (pref.showPanelOnStartup === 'library' && pref.layout !== 'compact') { - displayLibrary = true; - if (pref.libraryLayout === 'split') displayPlaylist = true; - } - else if (pref.showPanelOnStartup === 'biography' && pref.layout !== 'compact') { - displayPlaylist = true; - displayBiography = true; - } - else if (pref.showPanelOnStartup === 'lyrics' && pref.layout !== 'compact') { - displayLyrics(); - } -} - - -/////////////////////// -// * MAIN - TIMERS * // -/////////////////////// -/** - * Repaints rectangles on the seekbar for real time update. - */ -function refreshSeekbar() { - // * Time - window.RepaintRect(lowerBarTimeX, lowerBarTimeY, lowerBarTimeW, lowerBarTimeH, pref.spinDiscArt && !pref.displayLyrics); - - if (pref.seekbar === 'waveformbar') return; - - // * Progress bar - const x = pref.layout !== 'default' ? SCALE(18) : SCALE(38); - const y = (pref.seekbar === 'peakmeterbar' ? peakmeterBarY - SCALE(4) : progressBarY) - SCALE(2); - const w = pref.layout !== 'default' ? ww - SCALE(36) : ww - SCALE(76); - const h = (pref.seekbar === 'peakmeterbar' ? geo.peakmeterBarHeight + SCALE(8) : geo.progBarHeight) + SCALE(4); - window.RepaintRect(x, y, w, h, pref.spinDiscArt && !pref.displayLyrics); -} - - -/** - * Sets a given timer interval to update the progress bar. - */ -function setProgressBarRefresh() { - DebugLog('setProgressBarRefresh()'); - if (fb.PlaybackLength > 0) { - const refreshRate = { - // We want to update the progress bar for every pixel so divide total time by number of pixels in progress bar - variable: Math.abs(Math.ceil(1000 / ((ww - SCALE(80)) / fb.PlaybackLength))), - 1000: 1000, - 500: 500, - 333: 333, - 250: 250, - 200: 200, - 150: 150, - 120: 120, - 100: 100, - 80: 80, - 60: 60, - 30: 30 - }; - - progressBarTimerInterval = refreshRate[pref.seekbar === 'peakmeterbar' ? pref.peakmeterBarRefreshRate : pref.progressBarRefreshRate]; - - if (pref.progressBarRefreshRate === 'variable') { - while (progressBarTimerInterval > 500) { // We want even multiples of the base progressBarTimerInterval, so that the progress bar always updates as smoothly as possible - progressBarTimerInterval = Math.floor(progressBarTimerInterval / 2); - } - while (progressBarTimerInterval < 32) { // Roughly 30fps - progressBarTimerInterval *= 2; - } - } - } - else { // * Radio streaming - progressBarTimerInterval = 1000; - } - - if (timings.showDebugTiming) console.log(`Progress bar will update every ${progressBarTimerInterval}ms or ${1000 / progressBarTimerInterval} times per second.`); - - if (progressBarTimer) clearInterval(progressBarTimer); - progressBarTimer = null; - - if (!fb.IsPaused) { - progressBarTimer = setInterval(() => { - refreshSeekbar(); - }, progressBarTimerInterval || 1000); - } -} - - -///////////////////////// -// * MAIN - GRAPHICS * // -///////////////////////// -/** - * Creates the top menu and lower bar button images for button state 'Enabled', 'Hovered', 'Down'. - */ -function createButtonImages() { - const createButtonProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('createButtonImages'); - const transportCircleSize = Math.round(pref[`transportButtonSize_${pref.layout}`] * 0.93333); - let btns = {}; - - try { - btns = { - Stop: { - ico: g_guifx.stop, - font: ft.guifx, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - Previous: { - ico: g_guifx.previous, - font: ft.guifx, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - Play: { - ico: g_guifx.play, - font: ft.guifx, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - Pause: { - ico: g_guifx.pause, - font: ft.guifx, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - Next: { - ico: g_guifx.next, - font: ft.guifx, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - PlaybackDefault: { - ico: g_guifx.right, - font: ft.pbo_default, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - PlaybackRepeatPlaylist: { - ico: '\uf01e', - font: ft.pbo_repeat_playlist, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - PlaybackRepeatTrack: { - ico: '\uf021', - font: ft.pbo_repeat_track, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - PlaybackShuffle: { - ico: g_guifx.shuffle, - font: ft.pbo_shuffle, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - ShowVolume: { - ico: g_guifx.volume_down, - font: ft.guifx_volume, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - Reload: { - ico: g_guifx.power, - font: ft.guifx_reload, - type: 'transport', - w: transportCircleSize, - h: transportCircleSize - }, - Minimize: { - ico: '0', - font: ft.top_menu_caption, - type: 'window', - w: 22, - h: 22 - }, - Maximize: { - ico: '2', - font: ft.top_menu_caption, - type: 'window', - w: 22, - h: 22 - }, - Close: { - ico: 'r', - font: ft.top_menu_caption, - type: 'window', - w: 22, - h: 22 - }, - Hamburger: { - ico: '\uf0c9', - font: ft.top_menu_compact, - type: 'compact' - }, - TopMenu: { - ico: 'Menu', - font: ft.top_menu, - type: 'compact' - }, - File: { - ico: 'File', - font: ft.top_menu, - type: 'menu' - }, - Edit: { - ico: 'Edit', - font: ft.top_menu, - type: 'menu' - }, - View: { - ico: 'View', - font: ft.top_menu, - type: 'menu' - }, - Playback: { - ico: 'Playback', - font: ft.top_menu, - type: 'menu' - }, - MediaLibrary: { - ico: 'Media', - font: ft.top_menu, - type: 'menu' - }, - Help: { - ico: 'Help', - font: ft.top_menu, - type: 'menu' - }, - Playlists: { - ico: 'Playlists', - font: ft.top_menu, - type: 'menu' - }, - Options: { - ico: 'Options', - font: ft.top_menu, - type: 'menu' - }, - Details: { - ico: 'Details', - font: ft.top_menu, - type: 'menu' - }, - PlaylistArtworkLayout: { - ico: 'Playlist', - font: ft.top_menu, - type: 'menu' - }, - Library: { - ico: 'Library', - font: ft.top_menu, - type: 'menu' - }, - Lyrics: { - ico: 'Lyrics', - font: ft.top_menu, - type: 'menu' - }, - Biography: { - ico: 'Biography', - font: ft.top_menu, - type: 'menu' - }, - Rating: { - ico: 'Rating', - font: ft.top_menu, - type: 'menu' - }, - Properties: { - ico: 'Properties', - font: ft.top_menu, - type: 'menu' - }, - Settings: { - ico: 'Settings', - font: ft.top_menu, - type: 'menu' - }, - Back: { - ico: '\uE00E', - type: 'backforward', - font: ft.symbol, - w: 22, - h: 22 - }, - Forward: { - ico: '\uE00F', - type: 'backforward', - font: ft.symbol, - w: 22, - h: 22 - } - }; - } catch (e) { - console.log('**********************************'); - console.log('ATTENTION: Buttons could not be created'); - console.log(`Make sure you installed the theme correctly to ${fb.ProfilePath}.`); - console.log('**********************************'); - } - - - btnImg = []; - - for (const i in btns) { - if (btns[i].type === 'menu') { - const img = gdi.CreateImage(100, 100); - const g = img.GetGraphics(); - const measurements = g.MeasureString(btns[i].ico, btns[i].font, 0, 0, 0, 0); - - btns[i].w = Math.ceil(measurements.Width + 20); - img.ReleaseGraphics(g); - btns[i].h = Math.ceil(measurements.Height + 5); - } - - if (btns[i].type === 'compact') { - const img = gdi.CreateImage(100, 100); - const g = img.GetGraphics(); - const measurements = g.MeasureString(btns[i].ico, btns[i].font, 0, 0, 0, 0); - - btns[i].w = Math.ceil(measurements.Width + (RES_4K ? 32 : 41)); - img.ReleaseGraphics(g); - btns[i].h = Math.ceil(measurements.Height + (RES_4K ? -2 : 5)); - } - - // const { x, y } = btns[i]; - let { w, h } = btns[i]; - const lineW = SCALE(2); - - if (RES_4K && btns[i].type === 'transport') { - w *= 2; - h *= 2; - } else if (RES_4K && btns[i].type !== 'menu') { - w = Math.round(btns[i].w * 1.5); - h = Math.round(btns[i].h * 1.6); - } else if (RES_4K) { - w += 20; - h += 10; - } - - const stateImages = []; // 0=ButtonState.Default, 1=hover, 2=down, 3=Enabled; - for (let state = 0; state < Object.keys(ButtonState).length; state++) { - const btn = btns[i]; - if (state === 3 && btn.type !== 'image') break; - const img = gdi.CreateImage(w, h); - const g = img.GetGraphics(); - g.SetSmoothingMode(SmoothingMode.AntiAlias); - // * Positions playback icons weirdly on AntiAliasGridFit - if (btns[i].type !== 'transport' && !pref.customThemeFonts) { - g.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); - } - // * Positions some top menu buttons weirdly when using custom theme fonts on AntiAliasGridFit and vertical/horizontal centered font alignment, i.e StringFormat(1, 1); - else if ((btns[i].type === 'menu' || btn.type === 'compact') && pref.customThemeFonts || btns[i].type === 'transport') { - g.SetTextRenderingHint(TextRenderingHint.AntiAlias); - } - - let menuTextColor = col.menuTextNormal; - let menuRectColor = col.menuRectNormal; - let menuBgColor = col.menuBgColor; - let transportIconColor = col.transportIconNormal; - let transportEllipseColor = col.transportEllipseNormal; - let iconAlpha = 255; - - switch (state) { - case ButtonState.Hovered: - menuTextColor = col.menuTextHovered; - menuRectColor = col.menuRectHovered; - menuBgColor = col.menuBgColor; - transportIconColor = col.transportIconHovered; - transportEllipseColor = col.transportEllipseHovered; - iconAlpha = 215; - break; - case ButtonState.Down: - menuTextColor = col.menuTextDown; - menuRectColor = col.menuRectDown; - menuBgColor = col.menuBgColor; - transportIconColor = col.transportIconDown; - transportEllipseColor = col.transportEllipseDown; - iconAlpha = 215; - break; - case ButtonState.Enabled: - iconAlpha = 255; - break; - } - - switch (btn.type) { - case 'menu': case 'window': case 'compact': - if (pref.styleTopMenuButtons === 'default' || pref.styleTopMenuButtons === 'filled') { - if (pref.styleTopMenuButtons === 'filled') state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 3, 3, menuBgColor); - state && g.DrawRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 3, 3, 1, menuRectColor); - } - else if (pref.styleTopMenuButtons === 'bevel') { - state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, menuBgColor); - state && FillGradRoundRect(g, Math.floor(lineW / 2), Math.floor(lineW / 2) + 1, w, h - 1, 4, 4, 90, 0, col.menuStyleBg, 1); - state && g.DrawRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, 1, menuRectColor); - } - else if (pref.styleTopMenuButtons === 'inner') { - state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, menuBgColor); - state && FillGradRoundRect(g, Math.floor(lineW / 2), Math.floor(lineW / 2) + 1, w, h - 1, 4, 4, 90, 0, col.menuStyleBg, 0); - state && g.DrawRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, 1, menuRectColor); - } - else if (pref.styleTopMenuButtons === 'emboss') { - state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, menuBgColor); - state && FillGradRoundRect(g, Math.floor(lineW / 2), Math.floor(lineW / 2) + 1, w, h - 1, 4, 4, 90, 0, col.menuStyleBg, 0.33); - state && g.DrawRoundRect(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 1, 4, 4, 1, col.menuRectStyleEmbossTop); - state && g.DrawRoundRect(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2), w - lineW - 2, h - lineW - 1, 4, 4, 1, col.menuRectStyleEmbossBottom); - } - if (btn.type === 'compact') { - g.DrawString('\uf0c9', ft.top_menu_compact, menuTextColor, RES_4K ? -39 : -19, 0, w, h, StringFormat(1, 1)); - g.DrawString(btn.ico, btn.font, menuTextColor, RES_4K ? 20 : 10, RES_4K ? -1 : 0, w, h, StringFormat(1, 1)); - } else { - g.DrawString(btn.ico, btn.font, menuTextColor, 0, 0, w, btn.type === 'window' ? h : h - 1, StringFormat(1, 1)); - } - break; - - case 'transport': - if (pref.styleTransportButtons === 'default') { - g.DrawEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 2, lineW, transportEllipseColor); - g.FillEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 2, col.transportEllipseBg); - } - else if (pref.styleTransportButtons === 'bevel') { - g.FillEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW - 1, h - lineW - 1, col.transportStyleTop); - g.DrawEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW - 1, h - lineW, 1, col.transportStyleBottom); - FillGradEllipse(g, Math.floor(lineW / 2) - 0.5, Math.floor(lineW / 2), w + 0.5, h + 0.5, 90, 0, col.transportStyleBg, 1); - } - else if (pref.styleTransportButtons === 'inner') { - g.FillEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW - 1, col.transportStyleTop); - g.DrawEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2) - 1, w - lineW, h - lineW + 1, 1, col.transportStyleBottom); - FillGradEllipse(g, Math.floor(lineW / 2) - 0.5, Math.floor(lineW / 2), w + 1.5, h + 0.5, 90, 0, col.transportStyleBg, 0); - } - else if (pref.styleTransportButtons === 'emboss') { - g.FillEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 2, col.transportEllipseBg); - FillGradEllipse(g, Math.floor(lineW / 2) + 2, Math.floor(lineW / 2) + 2, w - lineW - 2, h - lineW - 2, 90, 0, col.transportStyleBg, 0.33); - g.DrawEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 2, w - lineW - 2, h - lineW - 3, lineW, col.transportStyleTop); - g.DrawEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2), w - lineW - 2, h - lineW - 2, lineW, col.transportStyleBottom); - } - g.DrawString(btn.ico, btn.font, transportIconColor, 1, (i === 'Stop' || i === 'Reload') ? 0 : 1, w, h, StringFormat(1, 1)); - break; - - case 'backforward': - g.DrawString(btn.ico, btn.font, g_pl_colors.plman_text_hovered, i === 'Back' ? -1 : 0, 0, w, h, StringFormat(1, 1)); - break; - } - - img.ReleaseGraphics(g); - stateImages[state] = img; - } - - btnImg[i] = stateImages; - } - if (createButtonProfiler) createButtonProfiler.Print(); -} - - -/** - * Creates the top menu and lower bar transport buttons. - * @param {number} ww window.Width. - * @param {number} wh window.Height. - */ -function createButtonObjects(ww, wh) { - btns = []; - const menuFontSize = pref[`menuFontSize_${pref.layout}`]; - const showingMinMaxButtons = !!((UIHacks && UIHacks.FrameStyle)); - const showTransportControls = pref[`showTransportControls_${pref.layout}`]; - - if (ww <= 0 || wh <= 0) { - return; - } else if (typeof btnImg === 'undefined') { - createButtonImages(); - } - - // * TOP MENU BUTTONS * // - /** @type {GdiBitmap[]} */ - let img = btnImg.File; - const w = img[0].Width; - const h = img[0].Height; - let x = RES_4K ? 18 : 8; - const y = Math.round(geo.topMenuHeight * 0.5 - h * 0.5 - SCALE(1)); - - // Top menu font size X-correction for Artwork and Compact layout - const xOffset = ww > SCALE(pref.layout === 'compact' ? 570 : 620) ? 0 : - menuFontSize === 13 && !RES_QHD ? SCALE(3) : - menuFontSize === 14 && !RES_QHD ? SCALE(5) : - menuFontSize === 16 ? RES_QHD ? 4 : SCALE(12) : 0; - - const widthCorrection = - RES_4K ? (pref.customThemeFonts && menuFontSize > 12 && ww < 1080) ? 12 : (pref.customThemeFonts && menuFontSize > 10 && ww < 1080) ? 6 : 3 : - (pref.customThemeFonts && menuFontSize > 12 && ww < 600) ? 6 : (pref.customThemeFonts && menuFontSize > 10 && ww < 600) ? 4 : 0; - const correction = widthCorrection + (pref.layout !== 'default' ? xOffset : 0); - - // * Top menu compact - if (pref.showTopMenuCompact) { - img = btnImg.TopMenu; - btns[19] = new Button(x, y, w + SCALE(41), h, 'Menu', img, 'Open menu'); - } - - // * Default foobar2000 buttons - if (!pref.showTopMenuCompact) { - img = btnImg.File; - btns[20] = new Button(x, y, w, h, 'File', img); - } - - // These buttons are not available in Artwork layout - if (pref.layout !== 'artwork') { - x += img[0].Width - correction; - img = btnImg.Edit; - if (!pref.showTopMenuCompact) btns[21] = new Button(x, y, img[0].Width, h, 'Edit', img); - - x += img[0].Width - correction; - img = btnImg.View; - if (!pref.showTopMenuCompact) btns[22] = new Button(x, y, img[0].Width, h, 'View', img); - - x += img[0].Width - correction; - img = btnImg.Playback; - if (!pref.showTopMenuCompact) btns[23] = new Button(x, y, img[0].Width, h, 'Playback', img); - - x += img[0].Width - correction; - img = btnImg.MediaLibrary; - if (!pref.showTopMenuCompact) btns[24] = new Button(x, y, img[0].Width, h, 'Library', img); - - x += img[0].Width - correction; - img = btnImg.Help; - if (!pref.showTopMenuCompact) btns[25] = new Button(x, y, img[0].Width, h, 'Help', img); - - x += img[0].Width - correction; - img = btnImg.Playlists; - if (!pref.showTopMenuCompact) btns[26] = new Button(x, y, img[0].Width, h, 'Playlists', img); - } - - // * Theme buttons - const showPanelDetails = pref[`showPanelDetails_${pref.layout}`]; - const showPanelLibrary = pref[`showPanelLibrary_${pref.layout}`]; - const showPanelBiography = pref[`showPanelBiography_${pref.layout}`]; - const showPanelLyrics = pref[`showPanelLyrics_${pref.layout}`]; - const showPanelRating = pref[`showPanelRating_${pref.layout}`]; - - const buttonCount = (showPanelDetails ? 1 : 0) + (showPanelLibrary ? 1 : 0) + (showPanelBiography ? 1 : 0) + (showPanelLyrics ? 1 : 0) + (showPanelRating ? 1 : 0); - const buttonXCorr = 0.33 + (buttonCount === 5 ? 0 : buttonCount === 4 ? 0.3 : buttonCount === 3 ? 0.6 : buttonCount === 2 ? 1.5 : buttonCount === 1 ? 4 : 0); - - x += img[0].Width - widthCorrection; - if (pref.layout === 'artwork') x -= xOffset; - // Options button is available in all layouts - img = btnImg.Options; - if (!pref.showTopMenuCompact) btns[27] = new Button(x, y, img[0].Width, h, 'Options', img, 'Theme options'); - - // These buttons are not available in Compact layout - if (pref.layout !== 'compact') { - if (pref.topMenuAlignment === 'center' && ww > SCALE(pref.layout === 'artwork' ? 600 : 1380) || pref.showTopMenuCompact) { - const centerMenu = Math.ceil(w * (buttonCount + (pref.layout === 'artwork' && pref.topMenuCompact ? 0.5 : 0)) + (menuFontSize * buttonCount * buttonXCorr)); - x = Math.round(ww * 0.5 - centerMenu); - } - - if (showPanelDetails) { - x += img[0].Width - correction; - img = btnImg.Details; - btns.details = new Button(x, y, img[0].Width, h, 'Details', img, 'Display Details'); - - // Playlist button only available in Artwork layout - if (pref.layout === 'artwork') { - x += img[0].Width - correction; - img = btnImg.PlaylistArtworkLayout; - btns.playlistArtworkLayout = new Button(x, y, img[0].Width, h, 'PlaylistArtworkLayout', img, 'Display Playlist'); - } - } - if (showPanelLibrary) { - x += img[0].Width - correction; - img = btnImg.Library; - btns.library = new Button(x, y, img[0].Width, h, 'library', img, 'Display Library'); - } - if (showPanelBiography) { - x += img[0].Width - correction; - img = btnImg.Biography; - btns.biography = new Button(x, y, img[0].Width, h, 'Biography', img, 'Display Biography'); - } - if (showPanelLyrics) { - x += img[0].Width - correction; - img = btnImg.Lyrics; - btns.lyrics = new Button(x, y, img[0].Width, h, 'Lyrics', img, 'Display Lyrics'); - } - if (showPanelRating) { - x += img[0].Width - correction; - img = btnImg.Rating; - btns.rating = new Button(x, y, img[0].Width, h, 'Rating', img, 'Rate Song'); - } - } - - // * Top menu 🗕 🗖 ✖ caption buttons - if (showingMinMaxButtons) { - const hideClose = UIHacks.FrameStyle === FrameStyle.SmallCaption && UIHacks.FullScreen !== true; - - const w = SCALE(22); - const h = w; - const p = 3; - const x = ww - w * (hideClose ? 2 : 3) - p * (hideClose ? 1 : 2) - (RES_4K ? 21 : 14); - const y = Math.round(geo.topMenuHeight * 0.5 - h * 0.5 - SCALE(1)); - - if (pref.layout === 'default') { - btns.Minimize = new Button(x, y, w, h, 'Minimize', btnImg.Minimize); - btns.Maximize = new Button(x + w + p, y, w, h, 'Maximize', btnImg.Maximize); - if (!hideClose) { - btns.Close = new Button(x + (w + p) * 2, menuFontSize < 10 ? y + 1 : y, menuFontSize < 10 ? w - 1 : w, menuFontSize < 10 ? h - 1 : h, 'Close', btnImg.Close); - } - } - else { - btns.Minimize = new Button(x + w + p, y, w, h, 'Minimize', btnImg.Minimize); - if (!hideClose) { - btns[12] = new Button(x + (w + p) * 2, y, w, h, 'Close', btnImg.Close); - } - } - } - - // * LOWER BAR TRANSPORT BUTTONS * // - if (showTransportControls) { - const lowerBarFontSize = pref[`lowerBarFontSize_${pref.layout}`]; - const showPlaybackOrderBtn = pref[`showPlaybackOrderBtn_${pref.layout}`]; - const showReloadBtn = pref[`showReloadBtn_${pref.layout}`]; - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; - const transportBtnSize = pref[`transportButtonSize_${pref.layout}`]; - const transportBtnSpacing = pref[`transportButtonSpacing_${pref.layout}`]; - - let count = 4 + (showPlaybackOrderBtn ? 1 : 0) + (showReloadBtn ? 1 : 0) + (showVolumeBtn ? 1 : 0); - - const buttonSize = SCALE(transportBtnSize); - const y = wh - buttonSize - SCALE(pref.layout !== 'default' ? 36 : 78) + SCALE(lowerBarFontSize); - const w = buttonSize; - const h = w; - const p = SCALE(transportBtnSpacing); // Space between buttons - const x = (ww - w * count - p * (count - 1)) / 2; - - const calcX = (index) => x + (w + p) * index; - - count = 0; - btns.stop = new Button(x, y, w, h, 'Stop', btnImg.Stop, btnTransportTooltip('stop')); - btns.prev = new Button(calcX(++count), y, w, h, 'Previous', btnImg.Previous, btnTransportTooltip('prev')); - btns.play = new Button(calcX(++count), y, w, h, 'PlayPause', !fb.IsPlaying || fb.IsPaused ? btnImg.Play : btnImg.Pause, btnTransportTooltip('play')); - btns.next = new Button(calcX(++count), y, w, h, 'Next', btnImg.Next, btnTransportTooltip('next')); - - if (showPlaybackOrderBtn) { - switch (plman.PlaybackOrder) { - case 0: - btns.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', btnImg.PlaybackDefault); - break; - case 1: - btns.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', btnImg.PlaybackRepeatPlaylist); - break; - case 2: - btns.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', btnImg.PlaybackRepeatTrack); - break; - case 3: case 4: case 5: case 6: - btns.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', btnImg.PlaybackShuffle); - break; - } - } - if (showReloadBtn) { - btns.reload = new Button(calcX(++count), y, w, h, 'Reload', btnImg.Reload, btnTransportTooltip('reload')); - } - if (showVolumeBtn) { - btns.volume = new Button(calcX(++count), y, w, h, 'Volume', btnImg.ShowVolume); - volumeBtn.setPosition(btns.volume.x, y, w); - } - } -} - - -/** - * Loads country flags when defined in tags, displayed in the lower bar and Details. - */ -function loadCountryFlags() { - flagImgs = []; - for (const country of GetMetaValues(tf.artist_country)) { - const flagImage = loadFlagImage(country); - flagImage && flagImgs.push(flagImage); - } -} - - -/** - * Loads flag images from the image directory based on the country name or ISO country code provided. - * @param {string} country The country for which we want to load the flag image. - * @returns {GdiBitmap} The flag image object. - */ -function loadFlagImage(country) { - const countryName = (ConvertIsoCountryCodeToFull(country) || country).trim().replace(/ /g, '-'); // In case we have a 2-digit country code - const path = `${$($Escape(paths.flagsBase)) + (RES_4K ? '64\\' : '32\\') + countryName}.png`; - return gdi.Image(path); -} - - -////////////////////////// -// * MAIN - ALBUM ART * // -////////////////////////// -/** - * Scales album art to a global size, handling potential errors. - * @global albumArtScaled The scaled album art image. - * @global albumArtSize The dimensions to scale the image to. - * @throws Logs an error if the scaling operation fails. - */ -function createScaledAlbumArt() { - if (albumArtScaled) albumArtScaled = null; - - try { - // * Avoid weird anti-aliased scaling along border of images, see: https://stackoverflow.com/questions/4772273/interpolationmode-highqualitybicubic-introducing-artefacts-on-edge-of-resized-im - albumArtScaled = albumArt.Resize(albumArtSize.w, albumArtSize.h, InterpolationMode.Bicubic); // Old method -> albumArtScaled = albumArt.Resize(albumArtSize.w, albumArtSize.h); - const sg = albumArtScaled.GetGraphics(); - const HQscaled = albumArt.Resize(albumArtSize.w, albumArtSize.h, InterpolationMode.HighQualityBicubic); - sg.DrawImage(HQscaled, 2, 2, albumArtScaled.Width - 4, albumArtScaled.Height - 4, 2, 2, albumArtScaled.Width - 4, albumArtScaled.Height - 4); - albumArtScaled.ReleaseGraphics(sg); - } catch (e) { - noArtwork = true; - albumArt = null; - noAlbumArtStub = true; - albumArtSize = new ImageSize(0, geo.topMenuHeight, 0, 0); - console.log('\n\n'); - } -} - - -/** - * Creates cropped album art within max dimensions. - * @param {Object} albumArt The original album art with Width and Height. - * @param {number} maxWidth The max width for the art. - * @param {number} maxHeight The max height for the art. - * @returns {Object} The cropped image and its scale factor. - */ -function createCroppedAlbumArt(albumArt, maxWidth, maxHeight) { - const widthScale = maxWidth / albumArt.Width; - const heightScale = maxHeight / albumArt.Height; - const scaledWidth = albumArt.Width * heightScale; - const scaledHeight = albumArt.Height * widthScale; - - // * Fill the height and crop the width - if (scaledWidth >= maxWidth) { - const cropWidth = (scaledWidth - maxWidth) / 2; - return { - image: CropImage(albumArt, cropWidth, 0), - scale: heightScale - }; - } - // * Fill the width and crop the height - else if (scaledHeight >= maxHeight) { - const cropHeight = scaledHeight - maxHeight; - return { - image: CropImage(albumArt, 0, cropHeight), - scale: widthScale - }; - } - // * If no cropping is needed, return the original image and scale - return { - image: albumArt, - scale: Math.min(widthScale, heightScale) - }; -} - - -/** - * Displays the next artwork image when cycling through album artworks with a default 30 sec interval or when using album art context menu. - */ -function displayNextImage() { - DebugLog(`Repainting in displayNextImage: ${albumArtIndex}`); - albumArtIndex = (albumArtIndex + 1) % albumArtList.length; - loadImageFromAlbumArtList(albumArtIndex); - if (pref.theme === 'reborn' || pref.theme === 'random' || pref.styleBlackAndWhiteReborn || pref.styleBlackReborn) { - newTrackFetchingArtwork = true; - getThemeColors(albumArt); - initTheme(); - DebugLog('\n>>> initTheme -> displayNextImage <<<\n'); - } - lastLeftEdge = 0; - resizeArtwork(true); // Needed to readjust discArt shadow size if artwork size changes - RepaintWindow(); - albumArtTimeout = setTimeout(() => { - displayNextImage(); - }, settings.artworkDisplayTime * 1000); - initButtonState(); -} - - -/** - * Fetches new album art when a new album is being played or when cycling through album artworks. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ -function fetchAlbumArt(metadb) { - albumArtList = []; - - const fetchAlbumArtProfiler = timings.showDebugTiming && fb.CreateProfiler('fetchAlbumArt'); - - const autoRandomPreset = - (!['off', 'track'].includes(pref.presetAutoRandomMode) && pref.presetSelectMode === 'harmonic' || - pref.presetAutoRandomMode === 'dblclick' && pref.presetSelectMode === 'theme') && !doubleClicked; - - if (isStreaming || isPlayingCD) { - discArt = disposeDiscArt(discArt); - discArtCover = disposeDiscArt(discArtCover); - albumArt = utils.GetAlbumArtV2(metadb); - pref.showGridTitle_default = true; - pref.showGridTitle_artwork = true; - if (albumArt) { - getThemeColors(albumArt); - resizeArtwork(true); - } else { - noArtwork = true; - shadowImg = null; - } - initTheme(); - DebugLog('\n>>> initTheme -> fetchNewArtwork -> isStreaming || isPlayingCD <<<\n'); - } - else { - if (!pref.showGridTitle_default && pref.layout === 'default') pref.showGridTitle_default = false; - if (!pref.showGridTitle_artwork && pref.layout === 'artwork') pref.showGridTitle_artwork = false; - - albumArtList = globals.imgPaths.map(path => utils.Glob($(path), FileAttributes.Directory | FileAttributes.Hidden)).flat(); - const filteredFileTypes = pref.filterDiscJpgsFromAlbumArt ? '(png|jpg)' : 'png'; - const pattern = new RegExp(`(cd|disc|vinyl|${settings.discArtBasename})([0-9]*|[a-h]).${filteredFileTypes}`, 'i'); - const imageType = /(jpg|png)$/i; - // * Remove duplicates and cd/vinyl art and make sure all files are jpg or pngs - albumArtList = [...new Set(albumArtList)].filter(path => !pattern.test(path) && imageType.test(path)); - - // * Try loading album art from artwork image paths - if (albumArtList.length && !pref.loadEmbeddedAlbumArtFirst) { - noArtwork = false; - noAlbumArtStub = false; - embeddedArt = false; - if (albumArtList.length > 1 && pref.cycleArt) { - albumArtTimeout = setTimeout(() => { - displayNextImage(); - }, settings.artworkDisplayTime * 1000); - } - albumArtIndex = 0; - loadImageFromAlbumArtList(albumArtIndex); // Display first image - } - // * If not found, try embedded artwork from music file - else if (metadb && (albumArt = utils.GetAlbumArtV2(metadb))) { - discArtCover = artCache.encache(utils.GetAlbumArtV2(metadb), albumArtList[metadb], 2); - noArtwork = false; - noAlbumArtStub = false; - if (autoRandomPreset) { // Prevent double initialization for theme presets to save performance, getThemeColors() and initTheme() already handled in getRandomThemePreset() - setRandomThemePreset(); - } else { - initThemeTags(); - getThemeColors(albumArt); - if (!loadingTheme) { - initTheme(); // * Prevent incorrect theme brightness at startup/reload when using embedded art - DebugLog('\n>>> initTheme -> fetchNewArtwork -> embeddedArt <<<\n'); - } - } - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } else { - resizeArtwork(true); - } - embeddedArt = true; - } - // * No album art found, using noAlbumArtStub - else { - noArtwork = true; - noAlbumArtStub = true; - albumArt = null; - discArtCover = null; - initTheme(); - DebugLog('\n>>> initTheme -> fetchNewArtwork -> noAlbumArtStub <<<\n'); - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } else { - resizeArtwork(true); - } - DebugLog('Repainting on_playback_new_track due to no cover image'); - RepaintWindow(); - } - } - - if (fetchAlbumArtProfiler) fetchAlbumArtProfiler.Print(); -} - - -/** - * Fetches new album art/disc art when a new album is being played, disc art has changed or when cycling through album artworks. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ -function fetchNewArtwork(metadb) { - if (pref.presetAutoRandomMode === 'album' || pref.presetSelectMode === 'harmonic') initThemeSkip = true; - fetchAlbumArt(metadb); - fetchDiscArt(); -} - - -/** - * Loads an image from the albumArtList array. - * @param {number} index The index of albumArtList signifying which image to load. - */ -function loadImageFromAlbumArtList(index) { - const metadb = fb.GetNowPlaying(); - const tempAlbumArt = artCache && artCache.getImage(albumArtList[index]); - const tempDiscArtCover = artCache && artCache.getImage(albumArtList[index], 2); - - const autoRandomPreset = - (!['off', 'track'].includes(pref.presetAutoRandomMode) && pref.presetSelectMode === 'harmonic' || - pref.presetAutoRandomMode === 'dblclick' && pref.presetSelectMode === 'theme') && !doubleClicked; - - const hasThemeTags = $('[%GR_THEME%]') || $('[%GR_STYLE%]') || $('[%GR_PRESET%]'); - - if (tempAlbumArt) { - albumArt = tempAlbumArt; - discArtCover = tempDiscArtCover; - albumArtCopy = albumArt; - - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - if (index !== 0 && !newTrackFetchingArtwork) return; - newTrackFetchingArtwork = false; - - if (autoRandomPreset) { // Prevent double initialization for theme presets to save performance, getThemeColors() and initTheme() already handled in getRandomThemePreset() - setRandomThemePreset(); - } else { - initThemeTags(); - getThemeColors(albumArt); - if (!initThemeSkip && !hasThemeTags) { - initTheme(); - DebugLog('\n>>> initTheme -> loadImageFromAlbumArtList -> tempAlbumArt <<<\n'); - } - } - } - else { - gdi.LoadImageAsyncV2(window.ID, albumArtList[index]).then(coverImage => { - albumArt = artCache.encache(coverImage, albumArtList[index]); - discArtCover = artCache.encache(coverImage, albumArtList[index], 2); - if (newTrackFetchingArtwork) { - if (!albumArt && fb.IsPlaying) { - // * If no album art on disk can be found, try embedded artwork from music file - if (metadb && (albumArt = utils.GetAlbumArtV2(metadb))) { - discArtCover = artCache.encache(utils.GetAlbumArtV2(metadb), albumArtList[index], 2); - noArtwork = false; - noAlbumArtStub = false; - embeddedArt = true; - } - // * Use noAlbumArtStub if album art could not be properly parsed - else { - noArtwork = true; - noAlbumArtStub = true; - embeddedArt = false; - console.log('\n\n'); - } - } - - if (autoRandomPreset) { // Prevent double initialization for theme presets to save performance, getThemeColors() and initTheme() already handled in getRandomThemePreset() - setRandomThemePreset(); - } else { - initThemeTags(); - getThemeColors(albumArt); - if (!hasThemeTags) { - initTheme(); - DebugLog('\n>>> initTheme -> loadImageFromAlbumArtList -> LoadImageAsyncV2 <<<\n'); - } - } - - newTrackFetchingArtwork = false; - } - - albumArtCopy = albumArt; - - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } else { - resizeArtwork(true); - } - - if (discArt) createDiscArtRotation(); - lastLeftEdge = 0; // Recalc label location - RepaintWindow(); - }); - } - - if (!displayLibrarySplit()) resizeArtwork(false); // Recalculate image positions - if (discArt) createDiscArtRotation(); -} - - -/** - * Resizes loaded artwork to have better drawing performance and resets its position. - * Also resets the size and position of the pause button and lyrics. - * @param {boolean} resetDiscArtPosition Whether the position of the disc art should be reset. - */ -function resizeArtwork(resetDiscArtPosition) { - DebugLog('Resizing artwork'); - hasArtwork = false; - resizeAlbumArt(); - resizeDiscArt(resetDiscArtPosition); - resetPausePosition(); - resetLyricsPosition(); -} - - -/** - * Resizes and resets the size and position of the album art. - * Accounts for different window states and user preferences to calculate - * the appropriate size and position of the album artwork. - */ -function resizeAlbumArt() { - if (!albumArt || !albumArt.Width || !albumArt.Height) { - albumArtSize = new ImageSize(0, geo.topMenuHeight, 0, 0); - return; - } - - // * Set album scale - const windowFullscreenOrMaximized = UIHacks.FullScreen || UIHacks.MainWindowState === WindowState.Maximized; - const aspectRatioInBounds = !pref.albumArtAspectRatioLimit || (albumArt.Width < albumArt.Height * pref.albumArtAspectRatioLimit) && (albumArt.Height < albumArt.Width * pref.albumArtAspectRatioLimit); - const albumArtCropped = pref.albumArtScale === 'cropped' && windowFullscreenOrMaximized && aspectRatioInBounds && (displayPlaylist || displayLibrary); - const albumArtStretched = pref.albumArtScale === 'stretched' && windowFullscreenOrMaximized && aspectRatioInBounds && (displayPlaylist || displayLibrary); - const albumArtMaxWidth = ww * 0.5; - const albumArtMaxHeight = wh - geo.topMenuHeight - geo.lowerBarHeight; - const albumArtScaleFactor = displayPlaylist || displayLibrary ? 0.5 : 0.75; - const albumArtScaleDefault = Math.min(ww * albumArtScaleFactor / albumArt.Width, albumArtMaxHeight / albumArt.Height); - const albumArtScaleArtwork = Math.min(ww / albumArt.Width, albumArtMaxHeight / albumArt.Height); - let albumArtScale = pref.layout === 'artwork' ? albumArtScaleArtwork : albumArtScaleDefault; - - // * Set album art width, height and proportions - if (albumArtCropped) { - const { image, scale } = createCroppedAlbumArt(albumArt, albumArtMaxWidth, albumArtMaxHeight); - albumArtCopy = image; - albumArtScale = scale; - albumArtSize.w = Math.floor(albumArtCopy.Width * albumArtScale); - albumArtSize.h = Math.floor(albumArtCopy.Height * albumArtScale); - } else if (albumArtStretched) { - albumArtCopy = null; - albumArtSize.w = albumArtMaxWidth; - albumArtSize.h = albumArtMaxHeight; - } else { // Restore original proportional album art image - albumArtCopy = null; - albumArtSize.w = Math.floor(albumArt.Width * albumArtScale); - albumArtSize.h = Math.floor(albumArt.Height * albumArtScale); - } - - // * Set xCenter position - let xCenter = ww * 0.5; - artOffCenter = false; - if (displayPlaylist || displayLibrary) { - xCenter = pref.layout === 'artwork' ? 0 : ww * 0.25; - } else if (albumArtScale === ww * 0.75 / albumArt.Width) { - xCenter = Math.round(ww * 0.66 - SCALE(40)); // xCenter += ww * 0.1; - artOffCenter = true; - } - - // * Set album art x-coordinate - switch (pref.layout) { - case 'default': // In a non-proportional player size, 'pref.albumArtAlign' sets album art alignment in Default layout - if (displayPlaylist || displayLibrary) { - switch (pref.albumArtAlign) { - case 'left': - albumArtSize.x = Math.round(Math.min(0, ww * 0.5 - albumArtSize.w)); - break; - case 'leftMargin': - albumArtSize.x = Math.round(Math.min(ww / wh > 1.8 ? SCALE(40) : 0, ww * 0.5 - albumArtSize.w)); - break; - case 'center': - albumArtSize.x = Math.round(Math.min(xCenter - 0.5 * albumArtSize.w, ww * 0.5 - albumArtSize.w)); - break; - default: - albumArtSize.x = Math.round(ww * 0.5 - albumArtSize.w); - break; - } - } else { - albumArtSize.x = Math.round(xCenter - 0.5 * albumArtSize.w); - } - break; - - case 'artwork': // And is always centered in Artwork layout - albumArtSize.x = Math.round(!displayPlaylist || pref.displayLyrics ? ww * 0.5 - albumArtSize.w * 0.5 : ww); - break; - } - - // * Set album art y-coordinate - const restrictedWidth = albumArtScale !== (wh - geo.topMenuHeight - geo.lowerBarHeight) / albumArt.Height; - const centerY = Math.floor(((wh - geo.lowerBarHeight + geo.topMenuHeight) / 2) - albumArtSize.h / 2); - albumArtSize.y = restrictedWidth ? Math.min(centerY, SCALE(150) + 10) : geo.topMenuHeight; - - createScaledAlbumArt(); - hasArtwork = true; -} - - -/** - * Resets the size and position of the pause button. - */ -function resetPausePosition() { - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; - const displayDetails = (pref.layout === 'artwork' ? displayPlaylist : !displayPlaylist) && !displayLibrary && !displayBiography; - const windowFullscreenOrMaximized = UIHacks.FullScreen || UIHacks.MainWindowState === WindowState.Maximized; - - const albumArtPauseBtnX = windowFullscreenOrMaximized ? ww * 0.25 : albumArtSize.x + albumArtSize.w * 0.5; - const albumArtPauseBtnY = albumArtSize.y + albumArtSize.h * 0.5; - const discArtPauseBtnX = discArtSize.x + discArtSize.w * 0.5; - const discArtPauseBtnY = discArtSize.y + discArtSize.h * 0.5; - - const noAlbumArtPauseBtnX = - !pref.panelWidthAuto && pref.layout !== 'artwork' && !noAlbumArtStub && (displayPlaylist || displayLibrary) || - pref.layout === 'artwork' || displayDetails || pref.lyricsLayout === 'full' && pref.displayLyrics ? ww * 0.5 : - pref.panelWidthAuto ? - pref.albumArtAlign === 'left' ? noAlbumArtSize * 0.5 : - pref.albumArtAlign === 'leftMargin' ? ww / wh > 1.8 ? noAlbumArtSize * 0.5 + SCALE(40) : 0 : - pref.albumArtAlign === 'center' ? Math.floor(ww * 0.5 - noAlbumArtSize * 0.5 - (ww * 0.25 - noAlbumArtSize * 0.5)) : - ww * 0.5 - noAlbumArtSize * 0.5 : - ww * 0.25; - - const noAlbumArtPauseBtnY = wh * 0.5 - geo.topMenuHeight; - - if (albumArt) pauseBtn.setCoords(albumArtPauseBtnX, albumArtPauseBtnY); - else if (discArt) pauseBtn.setCoords(discArtPauseBtnX, discArtPauseBtnY); - else if (noAlbumArtStub) pauseBtn.setCoords(noAlbumArtPauseBtnX, noAlbumArtPauseBtnY); -} - - -//////////////////////////// -// * DETAILS - DISC ART * // -//////////////////////////// -/** - * Creates and masks an image to the disc art. - * @param {GdiGraphics} gr - * @param {number} x The X-coordinate of the disc area. - * @param {number} y The Y-coordinate of the disc area. - * @param {number} w The width of the mask. - * @param {number} h The height of the mask. - * @param {number} srcX The X-coordinate of the source image. - * @param {number} srcY The Y-coordinate of the source image. - * @param {number} srcW The width of the source image. - * @param {number} srcH The height of the source image. - * @param {float=} angle The angle of the mask in degrees. Default 0. - * @param {number=} alpha The alpha of the mask. Values 0-255. - * @returns {GdiGraphics} The rounded masked image. - */ -function createDiscArtAlbumArtMask(gr, x, y, w, h, srcX, srcY, srcW, srcH, angle, alpha) { - // * First draw album art in the background - gr.DrawImage(albumArtScaled, x, y, w, h, 0, 0, w, h, 0, alpha); - - // * Mask - const maskImg = gdi.CreateImage(w, h); - let g = maskImg.GetGraphics(); - g.FillEllipse(discArtSize.x - albumArtSize.x + geo.discArtShadow - SCALE(4), discArtSize.y - albumArtSize.y + SCALE(2), - discArtSize.w - geo.discArtShadow + SCALE(4), discArtSize.h - geo.discArtShadow + SCALE(2), 0xffffffff); - maskImg.ReleaseGraphics(g); - - // * Album art - const albumArtImg = gdi.CreateImage(w, h); - g = albumArtImg.GetGraphics(); - g.DrawImage(albumArtScaled, 0, 0, w, h, 0, 0, albumArtScaled.Width, albumArtScaled.Height); - albumArtImg.ReleaseGraphics(g); - - const mask = maskImg.Resize(w, h); - albumArtImg.ApplyMask(mask); - - gr.DrawImage(albumArtImg, x, y, w, h, 0, 0, w, h, 0, 255); -} - - -/** - * Creates the album cover mask for the disc art stub. - * @param {GdiBitmap} img The image to apply the mask to. - * @param {number} w The width of the mask. - * @param {number} h The height of the mask. - */ -function createDiscArtCoverMask(img, w, h) { - const mask = GR(discArtSize.w, discArtSize.h, true, g => { - const lw = SCALE(25); - const innerRingSize = Math.round(discArtSize.h * 0.666 + lw * 0.5); - const innerCenterX = Math.round(discArtSize.w * 0.5); - const innerCenterY = Math.round(discArtSize.h * 0.5); - const innerRadiusX = Math.round(discArtSize.w * 0.5 - innerRingSize * 0.5); - const innerRadiusY = Math.round(discArtSize.h * 0.5 - innerRingSize * 0.5); - - g.SetSmoothingMode(SmoothingMode.AntiAlias); - g.FillSolidRect(0, 0, discArtSize.w, discArtSize.h, RGB(255, 255, 255)); - g.FillEllipse(lw * 0.5, lw * 0.5, discArtSize.w - lw, discArtSize.h - lw, RGB(0, 0, 0)); // Outer ring - g.FillEllipse(innerCenterX - innerRadiusX, innerCenterY - innerRadiusY, innerRadiusX * 2, innerRadiusY * 2, RGB(255, 255, 255)); // Inner ring - }); - - img.ApplyMask(mask.Resize(w, h)); -} - - -/** - * Creates the disc art rotation animation with RotateImg(). - */ -function createDiscArtRotation() { - // Drawing discArt rotated is slow, so first draw it rotated into the discArtRotation image, and then draw discArtRotation image unrotated in on_paint. - if (pref.displayDiscArt && (discArt && discArtSize.w > 0)) { - let tracknum = parseInt(fb.TitleFormat(`$num($if(${tf.vinyl_tracknum},$sub($mul(${tf.vinyl_tracknum},2),1),$if2(%tracknumber%,1)),1)`).Eval()) - 1; - if (!pref.rotateDiscArt || Number.isNaN(tracknum)) tracknum = 0; // Avoid NaN issues when changing tracks rapidly - - discArtRotation = RotateImg(discArt, discArtSize.w, discArtSize.h, tracknum * pref.rotationAmt); - if (['cdAlbumCover', 'vinylAlbumCover'].includes(pref.discArtStub) && discArtCover && (!pref.noDiscArtStub || pref.showDiscArtStub)) { - createDiscArtCoverMask(discArtCover, discArtCover.Width, discArtCover.Height); - discArtRotationCover = RotateImg(discArtCover, discArtSize.w, discArtSize.h, tracknum * pref.rotationAmt); - } - } - - // TODO: Once spinning art is done, scrap this and the rotation amount crap and just use indexes into the discArtArray when needed. - // ? IDEA: Smooth rotation to new position? - return discArtRotation; -} - - -/** - * Creates the drop shadow for disc art. - */ -function createDiscArtShadow() { - const discArtShadowProfiler = timings.showDebugTiming && fb.CreateProfiler('createDiscArtShadow'); - if ((albumArt && albumArtSize.w > 0) || (discArt && pref.displayDiscArt && discArtSize.w > 0)) { - const discArtMargin = SCALE(2); - shadowImg = discArt && !displayPlaylist && !displayLibrary && pref.displayDiscArt ? - gdi.CreateImage(discArtSize.x + discArtSize.w + 2 * geo.discArtShadow, discArtSize.h + discArtMargin + 2 * geo.discArtShadow) : - gdi.CreateImage(albumArtSize.x + albumArtSize.w + 2 * geo.discArtShadow, albumArtSize.h + 2 * geo.discArtShadow); - if (pref.layout === 'default' && shadowImg) { - const shimg = shadowImg.GetGraphics(); - // if (albumArt && !displayBiography) { - // shimg.FillRoundRect(geo.discArtShadow, geo.discArtShadow, albumArtSize.x + albumArtSize.w, albumArtSize.h, - // 0.5 * geo.discArtShadow, 0.5 * geo.discArtShadow, col.shadow); - // } - - if (discArt && pref.displayDiscArt && !displayPlaylist && !displayLibrary) { - const offset = discArtSize.w * 0.40; // Don't change this value - const xVal = discArtSize.x; - const shadowOffset = geo.discArtShadow * 2; - - shimg.DrawEllipse(xVal + shadowOffset, shadowOffset + discArtMargin, discArtSize.w - shadowOffset, discArtSize.w - shadowOffset, geo.discArtShadow * 2, col.discArtShadow); // outer shadow - shimg.DrawEllipse(xVal + geo.discArtShadow + offset, offset + geo.discArtShadow + discArtMargin, discArtSize.w - offset * 2, discArtSize.h - offset * 2, 60, col.discArtShadow); // inner shadow - } - shadowImg.ReleaseGraphics(shimg); - shadowImg.StackBlur(geo.discArtShadow); - } - } - - if (discArtShadowProfiler) discArtShadowProfiler.Print(); -} - - -/** - * Disposes the disc art image when changing or deactivating disc art. - * @param {GdiBitmap} discArtImg The loaded disc art image. - */ -function disposeDiscArt(discArtImg) { - discArtSize = new ImageSize(0, 0, 0, 0); - discArtImg = null; - return null; -} - - -/** - * Fetches new disc art when a new album is being played. - */ -function fetchDiscArt() { - const fetchDiscArtProfiler = timings.showDebugTiming && fb.CreateProfiler('fetchDiscArt'); - - const discArtAllPaths = [ - $(pref.cdartdisc_path), // Root -> cd%discnumber%.png - $(pref.cdartdisc_path_artwork_root), // Root Artwork -> cd%discnumber%.png - $(pref.cdartdisc_path_images_root), // Root Images -> cd%discnumber%.png - $(pref.cdartdisc_path_scans_root), // Root Scans -> cd%discnumber%.png - $(pref.cdartdisc_path_artwork), // Subfolder Artwork -> cd%discnumber%.png - $(pref.cdartdisc_path_images), // Subfolder Images -> cd%discnumber%.png - $(pref.cdartdisc_path_scans), // Subfolder Scans -> cd%discnumber%.png - $(pref.cdart_path), // Root -> cd.png - $(pref.cdart_path_artwork_root), // Root Artwork -> cd.png - $(pref.cdart_path_images_root), // Root Images -> cd.png - $(pref.cdart_path_scans_root), // Root Scans -> cd.png - $(pref.cdart_path_artwork), // Subfolder Artwork -> cd.png - $(pref.cdart_path_images), // Subfolder Images -> cd.png - $(pref.cdart_path_scans), // Subfolder Scans -> cd.png - $(pref.vinylside_path), // Root -> vinyl side - $(pref.vinylside_path_artwork_root), // Root Artwork -> vinyl%vinyl disc%.png - $(pref.vinylside_path_images_root), // Root Images -> vinyl%vinyl disc%.png - $(pref.vinylside_path_scans_root), // Root Scans -> vinyl%vinyl disc%.png - $(pref.vinylside_path_artwork), // Subfolder Artwork -> vinyl%vinyl disc%.png - $(pref.vinylside_path_images), // Subfolder Images -> vinyl%vinyl disc%.png - $(pref.vinylside_path_scans), // Subfolder Scans -> vinyl%vinyl disc%.png - $(pref.vinyl_path), // Root -> vinyl.png - $(pref.vinyl_path_artwork_root), // Root Artwork -> vinyl.png - $(pref.vinyl_path_images_root), // Root Images -> vinyl.png - $(pref.vinyl_path_scans_root), // Root Scans -> vinyl.png - $(pref.vinyl_path_artwork), // Subfolder Artwork -> vinyl.png - $(pref.vinyl_path_images), // Subfolder Images -> vinyl.png - $(pref.vinyl_path_scans) // Subfolder Scans -> vinyl.png - ]; - - const discArtStubPaths = { - cdAlbumCover: paths.cdArtTransStub, - cdWhite: paths.cdArtWhiteStub, - cdBlack: paths.cdArtBlackStub, - cdBlank: paths.cdArtBlankStub, - cdTrans: paths.cdArtTransStub, - vinylAlbumCover: paths.vinylArtBlackHoleStub, - vinylWhite: paths.vinylArtWhiteStub, - vinylVoid: paths.vinylArtVoidStub, - vinylColdFusion: paths.vinylArtColdFusionStub, - vinylRingOfFire: paths.vinylArtRingOfFireStub, - vinylMaple: paths.vinylArtMapleStub, - vinylBlack: paths.vinylArtBlackStub, - vinylBlackHole: paths.vinylArtBlackHoleStub, - vinylEbony: paths.vinylArtEbonyStub, - vinylTrans: paths.vinylArtTransStub - }; - - let discArtPath; - let tempDiscArt; - - if (pref.displayDiscArt && !isStreaming) { // We must attempt to load CD/vinyl art first so that the shadow is drawn correctly - if (pref.noDiscArtStub || pref.showDiscArtStub) { - // * Search for disc art - for (const path of discArtAllPaths) { - if (IsFile(path)) { - discArtFound = true; - discArtPath = path; - } - } - } - - // * No disc art found, display custom disc art stubs - if (!discArtPath && (!pref.noDiscArtStub || pref.showDiscArtStub)) { - discArtFound = false; - discArtPath = Object.prototype.hasOwnProperty.call(discArtStubPaths, pref.discArtStub) ? discArtStubPaths[pref.discArtStub] : paths.discArtCustomStub; - } - - // * Load disc art - if (loadFromCache) { - tempDiscArt = artCache && artCache.getImage(discArtPath); - } - if (tempDiscArt) { - disposeDiscArt(discArt); - discArt = tempDiscArt; - resizeArtwork(true); - createDiscArtRotation(); - if (pref.spinDiscArt) { - discArtArray = []; // Clear last image - setDiscArtRotationTimer(); - } - } - else { - gdi.LoadImageAsyncV2(window.ID, discArtPath).then(discArtImg => { - disposeDiscArt(discArt); // Delay disposal so we don't get flashing - discArt = artCache.encache(discArtImg, discArtPath); - resizeArtwork(true); - createDiscArtRotation(); - if (pref.spinDiscArt) { - discArtArray = []; // Clear last image - setDiscArtRotationTimer(); - } - lastLeftEdge = 0; // Recalc label location - RepaintWindow(); - }); - } - } - - if (fetchDiscArtProfiler) fetchDiscArtProfiler.Print(); -} - - -/** - * Resizes and resets the size and position of the disc art. - * @param {boolean} resetDiscArtPosition Whether the position of the disc art should be reset. - */ -function resizeDiscArt(resetDiscArtPosition) { - if (!discArt) { - discArtSize = new ImageSize(0, 0, 0, 0); - return; - } - - const discArtSizeCorr = SCALE(4); - const discArtMargin = SCALE(2); - const discArtMarginRight = SCALE(36); - const discArtMaxHeight = wh - geo.topMenuHeight - geo.lowerBarHeight; - const discScaleFactor = displayPlaylist || displayLibrary ? 0.5 : 0.75; - const discScale = Math.min(ww * discScaleFactor / discArt.Width, (discArtMaxHeight - SCALE(16)) / discArt.Height); - - if (hasArtwork) { - if (resetDiscArtPosition) { - discArtSize.x = - ww - (albumArtSize.x + albumArtSize.w) < albumArtSize.h * pref.discArtDisplayAmount ? Math.floor(ww - albumArtSize.h - discArtMarginRight) : - pref.discArtDisplayAmount === 1 ? Math.floor(ww - albumArtSize.h - discArtMarginRight) : - pref.discArtDisplayAmount === 0.5 ? Math.floor(Math.min(ww - albumArtSize.h - discArtMarginRight, - albumArtSize.x + albumArtSize.w - (albumArtSize.h - 4) * (1 - pref.discArtDisplayAmount) - (pref.discArtDisplayAmount === 1 || pref.discArtDisplayAmount === 0.5 ? 0 : discArtMarginRight))) : - Math.floor(albumArtSize.x + albumArtSize.w - (albumArtSize.h - discArtSizeCorr) * (1 - pref.discArtDisplayAmount) - discArtMarginRight); - - discArtSize.y = albumArtSize.y + discArtMargin; - discArtSize.w = albumArtSize.h - discArtSizeCorr; // Disc art must be square so use the height of album art for width of discArt - discArtSize.h = discArtSize.w; - } else { // When disc art moves because folder images are different sizes we want to push it outwards, but not move it back in so it jumps around less - discArtSize.x = Math.max(discArtSize.x, Math.floor(Math.min(ww - albumArtSize.h - discArtMarginRight, - albumArtSize.x + albumArtSize.w - (albumArtSize.h - 4) * (1 - pref.discArtDisplayAmount) - (pref.discArtDisplayAmount === 1 || pref.discArtDisplayAmount === 0.5 ? 0 : discArtMarginRight)))); - - discArtSize.y = discArtSize.y > 0 ? Math.min(discArtSize.y, albumArtSize.y + discArtMargin) : albumArtSize.y + discArtMargin; - discArtSize.w = Math.max(discArtSize.w, albumArtSize.h - discArtSizeCorr); - discArtSize.h = discArtSize.w; - if (discArtSize.x + discArtSize.w > ww) { - discArtSize.x = ww - discArtSize.w - discArtMarginRight; - } - } - } - else { // * No album art so we need to calc size of disc - let xCenter = ww * 0.5; - artOffCenter = false; - if (displayPlaylist || displayLibrary) { - xCenter = ww * 0.25; - } else if (discScale === ww * 0.75 / discArt.Width) { - xCenter = Math.round(ww * 0.66 - SCALE(40)); - artOffCenter = true; - } - - // Need to -4 from height and add 2 to y to avoid skipping discArt drawing - not sure this is needed - discArtSize.w = Math.floor(discArt.Width * discScale) - discArtSizeCorr; - discArtSize.h = discArtSize.w; - discArtSize.x = Math.floor(xCenter - discArtSize.w * 0.5); - - // * Set disc art y-coordinate - const restrictedWidth = discScale !== (discArtMaxHeight - SCALE(16)) / discArt.Height; - const centerY = geo.topMenuHeight + Math.floor(((discArtMaxHeight - SCALE(16)) / 2) - discArtSize.h / 2); - discArtSize.y = restrictedWidth ? Math.min(centerY, 160) : geo.topMenuHeight + discArtMargin; - - hasArtwork = true; - } - - if ((hasArtwork || noAlbumArtStub) && (discArt && pref.displayDiscArt && !displayPlaylist && !displayLibrary && pref.layout !== 'compact')) { - createDiscArtShadow(); - } -} - - -/** - * Sets the disc art timer with different set interval values for rotating the disc art. - */ -function setDiscArtRotationTimer() { - clearInterval(discArtRotationTimer); - if (pref.layout === 'default' && discArt && fb.IsPlaying && !fb.IsPaused && pref.displayDiscArt && pref.spinDiscArt && !displayPlaylist && !displayLibrary && !displayBiography) { - console.log(`creating ${pref.spinDiscArtImageCount} rotated disc images, shown every ${pref.spinDiscArtRedrawInterval}ms`); - discArtRotationTimer = setInterval(() => { - discArtRotationIndex++; - discArtRotationIndex %= pref.spinDiscArtImageCount; - discArtRotationIndexCover++; - discArtRotationIndexCover %= pref.spinDiscArtImageCount; - - if (!discArtArray[discArtRotationIndex] && discArt && discArtSize.w) { - DebugLog(`creating discArtImg: ${discArtRotationIndex} (${discArtSize.w}x${discArtSize.h}) with rotation: ${360 / pref.spinDiscArtImageCount * discArtRotationIndex} degrees`); - discArtArray[discArtRotationIndex] = RotateImg(discArt, discArtSize.w, discArtSize.h, 360 / pref.spinDiscArtImageCount * discArtRotationIndex); - if (['cdAlbumCover', 'vinylAlbumCover'].includes(pref.discArtStub) && discArtCover && (!pref.noDiscArtStub || pref.showDiscArtStub)) { - discArtArrayCover[discArtRotationIndexCover] = RotateImg(discArtCover, discArtSize.w, discArtSize.h, 360 / pref.spinDiscArtImageCount * discArtRotationIndexCover); - } - } - - // The first line of discArtImg that will be drawn - const discArtLeftEdge = pref.detailsAlbumArtOpacity !== 255 || pref.detailsAlbumArtDiscAreaOpacity !== 255 || pref.discArtOnTop ? discArtSize.x : albumArtSize.x + albumArtSize.w - 1; - window.RepaintRect(discArtLeftEdge, discArtSize.y, discArtSize.w - (discArtLeftEdge - discArtSize.x), discArtSize.h, !pref.discArtOnTop && !pref.displayLyrics); - }, pref.spinDiscArtRedrawInterval); - } -} - - -///////////////////////////////////// -// * DETAILS - BAND & LABEL LOGO * // -///////////////////////////////////// -/** - * Checks if a band logo exists at various paths. - * @param {string} bandStr The name of the band. - * @returns {string} The path of the band logo if it exists. - */ -function checkBandLogo(bandStr) { - // See if artist logo exists at various paths - const testBandLogoPath = (imgDir, name) => { - if (name) { - const logoPath = `${imgDir + name}.png`; - if (IsFile(logoPath)) { - console.log(`Found band logo: ${logoPath}`); - return logoPath; - } - } - return false; - }; - - return testBandLogoPath(paths.artistlogos, bandStr) || // Try 800x310 white - testBandLogoPath(paths.artistlogosColor, bandStr); // Try 800x310 color -} - - -/** - * Gets the band logo and its inverted version based on the current playing album artist in Details. - */ -function getBandLogo() { - bandLogo = null; - invertedBandLogo = null; - let path; - let tryArtistList = [ - ...GetMetaValues('%album artist%').map(artist => ReplaceFileChars(artist)), - ...GetMetaValues('%album artist%').map(artist => ReplaceFileChars(artist).replace(/^[Tt]he /, '')), - ReplaceFileChars($('[%track artist%]')), - ...GetMetaValues('%artist%').map(artist => ReplaceFileChars(artist)), - ...GetMetaValues('%artist%').map(artist => ReplaceFileChars(artist).replace(/^[Tt]he /, '')) - ]; - - tryArtistList = [...new Set(tryArtistList)]; - tryArtistList.some(artistString => { - path = checkBandLogo(artistString); - return path; - }); - - if (!path) return; - - bandLogo = artCache.getImage(path); - if (!bandLogo) { - const logo = gdi.Image(path); - if (logo) { - bandLogo = artCache.encache(logo, path); - invertedBandLogo = artCache.encache(logo.InvertColours(), `${path}-inv`); - } - } - - invertedBandLogo = artCache.getImage(`${path}-inv`); - if (!invertedBandLogo && bandLogo) { - invertedBandLogo = artCache.encache(bandLogo.InvertColours(), `${path}-inv`); - } -} - - -/** - * Gets label logos based on current playing album artist in Details. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ -function getLabelLogo(metadb) { - let labelStrings = []; - recordLabels = []; // Will free memory from earlier loaded record label images - recordLabelsInverted = []; - - for (const label of globals.labels) { - labelStrings.push(...GetMetaValues(label, metadb)); - } - labelStrings = [...new Set(labelStrings)]; - - for (const labelString of labelStrings) { - const addLabel = loadLabelLogo(labelString); - if (addLabel != null) { - recordLabels.push(addLabel); - try { - recordLabelsInverted.push(addLabel.InvertColours()); - } catch (e) {} - } - } -} - - -/** - * Loads the label logo in Details. - * @param {string} publisherString The name of a record label or publisher. - * @returns {GdiBitmap} The record label logo as a gdi image object. - */ -function loadLabelLogo(publisherString) { - const d = new Date(); - const lastSrchYear = d.getFullYear(); - let dir = paths.labelsBase; - let recordLabel = null; - let labelStr = ReplaceFileChars(publisherString); - - if (labelStr) { - // * First check for record label folder - if (IsFolder(dir + labelStr) || - IsFolder(dir + (labelStr = - labelStr.replace(/ Records$/, '') - .replace(/ Recordings$/, '') - .replace(/ Music$/, '') - .replace(/\.$/, '') - .replace(/[\u2010\u2013\u2014]/g, '-')))) { // Hyphen, endash, emdash - let year = parseInt($('$year(%date%)')); - for (; year <= lastSrchYear; year++) { - const yearFolder = `${dir + labelStr}\\${year}`; - if (IsFolder(yearFolder)) { - console.log(`Found folder for ${labelStr} for year ${year}.`); - dir += `${labelStr}\\${year}\\`; - break; - } - } - if (year > lastSrchYear) { - dir += `${labelStr}\\`; // We didn't find a year folder so use the "default" logo in the root - console.log(`Found folder for ${labelStr} and using latest logo.`); - } - } - // * Actually load the label from either the directory we found above, or the base record label folder - labelStr = ReplaceFileChars(publisherString); // We need to start over with the original string when searching for the file, just to be safe - let label = `${dir + labelStr}.png`; - - if (IsFile(label)) { - recordLabel = gdi.Image(label); - console.log('Found Record label:', label, !recordLabel ? '' : ''); - } - else { - labelStr = - labelStr.replace(/ Records$/, '') - .replace(/ Recordings$/, '') - .replace(/ Music$/, '') - .replace(/[\u2010\u2013\u2014]/g, '-'); // Hyphen, endash, emdash - - label = `${dir + labelStr}.png`; - if (IsFile(label)) return gdi.Image(label); - label = `${dir + labelStr} Records.png`; - if (IsFile(label)) return gdi.Image(label); - } - } - return recordLabel; -} - - -///////////////////////////////// -// * DETAILS - METADATA GRID * // -///////////////////////////////// -/** - * Calculates date ratios based on various time-related properties of a music track, displayed on the timeline in Details. - * @param {boolean} dontUpdateLastPlayed Whether the last played date should be updated or not. - * @param {string} currentLastPlayed The current value of the last played time. - */ -function calcDateRatios(dontUpdateLastPlayed, currentLastPlayed) { - const newDate = new Date(); - dontUpdateLastPlayed = dontUpdateLastPlayed || false; - - let ratio; - let lfmPlayedTimesJsonLast = ''; - let playedTimesJsonLast = ''; - let playedTimesRatios = []; - let lfmPlayedTimes = []; - let playedTimes = []; - let added = ToTime($('$if2(%added_enhanced%,%added%)')); - let lastPlayed = ToTime($('$if2(%last_played_enhanced%,%last_played%)')); - const firstPlayed = ToTime($('$if2(%first_played_enhanced%,%first_played%)')); - const today = DateToYMD(newDate); - - if (dontUpdateLastPlayed && $Date(lastPlayed) === today) { - lastPlayed = ToTime(currentLastPlayed); - } - - if (componentEnhancedPlaycount) { - const playedTimesJson = $('[%played_times_js%]', fb.GetNowPlaying()); - const lastfmJson = $('[%lastfm_played_times_js%]', fb.GetNowPlaying()); - const log = ''; // ! Don't need this crap to flood the console // playedTimesJson === playedTimesJsonLast && lastfmJson === lfmPlayedTimesJsonLast ? false : settings.showDebugLog; - lfmPlayedTimesJsonLast = lastfmJson; - playedTimesJsonLast = playedTimesJson; - lfmPlayedTimes = ParseJson(lastfmJson, 'lastfm: ', log); - playedTimes = ParseJson(playedTimesJson, 'foobar: ', log); - } - else { - playedTimes.push(firstPlayed); - playedTimes.push(lastPlayed); - } - - if (firstPlayed) { - if (!added) { - added = firstPlayed; - } - const age = CalcAge(added); - - timelineFirstPlayedRatio = CalcAgeRatio(firstPlayed, age); - timelineLastPlayedRatio = CalcAgeRatio(lastPlayed, age); - if (timelineLastPlayedRatio < timelineFirstPlayedRatio) { - // Due to daylight savings time, if there's a single play before the time changed lastPlayed could be < firstPlayed - timelineLastPlayedRatio = timelineFirstPlayedRatio; - } - - if (playedTimes.length) { - for (let i = 0; i < playedTimes.length; i++) { - ratio = CalcAgeRatio(playedTimes[i], age); - playedTimesRatios.push(ratio); - } - } else { - playedTimesRatios = [timelineFirstPlayedRatio, timelineLastPlayedRatio]; - playedTimes = [firstPlayed, lastPlayed]; - } - - let j = 0; - const tempPlayedTimesRatios = playedTimesRatios.slice(); - tempPlayedTimesRatios.push(1.0001); // Pick up every last.fm time after lastPlayed fb knows about - for (let i = 0; i < tempPlayedTimesRatios.length; i++) { - while (j < lfmPlayedTimes.length && (ratio = CalcAgeRatio(lfmPlayedTimes[j], age)) < tempPlayedTimesRatios[i]) { - playedTimesRatios.push(ratio); - playedTimes.push(lfmPlayedTimes[j]); - j++; - } - if (ratio === tempPlayedTimesRatios[i]) { // Skip one instance - // console.log('skipped -->', ratio); - j++; - } - } - playedTimesRatios.sort(); - playedTimes.sort(); - - timelineFirstPlayedRatio = playedTimesRatios[0]; - timelineLastPlayedRatio = playedTimesRatios[Math.max(0, playedTimesRatios.length - (dontUpdateLastPlayed ? 2 : 1))]; - } - else { - timelineFirstPlayedRatio = 0.33; - timelineLastPlayedRatio = 0.66; - } - str.timeline.setPlayTimes(timelineFirstPlayedRatio, timelineLastPlayedRatio, playedTimesRatios, playedTimes); -} - - -/** - * Loads the codec logo of the now playing track, displayed in the metadata grid in Details. - */ -function loadCodecLogo() { - const codec = $('$lower($if2(%codec%,$ext(%path%)))'); - const format = $('$lower($ext(%path%))', fb.GetNowPlaying()); - const lightBg = new Color(col.detailsText).brightness < 140; - const bw = lightBg ? 'black' : 'white'; - - paths.codecLogoAac = `${imagesPath}codec/aac-${bw}.png`; - paths.codecLogoAc3 = `${imagesPath}codec/ac3-${bw}.png`; - paths.codecLogoAlac = `${imagesPath}codec/alac-${bw}.png`; - paths.codecLogoAlaw = `${imagesPath}codec/alaw-${bw}.png`; - paths.codecLogoAmr = `${imagesPath}codec/amr-${bw}.png`; - paths.codecLogoApe = `${imagesPath}codec/ape-${bw}.png`; - paths.codecLogoCaf = `${imagesPath}codec/caf-${bw}.png`; - paths.codecLogoDsd = `${imagesPath}codec/dsd-${bw}.png`; - paths.codecLogoDsdSacd = `${imagesPath}codec/dsd-sacd-${bw}.png`; - paths.codecLogoDts = `${imagesPath}codec/dts-${bw}.png`; - paths.codecLogoDxd = `${imagesPath}codec/dxd-${bw}.png`; - paths.codecLogoFlac = `${imagesPath}codec/flac-${bw}.png`; - paths.codecLogoGsm = `${imagesPath}codec/gsm-${bw}.png`; - paths.codecLogoImaadpcm = `${imagesPath}codec/imaadpcm-${bw}.png`; - paths.codecLogoLa = `${imagesPath}codec/la-${bw}.png`; - paths.codecLogoMid = `${imagesPath}codec/mid-${bw}.png`; - paths.codecLogoMlp = `${imagesPath}codec/mlp-${bw}.png`; - paths.codecLogoMod = `${imagesPath}codec/mod-${bw}.png`; - paths.codecLogoMp2 = `${imagesPath}codec/mp2-${bw}.png`; - paths.codecLogoMp3 = `${imagesPath}codec/mp3-${bw}.png`; - paths.codecLogoMpc = `${imagesPath}codec/musepack-${bw}.png`; - paths.codecLogoMsadpcm = `${imagesPath}codec/msadpcm-${bw}.png`; - paths.codecLogoOfr = `${imagesPath}codec/ofr-${bw}.png`; - paths.codecLogoOgg = `${imagesPath}codec/ogg-${bw}.png`; - paths.codecLogoOpus = `${imagesPath}codec/opus-${bw}.png`; - paths.codecLogoPcm = `${imagesPath}codec/pcm-${bw}.png`; - paths.codecLogoPcmAiff = `${imagesPath}codec/pcm-aiff-${bw}.png`; - paths.codecLogoPcmWav = `${imagesPath}codec/pcm-wav-${bw}.png`; - paths.codecLogoQoa = `${imagesPath}codec/qoa-${bw}.png`; - paths.codecLogoShn = `${imagesPath}codec/shn-${bw}.png`; - paths.codecLogoSpx = `${imagesPath}codec/spx-${bw}.png`; - paths.codecLogoTak = `${imagesPath}codec/tak-${bw}.png`; - paths.codecLogoTta = `${imagesPath}codec/tta-${bw}.png`; - paths.codecLogoUlaw = `${imagesPath}codec/ulaw-${bw}.png`; - paths.codecLogoUsac = `${imagesPath}codec/usac-${bw}.png`; - paths.codecLogoWavpack = `${imagesPath}codec/wavpack-${bw}.png`; - paths.codecLogoWma = `${imagesPath}codec/wma-${bw}.png`; - - switch (true) { - case codec === 'aac' || format === 'aac': - case codec === 'aac acm codec' || format === 'aac': codecLogo = gdi.Image(paths.codecLogoAac); break; - case codec === 'ac3' || format === 'ac3': - case codec === 'ac3 acm codec' || format === 'ac3': - case codec === 'atsc a/52' || format === 'ac3': codecLogo = gdi.Image(paths.codecLogoAc3); break; - case codec === 'alac' || format === 'alac': codecLogo = gdi.Image(paths.codecLogoAlac); break; - case codec === 'ccitt a-law' || format === 'alaw': codecLogo = gdi.Image(paths.codecLogoAlaw); break; - case codec === 'amr' || format === 'amr': codecLogo = gdi.Image(paths.codecLogoAmr); break; - case codec === 'monkey\'s audio' || format === 'ape': codecLogo = gdi.Image(paths.codecLogoApe); break; - case codec === 'caf' || format === 'caf': codecLogo = gdi.Image(paths.codecLogoCaf); break; - case codec === 'dts' || format === 'dts': - case codec === 'dca (dts coherent acoustics)' || format === 'dca (dts coherent acoustics)': codecLogo = gdi.Image(paths.codecLogoDts); break; - case codec === 'flac' || format === 'flac': codecLogo = gdi.Image(paths.codecLogoFlac); break; - case codec === 'gsm 6.10' || format === 'gsm': codecLogo = gdi.Image(paths.codecLogoGsm); break; - case codec === 'ima adpcm' || format === 'imaadpcm': codecLogo = gdi.Image(paths.codecLogoImaadpcm); break; - case codec === 'la' || format === 'la': codecLogo = gdi.Image(paths.codecLogoLa); break; - case codec === 'mid' || format === 'mid': codecLogo = gdi.Image(paths.codecLogoMid); break; - case codec === 'mlp' || format === 'mlp': codecLogo = gdi.Image(paths.codecLogoMlp); break; - case codec === 'mod' || format === 'mod': codecLogo = gdi.Image(paths.codecLogoMod); break; - case codec === 'mp2' || format === 'mp2': codecLogo = gdi.Image(paths.codecLogoMp2); break; - case codec === 'mp3' || format === 'mp3': - case codec === 'mpeg layer-3' || format === 'mp3': codecLogo = gdi.Image(paths.codecLogoMp3); break; - case codec === 'musepack' || format === 'mpc': codecLogo = gdi.Image(paths.codecLogoMpc); break; - case codec === 'microsoft adpcm' || format === 'msadpcm': codecLogo = gdi.Image(paths.codecLogoMsadpcm); break; - case codec === 'optimfrog' || format === 'ofr': codecLogo = gdi.Image(paths.codecLogoOfr); break; - case codec === 'vorbis' || format === 'ogg': codecLogo = gdi.Image(paths.codecLogoOgg); break; - case codec === 'opus' || format === 'opus': codecLogo = gdi.Image(paths.codecLogoOpus); break; - case codec === 'qoa' || format === 'qoa': codecLogo = gdi.Image(paths.codecLogoQoa); break; - case codec === 'shorten' || format === 'shn': codecLogo = gdi.Image(paths.codecLogoShn); break; - case codec === 'speex' || format === 'spx': codecLogo = gdi.Image(paths.codecLogoSpx); break; - case codec === 'tak' || format === 'tak': codecLogo = gdi.Image(paths.codecLogoTak); break; - case codec === 'true audio' || format === 'tta': codecLogo = gdi.Image(paths.codecLogoTta); break; - case codec === 'ccitt u-law' || format === 'ulaw': codecLogo = gdi.Image(paths.codecLogoUlaw); break; - case codec === 'usac' || format === 'usac': codecLogo = gdi.Image(paths.codecLogoUsac); break; - case codec === 'wma' || format === 'wma': codecLogo = gdi.Image(paths.codecLogoWma); break; - case codec === 'wavpack' || format === 'wv': codecLogo = gdi.Image(paths.codecLogoWavpack); break; - - case ['dsd64', 'dsd128', 'dsd256', 'dsd512', 'dsd1024', 'dsd2048'].includes(codec): - codecLogo = gdi.Image(paths.codecLogoDsd); break; - case ['dxd64', 'dxd128', 'dxd256', 'dxd512', 'dxd1024', 'dxd2048'].includes(codec): - codecLogo = gdi.Image(paths.codecLogoDxd); break; - case ['dst64', 'dst128', 'dst256', 'dst512', 'dst1024', 'dst2048'].includes(codec) && format === 'iso': - codecLogo = gdi.Image(paths.codecLogoDsdSacd); break; - - case codec === 'pcm' && !['aiff', 'w64', 'wav'].includes(format): - codecLogo = gdi.Image(paths.codecLogoPcm); break; - case codec === 'pcm' && format === 'aiff': - codecLogo = gdi.Image(paths.codecLogoPcmAiff); break; - case codec === 'pcm' && ['w64', 'wav'].includes(format): - codecLogo = gdi.Image(paths.codecLogoPcmWav); break; - } -} - - -/** - * Loads the channel logo of the now playing track, displayed in the metadata grid in Details. - */ -function loadChannelLogo() { - const channels = $('%channels%'); - const type = - (pref.layout === 'default' && pref.showGridChannelLogo_default === 'textlogo' || - pref.layout === 'artwork' && pref.showGridChannelLogo_artwork === 'textlogo') ? '_text' : ''; - - const lightBg = new Color(col.detailsText).brightness < 140; - const bw = lightBg ? 'black' : 'white'; - - paths.channelLogo10Mono = `${imagesPath}channels/10_mono${type}-${bw}.png`; - paths.channelLogo20Stereo = `${imagesPath}channels/20_stereo${type}-${bw}.png`; - paths.channelLogo30Center = `${imagesPath}channels/30_center${type}-${bw}.png`; - paths.channelLogo40Quad = `${imagesPath}channels/40_quad${type}-${bw}.png`; - paths.channelLogo50Surr = `${imagesPath}channels/50_surround${type}-${bw}.png`; - paths.channelLogo51Surr = `${imagesPath}channels/51_surround${type}-${bw}.png`; - paths.channelLogo61Surr = `${imagesPath}channels/61_surround${type}-${bw}.png`; - paths.channelLogo71Surr = `${imagesPath}channels/71_surround${type}-${bw}.png`; - paths.channelLogo91Surr = `${imagesPath}channels/91_surround${type}-${bw}.png`; - paths.channelLogo111Surr = `${imagesPath}channels/111_surround${type}-${bw}.png`; - - switch (true) { - case channels === 'mono': channelLogo = gdi.Image(paths.channelLogo10Mono); break; - case channels === 'stereo': channelLogo = gdi.Image(paths.channelLogo20Stereo); break; - case channels === '3ch': channelLogo = gdi.Image(paths.channelLogo30Center); break; - case channels === '4ch': channelLogo = gdi.Image(paths.channelLogo40Quad); break; - case channels === '5ch': channelLogo = gdi.Image(paths.channelLogo50Surr); break; - case channels === '6ch': channelLogo = gdi.Image(paths.channelLogo51Surr); break; - case channels === '7ch': channelLogo = gdi.Image(paths.channelLogo61Surr); break; - case channels === '8ch': channelLogo = gdi.Image(paths.channelLogo71Surr); break; - case channels === '10ch': channelLogo = gdi.Image(paths.channelLogo91Surr); break; - case channels === '12ch': channelLogo = gdi.Image(paths.channelLogo111Surr); break; - } -} - - -/** - * Loads the release country flags, displayed in the metadata grid in Details. - */ -function loadReleaseCountryFlag() { - releaseFlagImg = loadFlagImage($(tf.releaseCountry)); -} - - -/** - * Updates the metadata grid in Details, reuses last value for last played unless provided one. - * @param {string} currentLastPlayed The current value of the "Last Played" metadata field. - * @param {string} currentPlayingPlaylist The current active playlist that is being played from. - * @returns {Array} The updated metadata grid, which is an array of objects with properties `label`, `val` and `age`. - */ -function updateMetadataGrid(currentLastPlayed, currentPlayingPlaylist) { - currentLastPlayed = (str && str.grid ? str.grid.find(value => value.label === 'Last Played') || {} : {}).val; - str.grid = []; - for (let k = 0; k < metadataGrid.length; k++) { - let val = $(metadataGrid[k].val); - if (val && metadataGrid[k].label) { - if (metadataGrid[k].age) { - val = $(`$date(${val})`); // Never show time - const age = CalcAgeDateString(val); - if (age) { - val += ` (${age})`; - } - } - str.grid.push({ - age: metadataGrid[k].age, - label: metadataGrid[k].label, - val - }); - } - } - if (typeof currentLastPlayed !== 'undefined') { - const lp = str.grid.find(value => value.label === 'Last Played'); - if (lp) { - lp.val = $Date(currentLastPlayed); - if (CalcAgeDateString(lp.val)) { - lp.val += ` (${CalcAgeDateString(lp.val)})`; - } - } - } - if (typeof currentPlayingPlaylist !== 'undefined') { - const pl = str.grid.find(value => value.label === 'Playing List'); - if (pl) { - pl.val = currentPlayingPlaylist; - } - } - return str.grid; -} - - -/////////////////////////////////// -// * PLAYLIST - INITIALIZATION * // -/////////////////////////////////// -/** - * Clears current used color of header and row nowplaying bg to prevent flashing from old used primary color. - */ -function clearPlaylistNowPlayingBg() { - if (['white', 'black', 'reborn', 'random'].includes(pref.theme)) { - g_pl_colors.header_nowplaying_bg = ''; - g_pl_colors.row_nowplaying_bg = ''; - } -} - - -/** - * Initializes the Playlist. - */ -function initPlaylist() { - playlist = new PlaylistPanel(setPlaylistX(), 0); - playlist.initialize(); -} - - -/** - * Updates the Playlist when content has changed, e.g when adding/removing items or changing the active playlist. - */ -function updatePlaylist() { - Debounce((playlistIndex) => { - trace_call && console.log('initPlaylistDebounced'); - playlist.on_playlist_items_added(playlistIndex); - }, 100, { - leading: false, - trailing: true - })(plman.ActivePlaylist); -} - - -///////////////////////////// -// * PLAYLIST - CONTROLS * // -///////////////////////////// -/** - * Sorts the Playlist by sort patterns defined in the config file. - */ -function setPlaylistSortOrder() { - const sortOrder = { - default: settings.playlistSortDefault, - artistDate_asc: settings.playlistSortArtistDate_asc, - artistDate_dsc: settings.playlistSortArtistDate_dsc, - albumTitle: settings.playlistSortAlbumTitle, - albumRating_asc: settings.playlistSortAlbumRating_asc, - albumRating_dsc: settings.playlistSortAlbumRating_dsc, - albumPlaycount_asc: settings.playlistSortAlbumPlaycount_asc, - albumPlaycount_dsc: settings.playlistSortAlbumPlaycount_dsc, - trackTitle: settings.playlistSortTrackTitle, - trackNumber: settings.playlistSortTrackNumber, - trackRating_asc: settings.playlistSortTrackRating_asc, - trackRating_dsc: settings.playlistSortTrackRating_dsc, - trackPlaycount_asc: settings.playlistSortTrackPlaycount_asc, - trackPlaycount_dsc: settings.playlistSortTrackPlaycount_dsc, - year_asc: settings.playlistSortYear_asc, - year_dsc: settings.playlistSortYear_dsc, - genre_asc: settings.playlistSortGenre, - genre_dsc: settings.playlistSortGenre, - label_asc: settings.playlistSortLabel, - label_dsc: settings.playlistSortLabel, - country_asc: settings.playlistSortCountry, - country_dsc: settings.playlistSortCountry, - filePath: settings.playlistSortFilePath, - custom: settings.playlistSortCustom - }; - - if (['genre_dsc', 'label_dsc', 'country_dsc'].includes(pref.playlistSortOrder)) { - plman.SortByFormatV2(plman.ActivePlaylist, sortOrder[pref.playlistSortOrder], -1); - } else { - plman.SortByFormat(plman.ActivePlaylist, sortOrder[pref.playlistSortOrder] || ''); - } -} - - -////////////////////////////////// -// * LIBRARY - INITIALIZATION * // -////////////////////////////////// -/** - * Initializes the Library. - */ -function initLibraryPanel() { - if (libraryInitialized) return; - ui = new UserInterface(); - panel = new Panel(); - sbar = new Scrollbar(); - vk = new Vkeys(); - lib = new Library(); - pop = new Populate(); - search = new Search(); - find = new Find(); - but = new Buttons(); - popUpBox = new PopUpBox(); - men = new MenuItems(); - timer = new Timers(); - libraryPanel = new LibraryPanel(); - library = new LibraryCallbacks(); - libraryInitialized = true; -} - - -/** - * Initializes active Library layout presets. - */ -function initLibraryLayout() { - const libraryLayoutSplitPresets = - pref.libraryLayoutSplitPreset || pref.libraryLayoutSplitPreset2 || pref.libraryLayoutSplitPreset3 || pref.libraryLayoutSplitPreset4; - - const setLibraryView = () => { - lib.logTree(); - pop.clearTree(); - ui.getFont(); // * Reset font size when pref.libraryLayoutSplitPreset4 was used - RepaintWindowRectAreas(); - if (pref.libraryLayout !== 'split' && (!pref.libraryLayoutFullPreset || !libraryLayoutSplitPresets)) { - ppt.albumArtShow = pref.savedAlbumArtShow; - ppt.albumArtLabelType = pref.savedAlbumArtLabelType; - } - panel.imgView = pref.libraryLayout === 'normal' && pref.libraryLayoutFullPreset ? ppt.albumArtShow = false : ppt.albumArtShow; - men.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]); - } - - // * Full layout preset - if (pref.libraryLayout === 'full' && pref.libraryLayoutFullPreset) { - pref.libraryDesign = 'reborn'; - pref.libraryThumbnailSize = pref.libraryThumbnailSizeSaved; - if (pref.playerSize_HD_small && (pref.libraryThumbnailSize === 'auto' || ppt.thumbNailSize === 'auto')) { - ppt.thumbNailSize = 1; - } - ppt.albumArtLabelType = 1; - panel.imgView = ppt.albumArtShow = true; - } - // * Split layout presets - else if (pref.libraryLayout === 'split' && libraryLayoutSplitPresets) { - if (pref.layout !== 'default') return; - - if (!g_properties.show_header) updatePlaylist(); - - if (pref.playlistLayout === 'full') { - pref.playlistLayout = 'normal'; - } - - if (pref.libraryLayoutSplitPreset) { - pref.libraryDesign = 'reborn'; - pref.libraryThumbnailSize = 'playlist'; - panel.imgView = ppt.albumArtShow = false; - ppt.albumArtLabelType = 1; - g_properties.show_header = true; - if (displayPlaylist && displayLibrary) { - g_properties.auto_collapse = true; - playlist.auto_collapse_header(); - } - else { - g_properties.auto_collapse = false; - playlist.expand_header(); - } - } - else if (pref.libraryLayoutSplitPreset2) { - pref.libraryDesign = 'reborn'; - pref.libraryThumbnailSize = 'playlist'; - panel.imgView = ppt.albumArtShow = false; - ppt.albumArtLabelType = 1; - g_properties.auto_collapse = false; - g_properties.show_header = displayPlaylist && !displayLibrary && pref.libraryLayout === 'split'; - updatePlaylist(); - } - else if (pref.libraryLayoutSplitPreset3) { - pref.libraryDesign = 'reborn'; - pref.libraryThumbnailSize = 'playlist'; - panel.imgView = ppt.albumArtShow = true; - ppt.albumArtLabelType = 1; - g_properties.show_header = true; - if (displayPlaylist && displayLibrary) { - g_properties.auto_collapse = true; - playlist.auto_collapse_header(); - } - else { - g_properties.auto_collapse = false; - playlist.expand_header(); - } - } - else if (pref.libraryLayoutSplitPreset4) { - pref.libraryDesign = 'artistLabelsRight'; - pref.libraryThumbnailSize = 'playlist'; - panel.imgView = ppt.albumArtShow = true; - ppt.albumArtLabelType = 2; - g_properties.show_header = true; - if (displayPlaylist && displayLibrary) { - g_properties.auto_collapse = true; - playlist.auto_collapse_header(); - } - else { - g_properties.auto_collapse = false; - playlist.expand_header(); - } - } - playlist.on_size(ww, wh); - } - - if (!libraryInitialized) return; - setLibraryView(); - setLibrarySize(); - initLibraryColors(); - initStyleColors(); - themeColorAdjustments(); - window.Repaint(); -} - - -/** - * Sets the Library design. - */ -function setLibraryDesign() { - switch (pref.libraryDesign) { - case 'traditional': panel.set('quickSetup', 0); break; - case 'modern': panel.set('quickSetup', 1); break; - case 'ultraModern': panel.set('quickSetup', 2); break; - case 'clean': panel.set('quickSetup', 3); break; - case 'facet': panel.set('quickSetup', 4); break; - case 'coversLabelsRight': panel.set('quickSetup', 5); break; - case 'coversLabelsBottom': panel.set('quickSetup', 6); break; - case 'coversLabelsBlend': panel.set('quickSetup', 7); break; - case 'artistLabelsRight': panel.set('quickSetup', 8); break; - case 'flowMode': panel.set('quickSetup', 11); pref.libraryLayout = 'full'; break; - case 'reborn': panel.set('quickSetup', 12); break; - } -} - - -/** - * Sets the Library size and position. - */ -function setLibrarySize() { - if (!libraryInitialized) return; - - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; - - const x = - pref.layout === 'artwork' || pref.libraryLayout !== 'normal' ? 0 : - pref.panelWidthAuto ? displayLibrarySplit() || !fb.IsPlaying ? 0 : noAlbumArtStub ? noAlbumArtSize : albumArtSize.x + albumArtSize.w : - ww * 0.5; - - const y = geo.topMenuHeight; - - const libraryWidth = - pref.layout === 'artwork' || pref.libraryLayout === 'full' ? ww : - pref.panelWidthAuto ? displayLibrarySplit() ? noAlbumArtSize : !fb.IsPlaying ? ww : ww - (noAlbumArtStub ? noAlbumArtSize : albumArtSize.x + albumArtSize.w) : - ww * 0.5; - - const libraryHeight = Math.max(0, wh - geo.lowerBarHeight - y); - - libraryPanel.on_size(x, y, libraryWidth, libraryHeight); -} - - -//////////////////////////// -// * LIBRARY - CONTROLS * // -//////////////////////////// -/** - * Drags and drops items from Library to Playlist in split layout. - */ -function librarySplitDragDrop() { - const handleList = pop.getHandleList('newItems'); - pop.sortIfNeeded(handleList); - fb.DoDragDrop(0, handleList, handleList.Count ? 1 | 4 : 0); - - const pl = plman.ActivePlaylist; - const drop_idx = playlistDropIndex; - - if (plman.IsPlaylistLocked(pl)) return; // Do nothing, it's locked or an auto-playlist - - plman.ClearPlaylistSelection(pl); - - setTimeout(() => { - plman.RemovePlaylistSelection(pl); - plman.InsertPlaylistItems(pl, drop_idx, handleList); - plman.SetPlaylistFocusItem(pl, drop_idx); - }, 1); -} - - -///////////////////////////// -// * LIBRARY - ALBUM ART * // -///////////////////////////// -/** - * Dynamically resizes Library album cover thumbnails based on the player size. - */ -function autoThumbnailSize() { - if (pref.libraryThumbnailSize !== 'auto') return; - - const noStd = ['coversLabelsRight', 'artistLabelsRight'].includes(pref.libraryDesign) || ppt.albumArtLabelType === 2; - const fullW = pref.libraryLayout === 'full' && pref.layout === 'default'; - - if (!RES_4K && !RES_QHD) { - if (pref.layout === 'default' && ww < 1600 && wh < 960 || pref.layout === 'artwork' && ww < 700 && wh < 860) { - ppt.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 0 : // Thumbnail size 'Small' or 'Mini' - pref.layout === 'artwork' ? 1 : 2; // Thumbnail size 'Small' or 'Regular' - ppt.verticalAlbumArtPad = 2; - } - if (pref.layout === 'default' && ww >= 1600 && wh >= 960 || pref.layout === 'artwork' && ww >= 700 && wh >= 860) { - ppt.thumbNailSize = noStd && fullW ? 2 : noStd && !fullW ? 1 : // Thumbnail size 'Small' - fullW ? 3 : 3; // Thumbnail size 'Medium' - ppt.verticalAlbumArtPad = 2; - } - if (pref.layout === 'default' && ww >= 1802 && wh >= 1061 || pref.layout === 'artwork' && ww >= 901 && wh >= 1062) { - ppt.thumbNailSize = noStd && !fullW ? 2 : noStd && fullW ? 3 : // Thumbnail size 'Small' or 'Regular' - fullW ? ww === 1802 && wh === 1061 ? 5 : 4 : 3; // Thumbnail size 'XL' or 'Large' or 'Medium' - ppt.verticalAlbumArtPad = 2; - } - } - else if (RES_QHD) { - if (pref.layout === 'default' && ww < 1802 && wh < 1061 || pref.layout === 'artwork' && ww < 901 && wh < 1061) { - ppt.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 0 : // Thumbnail size 'Small' or 'Mini' - pref.layout === 'artwork' ? 1 : 2; // Thumbnail size 'Small' or 'Regular' - ppt.verticalAlbumArtPad = 2; - } - if (pref.layout === 'default' && ww >= 1802 && wh >= 1061 || pref.layout === 'artwork' && ww >= 901 && wh >= 1061) { - ppt.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 1 : // Thumbnail size 'Small' - fullW ? 4 : 2; // Thumbnail size 'Medium' or 'Regular' - ppt.verticalAlbumArtPad = 3; - } - if (pref.layout === 'default' && ww >= 2280 && wh >= 1300 || pref.layout === 'artwork' && ww >= 1140 && wh >= 1300) { - ppt.thumbNailSize = noStd && !fullW ? 2 : noStd && fullW ? 3 : // Thumbnail size 'Small' or 'Regular' - fullW ? 5 : 3; // Thumbnail size 'Large' or 'Medium' - ppt.verticalAlbumArtPad = fullW ? 2 : 3; - } - } - else if (RES_4K) { - if (pref.layout === 'default' && ww < 2800 && wh < 1720 || pref.layout === 'artwork' && ww < 1400 && wh < 1720) { - ppt.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 0 : // Thumbnail size 'Small' or 'Mini' - fullW ? 2 : 1; // Thumbnail size 'Small' - ppt.verticalAlbumArtPad = 2; - } - if (pref.layout === 'default' && ww >= 2800 && wh >= 1720 || pref.layout === 'artwork' && ww >= 1400 && wh >= 1720) { - ppt.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 1 : // Thumbnail size 'Small' - fullW ? 3 : 1; // Thumbnail size 'Regular' or 'Small' - ppt.verticalAlbumArtPad = 3; - } - if (pref.layout === 'default' && ww >= 3400 && wh >= 2020 || pref.layout === 'artwork' && ww >= 1400 && wh >= 1720) { - ppt.thumbNailSize = noStd && !fullW ? 1 : noStd && fullW ? 3 : // Thumbnail size 'Small' or 'Regular' - fullW ? ww === 3400 && wh === 2020 ? 4 : 4 : 2; // Thumbnail size 'Medium' or 'Regular' - ppt.verticalAlbumArtPad = 2; - } - } -} - - -//////////////////////////////////// -// * BIOGRAPHY - INITIALIZATION * // -//////////////////////////////////// -/** - * Initializes the Biography. - */ -function initBiographyPanel() { - if (biographyInitialized) return; - uiBio = new UserInterfaceBio(); - vkBio = new VkeysBio(); - panelBio = new PanelBio(); - name = new Names(); - alb_scrollbar = new ScrollbarBio(); - art_scrollbar = new ScrollbarBio(); - art_scroller = new ScrollbarBio(); - cov_scroller = new ScrollbarBio(); - butBio = new ButtonsBio(); - popUpBoxBio = new PopUpBoxBio(); - txt = new Text(); - tagBio = new TaggerBio(); - resize = new ResizeHandler(); - libBio = new LibraryBio(); - imgBio = new ImagesBio(); - seeker = new Seeker(); - filmStrip = new FilmStrip(); - timerBio = new TimersBio(); - menBio = new MenuItemsBio(); - serverBio = new ServerBio(); - infoboxBio = new InfoboxBio(); - lyricsBio = new LyricsBio(); - biographyPanel = new BiographyPanel(); - biography = new BiographyCallbacks(); - biographyInitialized = true; -} - - -/** - * Initializes active Biography layout presets. - */ -function initBiographyLayout() { - if (pref.biographyLayoutFullPreset) { - pptBio.style = pref.biographyLayoutFullPreset && pref.layout === 'default' && pref.biographyLayout === 'full' ? 3 : 0; - pptBio.showFilmStrip = false; - pptBio.filmStripPos = 3; - } - RepaintWindowRectAreas(); - setBiographySize(); -} - - -/** - * Sets the Biography display layout. - */ -function setBiographyDisplay() { - switch (pref.biographyDisplay) { - case 'Image+text': - pptBio.img_only = false; - pptBio.text_only = false; - break; - case 'Image': - pptBio.img_only = true; - pptBio.text_only = false; - break; - case 'Text': - pptBio.img_only = false; - pptBio.text_only = true; - break; - } -} - - -/** - * Sets the Biography size and position. - */ -function setBiographySize() { - if (!biographyInitialized) return; - - const x = 0; - const y = geo.topMenuHeight; - - const biographyWidth = - pref.layout === 'artwork' || pref.biographyLayout === 'full' ? ww : - pref.panelWidthAuto ? (!albumArt && !noAlbumArtStub || !fb.IsPlaying) ? ww : albumArtSize.x + albumArtSize.w : - ww * 0.5; - - const biographyHeight = Math.max(0, wh - geo.lowerBarHeight - y); - - biographyPanel.on_size(x, y, biographyWidth, biographyHeight); -} - - -///////////////////////////////// -// * LYRICS - INITIALIZATION * // -///////////////////////////////// -/** - * Initializes Lyrics of the now playing song. - */ -function initLyrics() { - lyrics = new Lyrics(); - lyrics.on_size(albumArtSize.x, albumArtSize.y, albumArtSize.w, albumArtSize.h); - lyrics.initLyrics(); -} - - -/** - * Displays Lyrics on startup or when remembering the Lyrics panel state. - */ -function displayLyrics() { - fb.Play(); - displayPlaylist = pref.layout === 'default'; - setTimeout(() => { - pref.displayLyrics = true; - initLyrics(); - initButtonState(); - }, 500); -} - - -/** - * Resets the size and position of the lyrics. - */ -function resetLyricsPosition() { - const fullW = pref.layout === 'default' && pref.lyricsLayout === 'full' && pref.displayLyrics; - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; - - const lyricsX = - noAlbumArtStub ? - fullW ? pref.panelWidthAuto && pref.lyricsLayout === 'normal' ? noAlbumArtSize * 0.5 : ww * 0.333 : - pref.panelWidthAuto ? albumArtSize.x : 0 : - albumArtSize.x; - - const lyricsY = noAlbumArtStub ? geo.topMenuHeight : albumArtSize.y; - - const lyricsW = - noAlbumArtStub ? - pref.layout === 'artwork' ? ww : - fullW ? ww * 0.333 : - pref.panelWidthAuto ? noAlbumArtSize : - ww * 0.5 : - albumArtSize.w; - - const lyricsH = noAlbumArtStub ? wh - geo.topMenuHeight - geo.lowerBarHeight : albumArtSize.h; - - if (lyrics) { - lyrics.on_size(lyricsX, lyricsY, lyricsW, lyricsH); - } -} diff --git a/profile/georgia-reborn/scripts/Base/gr-main.js b/profile/georgia-reborn/scripts/Base/gr-main.js index e41e9101..31646b0f 100644 --- a/profile/georgia-reborn/scripts/Base/gr-main.js +++ b/profile/georgia-reborn/scripts/Base/gr-main.js @@ -1,1431 +1,5020 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Main * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Main * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////////////////////// -// * MAIN USER INTERFACE PARTS * // -/////////////////////////////////// +///////////////////////////// +// * MAIN USER INTERFACE * // +///////////////////////////// /** - * Draws the Main and Details background. - * @param {GdiGraphics} gr + * A class that is responsible for drawing all main user interface parts. */ -function drawBackgrounds(gr) { - const displayDetails = (pref.layout === 'artwork' ? displayPlaylist : !displayPlaylist) && !displayLibrary && !displayBiography; - gr.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); - gr.SetSmoothingMode(SmoothingMode.None); - - // * MAIN BACKGROUND * // - if (!albumArt && noArtwork) { // We use noArtwork to prevent flashing of blue default theme - initNoAlbumArtSize(); +class MainUI { + constructor() { + // * GEOMETRY * // + // #region GEOMETRY + /** @public @type {number} The global window.Width. */ + this.ww = 0; + /** @public @type {number} The global window.Height. */ + this.wh = 0; + /** @public @type {number} The width and height of the pause button. */ + this.pauseSize = SCALE(100); + /** @public @type {number} The size of the disc art shadow. */ + this.discArtShadow = SCALE(6); + /** @private @type {number} The top position of the metadata grid. */ + this.gridTop = 0; + /** @public @type {number} The height of the metadata grid tooltip area. */ + this.gridTooltipHeight = SCALE(100); + /** @public @type {number} The height of the timeline. */ + this.timelineHeight = SCALE(8); + /** @public @type {number} The height of the top menu. */ + this.topMenuHeight = SCALE(40); + /** @public @type {number} The height of the song title and time + progress bar area. */ + this.lowerBarHeight = SCALE(120); + /** @private @type {number} The width of time string in the lower bar. */ + this.lowerBarTimeW = 0; + /** @private @type {number} The height of time string in the lower bar. */ + this.lowerBarTimeH = 0; + /** @private @type {number} The x-position of the time string in the lower bar. */ + this.lowerBarTimeX = 0; + /** @private @type {number} The y-position of the time string in the lower bar. */ + this.lowerBarTimeY = 0; + /** @private @type {number} The y-position of the progress bar in the lower bar. */ + this.progressBarY = 0; + /** @public @type {number} The height of the progress bar. */ + this.progressBarH = SCALE(12); + /** @private @type {number} The y-position of the peakmeter bar in the lower bar. */ + this.peakmeterBarY = 0; + /** @public @type {number} The height of the peakmeter bar. */ + this.peakmeterBarH = SCALE(26); + /** @private @type {number} The y-position of the waveform bar in the lower bar. */ + this.waveformBarY = 0; + /** @public @type {number} The height of the waveform bar. */ + this.waveformBarH = SCALE(26); + // #endregion + + // * CONTROLS *// + // #region CONTROLS + /** @public @type {boolean} The top menu and contextual menu state, is it open ( active ) or not. */ + this.activeMenu = false; + /** @public @type {object} The theme button object. */ + this.btn = {}; + /** @public @type {GdiGraphics[]} The theme button images array. */ + this.btnImg = []; + /** @public @type {boolean} The display state of the Playlist panel. */ + this.displayPlaylist = false; + /** @public @type {boolean} The display state of the Playlist panel in Artwork layout. */ + this.displayPlaylistArtwork = false; + /** @public @type {boolean} The display state of the Details panel. */ + this.displayDetails = false; + /** @public @type {boolean} The display state of the Library panel. */ + this.displayLibrary = false; + /** @public @type {boolean} The display state of the Biography panel. */ + this.displayBiography = false; + /** @public @type {boolean} The display state of the Lyrics panel. */ + this.displayLyrics = false; + /** @public @type {boolean} The display state of custom theme menu when using Options > Theme > Edit custom theme. */ + this.displayCustomThemeMenu = false; + /** @public @type {boolean} The display state of the metadata grid menu. */ + this.displayMetadataGridMenu = false; + /** @public @type {boolean} The double click mouse state. */ + this.doubleClicked = false; + /** @public @type {object} The mouse move position state. */ + this.state = {}; + /** @public @type {string} The tooltip text handler for styled tooltip. */ + this.styledTooltipText = ''; + /** @public @type {boolean} The draw state of styled tooltip. */ + this.styledTooltipReady = false; + /** @public @type {FbTooltip} The tooltip object. */ + this.ttip = null; + // #endregion + + // * IMAGES * // + // #region IMAGES + /** @public @type {GdiBitmap} The big album art image displayed on the left side. */ + this.albumArt = null; + /** @private @type {GdiBitmap} The copy of the original album art image, used for cropping. */ + this.albumArtCopy = null; + /** @public @type {GdiBitmap[]} The album art list array containing album and disc art images. */ + this.albumArtList = []; + /** @public @type {number} The index of currently displayed album art if more than 1. */ + this.albumArtIndex = 0; + /** @public @type {object} The album art position (big image). */ + this.albumArtSize = new ImageSize(0, 0, 0, 0); + /** @public @type {GdiBitmap} The pre-scaled album art to speed up drawing considerably. */ + this.albumArtScaled = null; + /** @public @type {boolean} The state when artwork displayed is embedded and not loaded from a file. */ + this.albumArtEmbedded = false; + /** @private @type {boolean} The off-center position of the album art, if true, it will shift 40 pixels to the right. */ + this.albumArtOffCenter = false; + /** @private @type {boolean} The state to always load art from cache unless this is set. */ + this.albumArtFromCache = true; + /** @private @type {boolean} The state when album art or disc art has artwork loaded. */ + this.hasArtwork = false; + /** @public @type {boolean} The no album art stub when no album cover was found. */ + this.noAlbumArtStub = false; + /** @public @type {GdiBitmap} The disc art image used in Details. */ + this.discArt = null; + /** @public @type {GdiBitmap} The disc art album cover image used in Details. */ + this.discArtCover = null; + /** @public @type {GdiBitmap[]} The array of disc art images used in Details. */ + this.discArtArray = []; + /** @public @type {GdiBitmap[]} The array of disc art album cover images used in Details. */ + this.discArtArrayCover = []; + /** @private @type {boolean} The state when disc art was found on hard drive used in Details. */ + this.discArtFound = false; + /** @public @type {object} The disc art position used in Details (offset from albumArtSize). */ + this.discArtSize = new ImageSize(0, 0, 0, 0); + /** @public @type {GdiBitmap} The rotated disc art from the RotateImg helper used in Details. */ + this.discArtRotation = null; + /** @public @type {GdiBitmap} The rotated disc art album cover from the RotateImg helper used in Details. */ + this.discArtRotationCover = null; + /** @public @type {number} The global index of current discArtArray img to draw used in Details. */ + this.discArtRotationIndex = 0; + /** @public @type {number} The global index of current discArtArrayCover img to draw used in Details. */ + this.discArtRotationIndexCover = 0; + /** @public @type {GdiBitmap[]} The array of record label images used in Details. */ + this.recordLabels = []; + /** @public @type {GdiBitmap[]} The array of inverted record label images used in Details. */ + this.recordLabelsInverted = []; + /** @public @type {GdiBitmap} The band logo image used in Details. */ + this.bandLogo = null; + /** @public @type {GdiBitmap} The inverted band logo image shown in Details. */ + this.bandLogoInverted = null; + /** @public @type {GdiBitmap[]} The array of flag images shown in Details and in the lower bar. */ + this.flagImgs = []; + /** @private @type {GdiBitmap} The release country flag image shown in the metadata grid in Details. */ + this.releaseFlagImg = null; + /** @private @type {GdiBitmap} The codec logo image shown in the metadata grid in Details. */ + this.codecLogo = null; + /** @private @type {GdiBitmap} The channel logo image shown in the metadata grid in Details. */ + this.channelLogo = null; + /** @private @type {GdiBitmap} The Hi-Res Audio badge logo image shown on album art when enabled. */ + this.hiResAudioLogo = null; + /** @public @type {boolean} The last.fm logo image displayed when we %lastfm_play_count% > 0, shown in the metadata grid in Details. */ + this.playCountVerifiedByLastFm = false; + /** @private @type {GdiBitmap} The shadow behind labels used in Details. */ + this.labelShadowImg = null; + /** @private @type {GdiBitmap} The shadow behind the artwork + disc art used in Details. */ + this.shadowImg = null; + // #endregion + + // * STATE * // + // #region STATE + /** @public @type {number} The left edge of the record labels in Details. Saved so we don't have to recalculate every on every on_paint unless size has changed. */ + this.lastLeftEdge = 0; + /** @private @type {number} The last label height of the record labels in Details. Saved so we don't have to recalculate every on every on_paint unless size has changed. */ + this.lastLabelHeight = 0; + /** @private @type {number} The first played ratio used on the timeline in Details. */ + this.timelineFirstPlayedRatio = 0; + /** @private @type {number} The last played ratio used on the timeline in Details. */ + this.timelineLastPlayedRatio = 0; + /** @public @type {string} The path of the current playing album directory, used for art caching purposes on_playback_new_track. */ + this.currentAlbumFolder = ''; + /** @public @type {string} The path of the last played album directory, used for art caching purposes on_playback_new_track. */ + this.lastAlbumFolder = ''; + /** @public @type {string} %album% tag of the current playing album, used for art caching purposes on_playback_new_track. */ + this.lastAlbumFolderTag = ''; + /** @public @type {string} The disc number of the last played album directory, used for art caching purposes on_playback_new_track. */ + this.lastAlbumDiscNumber = ''; + /** @public @type {string} The vinyl side of the last played album, used for art caching purposes on_playback_new_track. */ + this.lastAlbumVinylSide = ''; + /** @public @type {string} The date and time of the current last played album, used for art caching purposes on_playback_new_track. */ + this.currentLastPlayed = ''; + /** @public @type {string} Displays the active playlist of the current playing track in the metadata grid in Details. */ + this.playingPlaylist = ''; + /** @public @type {number} Saves last playback order. */ + this.lastPlaybackOrder = 0; + /** @public @type {boolean} Saves active full width lyrics layout via pref.lyricsLayout. */ + this.lyricsLayoutFullWidth = false; + /** @public @type {boolean} Is the song from a streaming source? */ + this.isStreaming = false; + /** @public @type {boolean} Is the song playing from a CD? */ + this.isPlayingCD = false; + /** @private @type {boolean} Used to restore theme state after custom [%GR_THEME%] or [%GR_STYLE%] or [%GR_PRESET%] usage. */ + this.themeRestoreState = false; + /** @public @type {boolean} When active styles match any theme presets, used for the notification popup in the showThemePresetIndicator. */ + this.themePresetMatchMode = false; + /** @public @type {boolean} Used to hide theme preset indicator under certain conditions. */ + this.themePresetIndicator = true; + /** @public @type {string} The name of the current theme preset. */ + this.themePresetName = ''; + /** @public @type {string} The text of the theme notification. */ + this.themeNotification = ''; + /** @public @type {boolean} When no artwork, don't set themeColor every redraw. */ + this.themeColorSet = false; + /** @public @type {boolean} The state to override condition in getRandomThemeColor() when using "Generate new color" from context menu. */ + this.getRandomThemeColorContextMenu = false; + /** @public @type {boolean} Only use default theme when noArtwork was found. */ + this.noArtwork = false; + /** @public @type {boolean} Only load theme colors when newTrackFetchingArtwork = true. */ + this.newTrackFetchingArtwork = false; + /** @public @type {boolean} The state when new album art / disc art loaded and other things finished, used for smoother Playlist auto-scrolling. */ + this.newTrackFetchingDone = false; + /** @public @type {boolean} The state when Library should not call window.Reload() from panel.set() -> panel.load(), i.e when saving theme settings or restoring theme backup. */ + this.libraryCanReload = true; + /** @public @type {boolean} The state if grMain.ui.initTheme() needs to be fully executed to save performance. */ + this.initThemeFull = false; + /** @private @type {boolean} The state to skip most grMain.ui.initTheme() and get grMain.color.getThemeColors(grMain.ui.albumArt), mostly used for theme presets to prevent double inits. */ + this.initThemeSkip = false; + /** @private @type {boolean} The state when the theme is loading on startup or reload. */ + this.loadingTheme = false; + /** @public @type {boolean} The state when the theme has completely loaded, used for pseudo delay background logo mask on startup or reload. */ + this.loadingThemeComplete = false; + // #endregion + + // * TIMERS * // + // #region TIMERS + /** @public @type {number} The setTimeout ID for cycling album art. */ + this.albumArtTimeout = null; + /** @public @type {number} The timer when disc art spins while song is playing. */ + this.discArtRotationTimer = null; + /** @public @type {number} The timer interval for the biography auto-download. */ + this.autoDownloadBioTimer = null; + /** @public @type {number} The timer interval for the lyrics auto-download. */ + this.autoDownloadLyricsTimer = null; + /** @public @type {number} The setTimeout ID for hiding cursor. */ + this.hideCursorTimeout = null; + /** @public @type {number} The timer of progress bar. */ + this.progressBarTimer = null; + /** @public @type {number} The timer interval between progress bar updates. */ + this.progressBarTimerInterval = null; + /** @public @type {number} The timer for style auto random preset. */ + this.presetAutoRandomModeTimer = null; + /** @public @type {number} The timer for theme preset indicator. */ + this.presetIndicatorTimer = null; + /** @public @type {number} The timer for style auto color in Random theme. */ + this.randomThemeAutoColorTimer = null; + /** @public @type {number} The 10 minute timer for theme day/night mode. */ + this.themeDayNightModeTimer = null; + // #endregion + + // * DEBUG * // + // #region DEBUG + /** @public @type {number} Shows the image alpha in showThemeDebugOverlay. */ + this.blendedImgAlpha = 0; + /** @public @type {number} Shows the image blur in showThemeDebugOverlay. */ + this.blendedImgBlur = 0; + /** @public @type {string} Shows the col.primary in showThemeDebugOverlay. */ + this.selectedPrimaryColor = ''; + /** @public @type {string} Shows the col.primary_alt in showThemeDebugOverlay. */ + this.selectedPrimaryColor2 = ''; + /** @public @type {Array} Used in drawDebugRectAreas(). */ + this.repaintRects = []; + /** @public @type {number} Used in RepaintRectAreas(). */ + this.repaintRectCount = 0; + /** @public @type {boolean} The auto-download bio state, auto-downloading of artist biographies during shuffle playback with a 5-seconds timer. */ + this.autoDownloadBio = false; + /** @public @type {boolean} The auto-download lyrics state, auto-downloading of lyrics during repeat playlist playback with a 15-seconds timer. */ + this.autoDownloadLyrics = false; + /** @public @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ + this.traceCall = false; + /** @public @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ + this.traceOnMove = false; + /** @public @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ + this.traceListPerformance = false; + /** @public @type {boolean} Spams the console with draw times. */ + this.showDrawTiming = false; + /** @public @type {boolean} Spams the console with every section of the draw code to determine bottlenecks. */ + this.showExtraDrawTiming = false; + /** @public @type {boolean} Spams the console with debug timing. */ + this.showDebugTiming = false; + /** @public @type {boolean} Spams the console with memory statistic. */ + this.showRamUsage = false; + /** @public @type {boolean} Draws all window.RepaintRect as red outlines in the theme. */ + this.drawRepaintRects = false; + /** @public @type {boolean} Spams the console with panel trace call. */ + this.showPanelTraceCall = false; + /** @public @type {boolean} Spams the console with panel trace on move. */ + this.showPanelTraceOnMove = false; + /** @public @type {boolean} Spams the console with playlist list performance. */ + this.showPlaylistTraceListPerf = false; + // #endregion + } - if (!themeColorSet) { - setThemeColors(); - themeColorSet = true; + // * MAIN - PUBLIC METHODS - DRAW * // + // #region MAIN - PUBLIC METHODS - DRAW + /** + * Draws all main user interface parts in the correct order. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawMain(gr) { + const drawTimingStart = (this.showDrawTiming || this.showExtraDrawTiming) && new Date(); + + this.drawBackgrounds(gr); + + // * UIHacks aero glass shadow frame fix, needed for style Blend in Details + if (UIHacks.Aero.Effect === 2) gr.DrawLine(0, 0, this.ww, 0, 1, grCol.bg); + + this.drawDetailsBlend(gr); + this.drawPanels(gr); + this.drawAlbumArt(gr); + this.drawNoAlbumArt(gr) + this.drawHiResAudioLogo(gr); + this.drawPauseBtn(gr); + this.drawJumpSearch(gr); + this.drawDetailsMetadataGrid(gr); + this.drawDetailsBandLogo(gr); + this.drawDetailsLabelLogo(gr); + this.drawLyrics(gr); + this.drawStyles(gr); + this.drawThemeNotification(gr); + this.drawPanelShadows(gr); + this.drawTopMenuBar(gr); + this.drawLowerBar(gr); + this.drawCustomThemeMenu(gr); + this.drawMetadataGridMenu(gr); + this.drawStyledTooltips(gr); + this.drawStartupBackground(gr); + + // * UIHacks aero glass shadow frame fix + if (UIHacks.Aero.Effect === 2 && (!this.loadingThemeComplete && (grSet.styleBlend || grSet.styleBlend2)) || !grSet.styleBlend && !grSet.styleBlend2) { + gr.DrawLine(0, 0, this.ww, 0, 1, !this.loadingThemeComplete ? grCol.loadingThemeBg : grCol.uiHacksFrame); + if (grSet.styleDefault) gr.DrawLine(this.ww, this.wh - 1, 0, this.wh - 1, 1, !this.loadingThemeComplete ? grCol.loadingThemeBg : grCol.uiHacksFrame); + else if (grSet.styleGradient || grSet.styleGradient2) { + gr.DrawLine(0, 0, this.ww, 0, 1, grCol.bg); + gr.FillGradRect(-0.5, 0, this.ww, 1, grSet.styleGradient2 ? -200 : 0, grSet.styleGradient2 ? 0 : grCol.styleGradient, grSet.styleGradient2 ? grCol.styleGradient2 : 0, 0.5); + } } - } - gr.FillSolidRect(0, 0, ww, wh, col.bg); - // * ALBUM ART BACKGROUND * // - if (pref.albumArtBg !== 'none' && !displayDetails) { - gr.FillSolidRect(0, albumArtSize.y, pref.albumArtBg === 'full' || pref.layout === 'artwork' ? ww : albumArtSize.x, albumArtSize.h, col.detailsBg); + this.drawDebugThemeOverlay(gr); + this.drawDebugTiming(drawTimingStart); + this.drawDebugRectAreas(gr); + this.repaintRectCount = 0; } - // * DETAILS BACKGROUND * // - if (fb.IsPlaying && albumArt && displayDetails) { - if ((isStreaming && noArtwork || !albumArt && noArtwork)) { - gr.FillSolidRect(0, geo.topMenuHeight, ww, wh - geo.topMenuHeight - geo.lowerBarHeight, col.detailsBg); - } else { - gr.FillSolidRect(0, albumArtSize.y, pref.noDiscArtBg && !discArt ? ww : albumArtSize.x, albumArtSize.h, col.detailsBg); + /** + * Draws the Main and Details background. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawBackgrounds(gr) { + gr.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); + gr.SetSmoothingMode(SmoothingMode.None); + + // * MAIN BACKGROUND * // + if (!this.albumArt && this.noArtwork) { // We use noArtwork to prevent flashing of blue default theme + this.initNoAlbumArtSize(); + + if (!this.themeColorSet) { + grm.color.setThemeColors(); + this.themeColorSet = true; + } } - } + gr.FillSolidRect(0, 0, this.ww, this.wh, grCol.bg); - gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); -} + // * ALBUM ART BACKGROUND * // + if (grSet.albumArtBg !== 'none' && !this.displayDetails) { + gr.FillSolidRect(0, this.albumArtSize.y, grSet.albumArtBg === 'full' || grSet.layout === 'artwork' ? this.ww : this.albumArtSize.x, this.albumArtSize.h, grCol.detailsBg); + } + // * DETAILS BACKGROUND * // + if (fb.IsPlaying && this.displayDetails || this.displayLyrics && grSet.lyricsLayout === 'full') { + if (this.isStreaming && this.noArtwork || !this.albumArt && this.noArtwork || this.displayLyrics && grSet.lyricsLayout === 'full') { + gr.FillSolidRect(0, this.topMenuHeight, this.ww, this.wh - this.topMenuHeight - this.lowerBarHeight, grCol.detailsBg); + } else { + gr.FillSolidRect(0, this.albumArtSize.y, grSet.noDiscArtBg && !this.discArt ? this.ww : this.albumArtSize.x, this.albumArtSize.h, grCol.detailsBg); + } + } -/** - * Draws the style Blend in the Details panel. - * @param {GdiGraphics} gr - */ -function drawDetailsBlend(gr) { - if (pref.styleBlend && albumArt && blendedImg && (!displayPlaylist && !displayLibrary && !displayBiography && pref.layout === 'default' || - !displayPlaylistArtwork && !displayLibrary && pref.layout === 'artwork')) { - gr.DrawImage(blendedImg, 0, 0, ww, wh, 0, 0, blendedImg.Width, blendedImg.Height); + gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); } -} - -/** - * Draws the Playlist, Library and Biography panels. - * @param {GdiGraphics} gr - */ -function drawPanels(gr) { - if (pref.layout === 'default' || pref.layout === 'artwork') { - if (displayLibrary) { - const drawLibraryProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> library'); - libraryPanel.on_paint(gr); - if (drawLibraryProfiler) drawLibraryProfiler.Print(); - } - if (pref.layout === 'default' && displayPlaylist || pref.layout === 'artwork' && displayPlaylistArtwork || displayLibrarySplit()) { - const drawPlaylistProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> playlist'); - playlist.on_paint(gr); - if (drawPlaylistProfiler) drawPlaylistProfiler.Print(); + /** + * Draws the style Blend in the Details panel. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDetailsBlend(gr) { + if (grSet.styleBlend && this.albumArt && grCol.imgBlended && this.displayDetails) { + gr.DrawImage(grCol.imgBlended, 0, 0, this.ww, this.wh, 0, 0, grCol.imgBlended.Width, grCol.imgBlended.Height); } - if (displayBiography) { - const drawBiographyProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> biography'); - biographyPanel.on_paint(gr); - if (drawBiographyProfiler) drawBiographyProfiler.Print(); - } - } - else if (pref.layout === 'compact' && displayPlaylist) { - const drawPlaylistProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> playlist'); - playlist.on_paint(gr); - if (drawPlaylistProfiler) drawPlaylistProfiler.Print(); } -} - - -/** - * Draws the big album art on the left side. - * @param {GdiGraphics} gr - */ -function drawAlbumArt(gr) { - const displayAlbumArt = - pref.layout === 'default' && (pref.playlistLayout !== 'full' && displayPlaylist && !displayBiography - || pref.libraryLayout === 'normal' && displayLibrary || pref.displayLyrics) - || !displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography; - - const displayDetails = (pref.layout === 'artwork' ? !displayPlaylistArtwork : !displayPlaylist) && !displayLibrary && !displayBiography; - - if (!fb.IsPlaying || !displayAlbumArt || displayLibrarySplit()) return; - - const drawAlbumArtProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> album art'); - // * BIG ALBUM ART - NEEDS TO BE DRAWN AFTER ALL BLENDING IS DONE, I.E AFTER PLAYLIST * // - if (!noAlbumArtStub) { - if (discArt && !discArtRotation && !displayPlaylist && !displayLibrary && pref.displayDiscArt) { - createDiscArtRotation(); - } - if (albumArt && (albumArtScaled || discArtRotation) && !displayBiography && !displayPlaylistArtwork && - (discArt && pref.displayDiscArt && !displayPlaylist && !displayLibrary)) { - shadowImg && gr.DrawImage(shadowImg, -geo.discArtShadow, albumArtSize.y - geo.discArtShadow, shadowImg.Width, shadowImg.Height, 0, 0, shadowImg.Width, shadowImg.Height); - // gr.DrawRect(-geo.discArtShadow, albumArtSize.y - geo.discArtShadow, shadowImg.Width, shadowImg.Height, 1, RGBA(0,0,255,125)); // Viewing border line - } - if (albumArt && albumArtScaled) { - if (!pref.discArtOnTop || pref.displayLyrics) { - if (discArtRotation && !displayPlaylist && !displayLibrary) { - drawDiscArt(gr); - } - if (discArtRotation && !displayPlaylist && !displayLibrary && pref.detailsAlbumArtDiscAreaOpacity !== 255) { // Do not use opacity if image is a booklet, i.e albumArtSize.w > ww * 0.66 - createDiscArtAlbumArtMask(gr, albumArtSize.x, albumArtSize.y, albumArtSize.w, albumArtSize.h, 0, 0, albumArtScaled.Width, albumArtScaled.Height, 0, displayDetails && !pref.displayLyrics && albumArtSize.w < ww * 0.66 ? pref.detailsAlbumArtDiscAreaOpacity : 255); - } else { - gr.DrawImage(albumArtScaled, albumArtSize.x, albumArtSize.y, albumArtSize.w, albumArtSize.h, 0, 0, albumArtScaled.Width, albumArtScaled.Height, 0, displayDetails && !pref.displayLyrics && albumArtSize.w < ww * 0.66 ? pref.detailsAlbumArtOpacity : 255); - } - } else { // Draw discArt on top of front cover - gr.DrawImage(albumArtScaled, albumArtSize.x, albumArtSize.y, albumArtSize.w, albumArtSize.h, 0, 0, albumArtScaled.Width, albumArtScaled.Height); - if (discArtRotation && !displayPlaylist && !displayLibrary) { - drawDiscArt(gr); - } + /** + * Draws the Playlist, Library and Biography panels. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawPanels(gr) { + if (grSet.layout === 'default' || grSet.layout === 'artwork') { + if (this.displayLibrary) { + const drawLibraryProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> library'); + lib.call.on_paint(gr); + if (drawLibraryProfiler) drawLibraryProfiler.Print(); + } + if (grSet.layout === 'default' && this.displayPlaylist || grSet.layout === 'artwork' && this.displayPlaylistArtwork || this.displayLibrarySplit()) { + const drawPlaylistProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> playlist'); + pl.call.on_paint(gr); + if (drawPlaylistProfiler) drawPlaylistProfiler.Print(); + } + if (this.displayBiography) { + const drawBiographyProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> biography'); + bio.call.on_paint(gr); + if (drawBiographyProfiler) drawBiographyProfiler.Print(); } } - else if (discArtRotation && pref.displayDiscArt && !displayPlaylist && !displayLibrary && !displayBiography && !pref.displayLyrics) { - drawDiscArt(gr); // Disc art, but no album art + else if (grSet.layout === 'compact' && this.displayPlaylist) { + const drawPlaylistProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> playlist'); + pl.call.on_paint(gr); + if (drawPlaylistProfiler) drawPlaylistProfiler.Print(); } } - if (drawAlbumArtProfiler) drawAlbumArtProfiler.Print(); -} - - -/** - * Draws the no album art stub when no album cover exists. - * @param {GdiGraphics} gr - */ -function drawNoAlbumArt(gr) { - const noAlbumArtLayoutDefault = - pref.layout === 'default' && !displayLibrarySplit() && (displayPlaylist && pref.playlistLayout !== 'full' && !displayBiography || displayLibrary && pref.libraryLayout === 'normal'); - - const noAlbumArtLayoutArtwork = - pref.layout === 'artwork' && !displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography; - - if (!albumArt && noArtwork && fb.IsPlaying) { - if (noAlbumArtLayoutDefault || noAlbumArtLayoutArtwork) { - // * Clear previous artwork related stuff - noAlbumArtStub = true; - albumArt = null; - discArt = null; - discArtCover = null; - discArtArray = []; - discArtArrayCover = []; + /** + * Draws the big album art on the left side. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawAlbumArt(gr) { + const displayAlbumArt = + grSet.layout === 'default' && (grSet.playlistLayout !== 'full' && this.displayPlaylist && !this.displayBiography + || grSet.libraryLayout === 'normal' && this.displayLibrary || this.displayLyrics) + || !this.displayPlaylist && !this.displayPlaylistArtwork && !this.displayLibrary && !this.displayBiography; - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; + if (!fb.IsPlaying || !displayAlbumArt || this.displayLibrarySplit()) return; - const bgWidth = - pref.lyricsLayout === 'full' && pref.displayLyrics || pref.layout === 'artwork' ? ww : - pref.panelWidthAuto ? noAlbumArtSize : - ww * 0.5; + const drawAlbumArtProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> album art'); - const bgHeight = noAlbumArtSize; - - const noteWidth = - pref.layout === 'artwork' ? ww : - pref.panelWidthAuto ? noAlbumArtSize : - ww * 0.5; + // * BIG ALBUM ART - NEEDS TO BE DRAWN AFTER ALL BLENDING IS DONE, I.E AFTER PLAYLIST * // + if (!this.noAlbumArtStub) { + if (this.displayDetails && grSet.displayDiscArt && this.discArt && !this.discArtRotation) { + this.createDiscArtRotation(); + } + if (this.displayDetails && grSet.displayDiscArt && this.albumArt && (this.albumArtScaled || this.discArtRotation) && this.discArt) { + this.shadowImg && gr.DrawImage(this.shadowImg, -this.discArtShadow, this.albumArtSize.y - this.discArtShadow, this.shadowImg.Width, this.shadowImg.Height, 0, 0, this.shadowImg.Width, this.shadowImg.Height); + } + if (this.albumArt && this.albumArtScaled) { + if (!grSet.discArtOnTop || this.displayLyrics) { + if (this.discArtRotation) { + this.drawDiscArt(gr); + } + if (this.displayDetails && this.discArtRotation && grSet.detailsAlbumArtDiscAreaOpacity !== 255) { // Do not use opacity if image is a booklet, i.e this.albumArtSize.w > this.ww * 0.66 + this.createDiscArtAlbumArtMask(gr, this.albumArtSize.x, this.albumArtSize.y, this.albumArtSize.w, this.albumArtSize.h, 0, 0, this.albumArtScaled.Width, this.albumArtScaled.Height, 0, this.displayDetails && !this.displayLyrics && this.albumArtSize.w < this.ww * 0.66 ? grSet.detailsAlbumArtDiscAreaOpacity : 255); + } else { + gr.DrawImage(this.albumArtScaled, this.albumArtSize.x, this.albumArtSize.y, this.albumArtSize.w, this.albumArtSize.h, 0, 0, this.albumArtScaled.Width, this.albumArtScaled.Height, 0, this.displayDetails && !this.displayLyrics && this.albumArtSize.w < this.ww * 0.66 ? grSet.detailsAlbumArtOpacity : 255); + } + } else { // Draw discArt on top of front cover + gr.DrawImage(this.albumArtScaled, this.albumArtSize.x, this.albumArtSize.y, this.albumArtSize.w, this.albumArtSize.h, 0, 0, this.albumArtScaled.Width, this.albumArtScaled.Height); + if (this.discArtRotation) { + this.drawDiscArt(gr); + } + } + } + } - const noteHeight = noAlbumArtSize + ft.no_album_art_stub.Height * 0.5 - SCALE(14); + if (drawAlbumArtProfiler) drawAlbumArtProfiler.Print(); + } - // * Stub background - gr.FillSolidRect(0, geo.topMenuHeight, albumArtSize.x, bgHeight, !pref.albumArtBg ? g_pl_colors.bg : col.bg); - gr.FillSolidRect(albumArtSize.x, geo.topMenuHeight, bgWidth, bgHeight, g_pl_colors.bg); - if (!pref.displayLyrics) { - gr.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); - gr.DrawString('\uf001', ft.no_album_art_stub, col.noAlbumArtStub, albumArtSize.x, 0, noteWidth, noteHeight, StringFormat(1, 1)); + /** + * Draws the no album art stub when no album cover exists. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawNoAlbumArt(gr) { + const noAlbumArtLayoutDefault = + grSet.layout === 'default' && !this.displayLibrarySplit() && (this.displayPlaylist && grSet.playlistLayout !== 'full' && !this.displayBiography || this.displayLibrary && grSet.libraryLayout === 'normal'); + + const noAlbumArtLayoutArtwork = + grSet.layout === 'artwork' && !this.displayPlaylist && !this.displayPlaylistArtwork && !this.displayLibrary && !this.displayBiography; + + if (!this.albumArt && this.noArtwork && fb.IsPlaying) { + if (noAlbumArtLayoutDefault || noAlbumArtLayoutArtwork) { + // * Clear previous artwork related stuff + this.noAlbumArtStub = true; + this.albumArt = null; + this.discArt = null; + this.discArtCover = null; + this.discArtArray = []; + this.discArtArrayCover = []; + + const noAlbumArtSize = this.wh - this.topMenuHeight - this.lowerBarHeight; + + const bgWidth = + grSet.lyricsLayout === 'full' && this.displayLyrics || grSet.layout === 'artwork' ? this.ww : + grSet.panelWidthAuto ? noAlbumArtSize : + this.ww * 0.5; + + const bgHeight = noAlbumArtSize; + + const noteWidth = + grSet.layout === 'artwork' ? this.ww : + grSet.panelWidthAuto ? noAlbumArtSize : + this.ww * 0.5; + + const noteHeight = noAlbumArtSize + grFont.noAlbumArtStub.Height * 0.5 - SCALE(14); + + // * Stub background + gr.FillSolidRect(0, this.topMenuHeight, this.albumArtSize.x, bgHeight, !grSet.albumArtBg ? pl.col.bg : grCol.bg); + gr.FillSolidRect(this.albumArtSize.x, this.topMenuHeight, bgWidth, bgHeight, pl.col.bg); + if (!this.displayLyrics) { + gr.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); + gr.DrawString('\uf001', grFont.noAlbumArtStub, grCol.noAlbumArtStub, this.albumArtSize.x, 0, noteWidth, noteHeight, StringFormat(1, 1)); + } } } + else if (grSet.panelWidthAuto && !fb.IsPlaying) { + this.noAlbumArtStub = true; // * Needed on_playback_stop when noAlbumArtStub was playing to reposition all panels correctly + } + else { + this.noAlbumArtStub = false; + } } - else if (pref.panelWidthAuto && !fb.IsPlaying) { - noAlbumArtStub = true; // * Needed on_playback_stop when noAlbumArtStub was playing to reposition all panels correctly - } - else { - noAlbumArtStub = false; - } -} + /** + * Draws the disc art in Details. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDiscArt(gr) { + if (grSet.layout !== 'default' || !grSet.displayDiscArt || !this.displayDetails || + this.discArtSize.y < this.albumArtSize.y || this.discArtSize.h > this.albumArtSize.h) { + return; + } -/** - * Draws the disc art in Details. - * @param {GdiGraphics} gr - */ -function drawDiscArt(gr) { - if (pref.layout === 'default' && pref.displayDiscArt && discArtSize.y >= albumArtSize.y && discArtSize.h <= albumArtSize.h) { - const drawDiscArtProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> disc art'); - const discArtImg = discArtArray[discArtRotationIndex] || discArtRotation; - gr.DrawImage(discArtImg, discArtSize.x, discArtSize.y, discArtSize.w, discArtSize.h, 0, 0, discArtImg.Width, discArtImg.Height, 0); + const drawDiscArtProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> disc art'); + const discArtImg = this.discArtArray[this.discArtRotationIndex] || this.discArtRotation; + gr.DrawImage(discArtImg, this.discArtSize.x, this.discArtSize.y, this.discArtSize.w, this.discArtSize.h, 0, 0, discArtImg.Width, discArtImg.Height, 0); - if (['cdAlbumCover', 'vinylAlbumCover'].includes(pref.discArtStub) && discArtCover && !discArtFound && (!pref.noDiscArtStub || pref.showDiscArtStub)) { - const discArtImgCover = discArtArrayCover[discArtRotationIndexCover] || discArtRotationCover; - gr.DrawImage(discArtImgCover, discArtSize.x, discArtSize.y, discArtSize.w, discArtSize.h, 0, 0, discArtImg.Width, discArtImg.Height, 0); + if (['cdAlbumCover', 'vinylAlbumCover'].includes(grSet.discArtStub) && this.discArtCover && !this.discArtFound && (!grSet.noDiscArtStub || grSet.showDiscArtStub)) { + const discArtImgCover = this.discArtArrayCover[this.discArtRotationIndexCover] || this.discArtRotationCover; + gr.DrawImage(discArtImgCover, this.discArtSize.x, this.discArtSize.y, this.discArtSize.w, this.discArtSize.h, 0, 0, discArtImg.Width, discArtImg.Height, 0); } if (drawDiscArtProfiler) drawDiscArtProfiler.Print(); } - // Show full background when displaying the Lyrics panel and lyrics layout is in full width - if (pref.displayLyrics && pref.lyricsLayout === 'full' && pref.layout === 'default' && !displayLibrary && !displayPlaylist && !displayBiography) { - gr.FillSolidRect(albumArtSize.x + albumArtSize.w - SCALE(1), albumArtSize.y, albumArtSize.x + SCALE(2), albumArtSize.h, col.detailsBg); - } -} - - -/** - * Draws the Hi-Res Audio logo on album art. - * @param {GdiGraphics} gr - */ -function drawHiResAudioLogo(gr) { - const trackIsHiRes = - (Number($('$info(bitspersample)', fb.GetNowPlaying())) > 16 || Number($('$info(bitrate)', fb.GetNowPlaying())) > 1411); - - const displayHiResAudioLogo = - trackIsHiRes && pref.showHiResAudioBadge && pref.layout !== 'compact' && - (displayPlaylist && !displayPlaylistArtwork && !displayLibrarySplit() && !displayBiography && (pref.playlistLayout === 'normal' || pref.displayLyrics) || - !displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography || - displayLibrary && pref.libraryLayout === 'normal' && pref.layout === 'default'); - - if (!displayHiResAudioLogo) return; - - const logoPath = `${imagesPath}misc\\`; - const plus4k = RES_4K ? '4K-' : ''; - const plusRound = pref.hiResAudioBadgeRound ? '-round' : ''; + /** + * Draws the Hi-Res Audio logo on album art. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawHiResAudioLogo(gr) { + const trackIsHiRes = + (Number($('$info(bitspersample)', fb.GetNowPlaying())) > 16 || Number($('$info(bitrate)', fb.GetNowPlaying())) > 1411); - if (pref.hiResAudioBadgeSize === 'small') paths.hiResAudioImage = `${logoPath}${plus4k}hi-res-audio-small${plusRound}.png`; - else if (pref.hiResAudioBadgeSize === 'normal') paths.hiResAudioImage = `${logoPath}${plus4k}hi-res-audio-normal${plusRound}.png`; - else if (pref.hiResAudioBadgeSize === 'large') paths.hiResAudioImage = `${logoPath}${plus4k}hi-res-audio-large${plusRound}.png`; + const displayHiResAudioLogo = + trackIsHiRes && grSet.showHiResAudioBadge && grSet.layout !== 'compact' && + (this.displayPlaylist && !this.displayPlaylistArtwork && !this.displayLibrarySplit() && !this.displayBiography && (grSet.playlistLayout === 'normal' || this.displayLyrics) || + !this.displayPlaylist && !this.displayPlaylistArtwork && !this.displayLibrary && !this.displayBiography || + this.displayLibrary && grSet.libraryLayout === 'normal' && grSet.layout === 'default'); - hiResAudioImg = gdi.Image(paths.hiResAudioImage); + if (!displayHiResAudioLogo) return; - const x = - pref.hiResAudioBadgePos === 'topleft' ? albumArtSize.x + SCALE(20) : - pref.hiResAudioBadgePos === 'topright' ? albumArtSize.x + albumArtSize.w - hiResAudioImg.Width - SCALE(20) : - pref.hiResAudioBadgePos === 'bottomleft' ? albumArtSize.x + SCALE(40) : - pref.hiResAudioBadgePos === 'bottomright' ? albumArtSize.x + albumArtSize.w - hiResAudioImg.Width - SCALE(40) : ''; + this.hiResAudioLogo = gdi.Image(grPath.hiResAudioLogoPath()); - const y = - pref.hiResAudioBadgePos === 'topleft' || pref.hiResAudioBadgePos === 'topright' ? albumArtSize.y + SCALE(20) : - albumArtSize.y + albumArtSize.h - hiResAudioImg.Height - SCALE(40); - - gr.DrawImage(hiResAudioImg, x, y, hiResAudioImg.Width, hiResAudioImg.Height, 0, 0, hiResAudioImg.Width, hiResAudioImg.Height); -} + const x = + grSet.hiResAudioBadgePos === 'topleft' ? this.albumArtSize.x + SCALE(20) : + grSet.hiResAudioBadgePos === 'topright' ? this.albumArtSize.x + this.albumArtSize.w - this.hiResAudioLogo.Width - SCALE(20) : + grSet.hiResAudioBadgePos === 'bottomleft' ? this.albumArtSize.x + SCALE(40) : + grSet.hiResAudioBadgePos === 'bottomright' ? this.albumArtSize.x + this.albumArtSize.w - this.hiResAudioLogo.Width - SCALE(40) : ''; + const y = + grSet.hiResAudioBadgePos === 'topleft' || grSet.hiResAudioBadgePos === 'topright' ? this.albumArtSize.y + SCALE(20) : + this.albumArtSize.y + this.albumArtSize.h - this.hiResAudioLogo.Height - SCALE(40); -/** - * Draws the pause button centered on album art. - * @param {GdiGraphics} gr - */ -function drawPauseBtn(gr) { - if (loadingThemeComplete && pref.showPause && fb.IsPaused && !presetIndicatorTimer && !doubleClicked - && - (pref.layout === 'default' && (displayPlaylist && pref.playlistLayout !== 'full' && !displayLibrary && !displayBiography || - displayLibrary && pref.libraryLayout === 'normal' && !displayPlaylist || !displayPlaylist && !displayLibrary && !displayBiography) - || - pref.layout === 'artwork' && !displayPlaylist && !displayPlaylistArtwork && !displayLibrary && !displayBiography)) { - pauseBtn.draw(gr); + gr.DrawImage(this.hiResAudioLogo, x, y, this.hiResAudioLogo.Width, this.hiResAudioLogo.Height, 0, 0, this.hiResAudioLogo.Width, this.hiResAudioLogo.Height); } -} - - -/** - * Draws the jump search on the right centered side. - * @param {GdiGraphics} gr - */ -function drawJumpSearch(gr) { - jumpSearch.setY(Math.round(wh * 0.5 - geo.topMenuHeight - geo.lowerBarHeight)); - jumpSearch.draw(gr); -} - -/** - * Draws the metadata grid on the left side in the Details panel. - * @param {GdiGraphics} gr - */ -function drawDetailsMetadataGrid(gr) { - gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); - gr.SetInterpolationMode(InterpolationMode.HighQualityBicubic); - - const displayDetails = (pref.layout === 'artwork' ? !displayPlaylistArtwork && displayPlaylist : !displayPlaylist) && !displayLibrary && !displayBiography; - if (!displayDetails || pref.lyricsLayout === 'full' && pref.displayLyrics) return; - - const drawMetadataGridProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> metadata grid'); - - const gridArtistFontSize = pref[`gridArtistFontSize_${pref.layout}`]; - const showGridArtist = pref[`showGridArtist_${pref.layout}`]; - const showGridTrackNum = pref[`showGridTrackNum_${pref.layout}`]; - const showGridTitle = pref[`showGridTitle_${pref.layout}`]; - const showGridTimeline = pref[`showGridTimeline_${pref.layout}`]; - const showGridArtistFlags = pref[`showGridArtistFlags_${pref.layout}`]; - const showGridReleaseFlags = pref[`showGridReleaseFlags_${pref.layout}`]; - const showGridChannelLogo = pref[`showGridChannelLogo_${pref.layout}`]; - const showGridCodecLogo = pref[`showGridCodecLogo_${pref.layout}`]; - - const marginLeft = SCALE(pref.layout !== 'default' ? 20 : 40); - const marginRight = SCALE(20); - const textWidth = Math.round((!albumArt && discArt ? discArtSize.x : albumArtSize.x) - geo.discArtShadow - marginLeft - marginRight); - gridTop = albumArtSize.y ? albumArtSize.y + marginLeft : geo.topMenuHeight + marginLeft; - - // * DETAILS METADATA GRID * // - if (textWidth > 150) { - /** @type {MeasureStringInfo} */ - let txtRec; - let gridArtistTxtRec; - let gridTitleTxtRec; - let gridAlbumTxtRec; - - const drawArtist = (top) => { - if (!str.artist) return 0; - - const flagSizeWhiteSpaceTable = { - 24: [35, 29, 24, 18, 12, 6], - 22: [36, 30, 25, 19, 12, 6], - 20: [37, 31, 26, 20, 12, 6], - 19: [38, 32, 26, 20, 12, 6], - 18: [39, 32, 26, 20, 13, 6], - 17: [40, 33, 27, 20, 13, 6], - 16: [41, 34, 28, 21, 13, 6], - 15: [42, 35, 29, 22, 14, 6], - 14: [44, 36, 29, 22, 14, 6], - 13: [45, 37, 30, 23, 15, 6], - 12: [47, 39, 31, 24, 15, 7], - 11: [49, 41, 32, 25, 16, 7], - 10: [51, 43, 34, 26, 17, 7] - }; + /** + * Draws the pause button centered on album art. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawPauseBtn(gr) { + if (this.loadingThemeComplete && grSet.showPause && fb.IsPaused && !this.presetIndicatorTimer && !this.doubleClicked + && + (grSet.layout === 'default' && (this.displayPlaylist && grSet.playlistLayout !== 'full' && !this.displayLibrary && !this.displayBiography || + this.displayLibrary && grSet.libraryLayout === 'normal' && !this.displayPlaylist || this.displayDetails) + || + grSet.layout === 'artwork' && !this.displayPlaylist && !this.displayPlaylistArtwork && !this.displayLibrary && !this.displayBiography)) { + grm.pseBtn.draw(gr); + } + } - const flagSizeWhiteSpace = ' '.repeat( - flagSizeWhiteSpaceTable[gridArtistFontSize][ - flagImgs.length >= 6 ? 0 : - flagImgs.length === 5 ? 1 : - flagImgs.length === 4 ? 2 : - flagImgs.length === 3 ? 3 : - flagImgs.length === 2 ? 4 : - 5 - ] - ); + /** + * Draws the jump search on the right centered side. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawJumpSearch(gr) { + grm.jSearch.setY(Math.round(this.wh * 0.5 - this.topMenuHeight - this.lowerBarHeight)); + grm.jSearch.draw(gr); + } - const flagSize = - flagImgs.length >= 6 ? SCALE(84 + gridArtistFontSize * 6) : - flagImgs.length === 5 ? SCALE(70 + gridArtistFontSize * 5) : - flagImgs.length === 4 ? SCALE(56 + gridArtistFontSize * 4) : - flagImgs.length === 3 ? SCALE(42 + gridArtistFontSize * 3) : - flagImgs.length === 2 ? SCALE(28 + gridArtistFontSize * 2) : - flagImgs.length === 1 ? SCALE(14 + gridArtistFontSize) : ''; - - gridArtistTxtRec = gr.MeasureString(str.artist, ft.grd_artist, 0, 0, showGridArtistFlags && flagImgs.length ? textWidth - flagSize : textWidth, wh); - const gridArtistNumLines = Math.min(2, gridArtistTxtRec.Lines); - const gridArtistNumHeight = gr.CalcTextHeight(str.artist, ft.grd_artist) * gridArtistNumLines + 3; - const gridArtistHeight = gr.CalcTextHeight(str.artist, ft.grd_artist); - - // * Apply better anti-aliasing on smaller font sizes in HD res - gr.SetTextRenderingHint(!RES_4K && gridArtistFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); - DrawString(gr, showGridArtistFlags && flagImgs.length ? flagSizeWhiteSpace + str.artist : str.artist, ft.grd_artist, ['white', 'black', 'reborn', 'random'].includes(pref.theme) ? col.detailsText : pref.theme === 'cream' ? g_pl_colors.header_artist_normal : g_pl_colors.header_artist_playing, marginLeft, Math.round(top), textWidth, gridArtistNumHeight, g_string_format.trim_ellipsis_char); - - // * Artist flags - if (str.artist && flagImgs.length && showGridArtistFlags && displayDetails) { - const maxFlags = Math.min(flagImgs.length, 6); - let flagsLeft = marginLeft; - for (let i = 0; i < maxFlags; i++) { - gr.DrawImage(flagImgs[i], flagsLeft, Math.round(top - (flagImgs[i].Height / (gridArtistHeight + SCALE(2))) - (RES_4K ? 1 : 0)), flagImgs[i].Width + SCALE(gridArtistFontSize) - SCALE(26), gridArtistHeight + SCALE(2), 0, 0, flagImgs[i].Width, flagImgs[i].Height); - flagsLeft += flagImgs[i].Width + SCALE(gridArtistFontSize) - SCALE(18); + /** + * Draws the metadata grid on the left side in the Details panel. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDetailsMetadataGrid(gr) { + gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); + gr.SetInterpolationMode(InterpolationMode.HighQualityBicubic); + + if (!fb.IsPlaying || !this.displayDetails || grSet.lyricsLayout === 'full' && this.displayLyrics) return; + + const drawMetadataGridProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> metadata grid'); + + const gridArtistFontSize = grSet[`gridArtistFontSize_${grSet.layout}`]; + const showGridArtist = grSet[`showGridArtist_${grSet.layout}`]; + const showGridTrackNum = grSet[`showGridTrackNum_${grSet.layout}`]; + const showGridTitle = grSet[`showGridTitle_${grSet.layout}`]; + const showGridTimeline = grSet[`showGridTimeline_${grSet.layout}`]; + const showGridArtistFlags = grSet[`showGridArtistFlags_${grSet.layout}`]; + const showGridReleaseFlags = grSet[`showGridReleaseFlags_${grSet.layout}`]; + const showGridChannelLogo = grSet[`showGridChannelLogo_${grSet.layout}`]; + const showGridCodecLogo = grSet[`showGridCodecLogo_${grSet.layout}`]; + + const marginLeft = SCALE(grSet.layout !== 'default' ? 20 : 40); + const marginRight = SCALE(20); + const textWidth = Math.round((!this.albumArt && this.discArt ? this.discArtSize.x : this.albumArtSize.x) - this.discArtShadow - marginLeft - marginRight); + this.gridTop = this.albumArtSize.y ? this.albumArtSize.y + marginLeft : this.topMenuHeight + marginLeft; + + // * DETAILS METADATA GRID * // + if (textWidth > 150) { + let txtRec; + let gridArtistTxtRec; + let gridTitleTxtRec; + let gridAlbumTxtRec; + + const drawArtist = (top) => { + if (!grStr.artist) return 0; + + const flagSizeWhiteSpaceTable = { + 24: [35, 29, 24, 18, 12, 6], + 22: [36, 30, 25, 19, 12, 6], + 20: [37, 31, 26, 20, 12, 6], + 19: [38, 32, 26, 20, 12, 6], + 18: [39, 32, 26, 20, 13, 6], + 17: [40, 33, 27, 20, 13, 6], + 16: [41, 34, 28, 21, 13, 6], + 15: [42, 35, 29, 22, 14, 6], + 14: [44, 36, 29, 22, 14, 6], + 13: [45, 37, 30, 23, 15, 6], + 12: [47, 39, 31, 24, 15, 7], + 11: [49, 41, 32, 25, 16, 7], + 10: [51, 43, 34, 26, 17, 7] + }; + + const flagSizeWhiteSpace = ' '.repeat( + flagSizeWhiteSpaceTable[gridArtistFontSize][ + this.flagImgs.length >= 6 ? 0 : + this.flagImgs.length === 5 ? 1 : + this.flagImgs.length === 4 ? 2 : + this.flagImgs.length === 3 ? 3 : + this.flagImgs.length === 2 ? 4 : + 5 + ] + ); + + const flagSize = + this.flagImgs.length >= 6 ? SCALE(84 + gridArtistFontSize * 6) : + this.flagImgs.length === 5 ? SCALE(70 + gridArtistFontSize * 5) : + this.flagImgs.length === 4 ? SCALE(56 + gridArtistFontSize * 4) : + this.flagImgs.length === 3 ? SCALE(42 + gridArtistFontSize * 3) : + this.flagImgs.length === 2 ? SCALE(28 + gridArtistFontSize * 2) : + this.flagImgs.length === 1 ? SCALE(14 + gridArtistFontSize) : ''; + + gridArtistTxtRec = gr.MeasureString(grStr.artist, grFont.gridArtist, 0, 0, showGridArtistFlags && this.flagImgs.length ? textWidth - flagSize : textWidth, this.wh); + const gridArtistNumLines = Math.min(2, gridArtistTxtRec.Lines); + const gridArtistNumHeight = gr.CalcTextHeight(grStr.artist, grFont.gridArtist) * gridArtistNumLines + 3; + const gridArtistHeight = gr.CalcTextHeight(grStr.artist, grFont.gridArtist); + + // * Apply better anti-aliasing on smaller font sizes in HD res + gr.SetTextRenderingHint(!RES._4K && gridArtistFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); + DrawString(gr, showGridArtistFlags && this.flagImgs.length ? flagSizeWhiteSpace + grStr.artist : grStr.artist, grFont.gridArtist, ['white', 'black', 'reborn', 'random'].includes(grSet.theme) ? grCol.detailsText : grSet.theme === 'cream' ? pl.col.header_artist_normal : pl.col.header_artist_playing, marginLeft, Math.round(top), textWidth, gridArtistNumHeight, Stringformat.trim_ellipsis_char); + + // * Artist flags + if (grStr.artist && this.flagImgs.length && showGridArtistFlags && this.displayDetails) { + const maxFlags = Math.min(this.flagImgs.length, 6); + let flagsLeft = marginLeft; + for (let i = 0; i < maxFlags; i++) { + gr.DrawImage(this.flagImgs[i], flagsLeft, Math.round(top - (this.flagImgs[i].Height / (gridArtistHeight + SCALE(2))) - (RES._4K ? 1 : 0)), this.flagImgs[i].Width + SCALE(gridArtistFontSize) - SCALE(26), gridArtistHeight + SCALE(2), 0, 0, this.flagImgs[i].Width, this.flagImgs[i].Height); + flagsLeft += this.flagImgs[i].Width + SCALE(gridArtistFontSize) - SCALE(18); + } } - } - return gridArtistNumHeight + (RES_4K ? 17 : 9); - } + return gridArtistNumHeight + (RES._4K ? 17 : 9); + }; - gridTop -= SCALE(2); + this.gridTop -= SCALE(2); - const drawTitle = (top) => { - if (!str.title) return 0; - gridTitleTxtRec = gr.MeasureString(isStreaming ? showGridTrackNum ? str.tracknum + str.title : str.title : str.tracknum === '' ? str.title : showGridTrackNum ? `${str.tracknum}\xa0${str.title}` : str.title, ft.grd_title, 0, 0, textWidth, wh); - const gridTitleNumLines = Math.min(2, gridTitleTxtRec.Lines); - const gridTitleNumHeight = gr.CalcTextHeight(str.title, ft.grd_title) * gridTitleNumLines + 3; - const gridTitleFontSize = pref[`gridTitleFontSize_${pref.layout}`]; + const drawTitle = (top) => { + if (!grStr.title) return 0; + gridTitleTxtRec = gr.MeasureString(this.isStreaming ? showGridTrackNum ? grStr.tracknum + grStr.title : grStr.title : grStr.tracknum === '' ? grStr.title : showGridTrackNum ? `${grStr.tracknum}\xa0${grStr.title}` : grStr.title, grFont.gridTitle, 0, 0, textWidth, this.wh); + const gridTitleNumLines = Math.min(2, gridTitleTxtRec.Lines); + const gridTitleNumHeight = gr.CalcTextHeight(grStr.title, grFont.gridTitle) * gridTitleNumLines + 3; + const gridTitleFontSize = grSet[`gridTitleFontSize_${grSet.layout}`]; - // * Apply better anti-aliasing on smaller font sizes in HD res - gr.SetTextRenderingHint(!RES_4K && gridTitleFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); - DrawString(gr, isStreaming ? showGridTrackNum ? str.tracknum + str.title : str.title : str.tracknum === '' ? str.title : showGridTrackNum ? `${str.tracknum}\xa0${str.title}` : str.title, ft.grd_title, col.detailsText, marginLeft, Math.round(top), textWidth, gridTitleNumHeight, g_string_format.trim_ellipsis_char); + // * Apply better anti-aliasing on smaller font sizes in HD res + gr.SetTextRenderingHint(!RES._4K && gridTitleFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); + DrawString(gr, this.isStreaming ? showGridTrackNum ? grStr.tracknum + grStr.title : grStr.title : grStr.tracknum === '' ? grStr.title : showGridTrackNum ? `${grStr.tracknum}\xa0${grStr.title}` : grStr.title, grFont.gridTitle, grCol.detailsText, marginLeft, Math.round(top), textWidth, gridTitleNumHeight, Stringformat.trim_ellipsis_char); - return gridTitleNumHeight + (RES_4K ? 17 : 9); - } + return gridTitleNumHeight + (RES._4K ? 17 : 9); + }; - gridTop -= SCALE(2); + this.gridTop -= SCALE(2); - const drawAlbumTitle = (top, maxLines) => { - if (!str.album) return 0; - gridAlbumTxtRec = gr.MeasureString(str.album, ft.grd_album, 0, 0, textWidth, wh); - const gridAlbumNumLines = Math.min(showGridArtist || showGridTitle ? 2 : 3, gridAlbumTxtRec.Lines); - const gridAlbumNumHeight = gr.CalcTextHeight(str.album, ft.grd_album) * gridAlbumNumLines + 3; - const gridAlbumFontSize = pref[`gridAlbumFontSize_${pref.layout}`]; + const drawAlbumTitle = (top, maxLines) => { + if (!grStr.album) return 0; + gridAlbumTxtRec = gr.MeasureString(grStr.album, grFont.gridAlbum, 0, 0, textWidth, this.wh); + const gridAlbumNumLines = Math.min(showGridArtist || showGridTitle ? 2 : 3, gridAlbumTxtRec.Lines); + const gridAlbumNumHeight = gr.CalcTextHeight(grStr.album, grFont.gridAlbum) * gridAlbumNumLines + 3; + const gridAlbumFontSize = grSet[`gridAlbumFontSize_${grSet.layout}`]; - // * Apply better anti-aliasing on smaller font sizes in HD res - gr.SetTextRenderingHint(!RES_4K && gridAlbumFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); - DrawString(gr, str.album, ft.grd_album, col.detailsText, marginLeft, Math.round(top), textWidth, gridAlbumNumHeight, g_string_format.trim_ellipsis_char); + // * Apply better anti-aliasing on smaller font sizes in HD res + gr.SetTextRenderingHint(!RES._4K && gridAlbumFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); + DrawString(gr, grStr.album, grFont.gridAlbum, grCol.detailsText, marginLeft, Math.round(top), textWidth, gridAlbumNumHeight, Stringformat.trim_ellipsis_char); - return gridAlbumNumHeight + SCALE(13); - } + return gridAlbumNumHeight + SCALE(13); + }; - if (showGridArtist) { - gridTop += drawArtist(gridTop); - } - if (showGridTitle) { - gridTop += drawTitle(gridTop); - } else if (!showGridArtist) { - gridTop += drawAlbumTitle(gridTop, 3); - } - // * Timeline - if (showGridTimeline && str.timeline && fb.IsPlaying) { - str.timeline.setSize(marginLeft, gridTop + SCALE(4), albumArtSize.x - marginLeft * 2); - str.timeline.draw(gr); - } - // * Tooltip - if (str.metadata_grid_tt && fb.IsPlaying) { - str.metadata_grid_tt.setSize(marginLeft, geo.topMenuHeight, albumArtSize.x - marginLeft * 2); - str.metadata_grid_tt.draw(gr); - } - if (showGridTimeline) { - gridTop += geo.timelineHeight + SCALE(20); - } - if (showGridArtist || showGridTitle) { - gridTop += drawAlbumTitle(gridTop, 2); - } + if (showGridArtist) { + this.gridTop += drawArtist(this.gridTop); + } + if (showGridTitle) { + this.gridTop += drawTitle(this.gridTop); + } else if (!showGridArtist) { + this.gridTop += drawAlbumTitle(this.gridTop, 3); + } + // * Timeline + if (showGridTimeline && grm.timeline && fb.IsPlaying) { + grm.timeline.setSize(marginLeft, this.gridTop + SCALE(4), this.albumArtSize.x - marginLeft * 2); + grm.timeline.draw(gr); + } + // * Tooltip + if (grm.gridTip && fb.IsPlaying) { + grm.gridTip.setSize(marginLeft, this.topMenuHeight, this.albumArtSize.x - marginLeft * 2); + grm.gridTip.draw(gr); + } + if (showGridTimeline) { + this.gridTop += this.timelineHeight + SCALE(20); + } + if (showGridArtist || showGridTitle) { + this.gridTop += drawAlbumTitle(this.gridTop, 2); + } - // * Tags - const font_array = [ft.grd_key]; - const key_font_array = [ft.grd_val]; - let grid_key_ft = ft.grd_key; - for (const el of str.grid) { - if (font_array.length > 1) { // Only check if there's more than one entry in font_array - grid_key_ft = ChooseFontForWidth(gr, textWidth / 3, el, font_array); - while (grid_key_ft !== font_array[0]) { // If font returned was first item in the array, then everything fits, otherwise pare down array - font_array.shift(); - key_font_array.shift(); + // * Tags + const font_array = [grFont.gridKey]; + const key_font_array = [grFont.gridVal]; + let grid_key_ft = grFont.gridKey; + for (const el of grStr.grid) { + if (font_array.length > 1) { // Only check if there's more than one entry in font_array + grid_key_ft = ChooseFontForWidth(gr, textWidth / 3, el, font_array); + while (grid_key_ft !== font_array[0]) { // If font returned was first item in the array, then everything fits, otherwise pare down array + font_array.shift(); + key_font_array.shift(); + } } } - } - const grid_val_ft = key_font_array.shift(); - const col1Width = CalcGridMaxTextWidth(gr, str.grid, grid_key_ft); - const columnMargin = SCALE(10); - const col2Width = textWidth - columnMargin - col1Width + SCALE(5); - const col2Left = marginLeft + col1Width + columnMargin; - - for (let k = 0; k < str.grid.length; k++) { - let key = str.grid[k].label; - let value = str.grid[k].val; - let showLastFmImage = false; - let showReleaseFlagImage = false; - let showGridCodecLogoImage = false; - let showGridChannelLogoImage = false; - let dropShadow = false; - let grid_val_col = col.detailsText; - - if (value.length) { - switch (key) { - case 'Catalog': - case 'Rel. Country': - showReleaseFlagImage = showGridReleaseFlags; - break; + const grid_val_ft = key_font_array.shift(); + const col1Width = CalcGridMaxTextWidth(gr, grStr.grid, grid_key_ft); + const columnMargin = SCALE(10); + const col2Width = textWidth - columnMargin - col1Width + SCALE(5); + const col2Left = marginLeft + col1Width + columnMargin; + + for (let k = 0; k < grStr.grid.length; k++) { + let key = grStr.grid[k].label; + let value = grStr.grid[k].val; + let showLastFmImage = false; + let showReleaseFlagImage = false; + let showGridCodecLogoImage = false; + let showGridChannelLogoImage = false; + let dropShadow = false; + let grid_val_col = grCol.detailsText; + + if (value.length) { + switch (key) { + case 'Catalog': + case 'Rel. Country': + showReleaseFlagImage = showGridReleaseFlags; + break; - case 'Codec': { - const codec = $('$lower($if2(%codec%,$ext(%path%)))'); - if (['dts', 'dca (dts coherent acoustics)'].includes(codec)) { - value = 'DCA'; // * Show only DCA abbreviation if codec is DTS + case 'Codec': { + const codec = $('$lower($if2(%codec%,$ext(%path%)))'); + if (['dts', 'dca (dts coherent acoustics)'].includes(codec)) { + value = 'DCA'; // * Show only DCA abbreviation if codec is DTS + } + showGridCodecLogoImage = showGridCodecLogo; + break; } - showGridCodecLogoImage = showGridCodecLogo; - break; - } - case 'Channels': { - const channels = $('%channels%'); - const logoType = pref[`showGridChannelLogo_${pref.layout}`]; - const textLogo = logoType === 'textlogo'; - const noLogo = logoType === false; - const ChannelString = (number, string) => { - if (textLogo) return string; - if (noLogo) return `${number} \u00B7 ${string}`; - }; - const channelLogoMapping = { - 'mono': ChannelString(1, 'Mono'), - 'stereo': ChannelString(2, 'Stereo'), - '3ch': ChannelString(3, 'Center'), - '4ch': ChannelString(4, 'Quad'), - '5ch': ChannelString(5, 'Surround'), - '6ch': ChannelString(6, 'Surround'), - '7ch': ChannelString(7, 'Surround'), - '8ch': ChannelString(8, 'Surround'), - '10ch': ChannelString(10, 'Surround'), - '12ch': ChannelString(12, 'Surround') - }; - // * Remap foobar's org. channel strings - if (Object.prototype.hasOwnProperty.call(channelLogoMapping, channels)) { - value = channelLogoMapping[channels]; + case 'Channels': { + const channels = $('%channels%'); + const logoType = grSet[`showGridChannelLogo_${grSet.layout}`]; + const textLogo = logoType === 'textlogo'; + const noLogo = logoType === false; + const ChannelString = (number, string) => { + if (textLogo) return string; + if (noLogo) return `${number} \u00B7 ${string}`; + }; + const channelLogoMapping = { + 'mono': ChannelString(1, 'Mono'), + 'stereo': ChannelString(2, 'Stereo'), + '3ch': ChannelString(3, 'Center'), + '4ch': ChannelString(4, 'Quad'), + '5ch': ChannelString(5, 'Surround'), + '6ch': ChannelString(6, 'Surround'), + '7ch': ChannelString(7, 'Surround'), + '8ch': ChannelString(8, 'Surround'), + '10ch': ChannelString(10, 'Surround'), + '12ch': ChannelString(12, 'Surround') + }; + // * Remap foobar's org. channel strings + if (Object.prototype.hasOwnProperty.call(channelLogoMapping, channels)) { + value = channelLogoMapping[channels]; + } + showGridChannelLogoImage = showGridChannelLogo; + break; } - showGridChannelLogoImage = showGridChannelLogo; - break; - } - case 'Hotness': - grid_val_col = col.detailsHotness; - dropShadow = true; - break; + case 'Hotness': + grid_val_col = grCol.detailsHotness; + dropShadow = true; + break; - case 'Play Count': - showLastFmImage = true; - break; + case 'Play Count': + showLastFmImage = true; + break; - case 'Rating': - grid_val_col = col.detailsRating; - dropShadow = true; - break; + case 'Rating': + grid_val_col = grCol.detailsRating; + dropShadow = true; + break; - default: { - let matchCount = 0; - const smallHDRes = pref.displayRes === 'HD' && (ww < 1250 || wh < 800); - const smallQHDRes = pref.displayRes === 'QHD' && (ww < 1350 || wh < 900); - const small4KRes = pref.displayRes === '4K' && (ww < 2350 || wh < 1550); - const basicMeta = ['Year', 'Label', 'Genre', 'Codec', 'Channels', 'Source', 'Data', 'Play Count', 'Rating']; - - // * On small player sizes, there is no space for all metadata entries. Hide them and only display entries from basicMeta. - if (pref.autoHideGridMetadata && pref.layout === 'default' && (smallHDRes || smallQHDRes || small4KRes) && !basicMeta.includes(key)) { - value = ''; - key = ''; - matchCount++; + default: { + let matchCount = 0; + const smallHDRes = grSet.displayRes === 'HD' && (this.ww < 1250 || this.wh < 800); + const smallQHDRes = grSet.displayRes === 'QHD' && (this.ww < 1350 || this.wh < 900); + const small4KRes = grSet.displayRes === '4K' && (this.ww < 2350 || this.wh < 1550); + const basicMeta = ['Year', 'Label', 'Genre', 'Codec', 'Channels', 'Source', 'Data', 'Play Count', 'Rating']; + + // * On small player sizes, there is no space for all metadata entries. Hide them and only display entries from basicMeta. + if (grSet.autoHideGridMetadata && grSet.layout === 'default' && (smallHDRes || smallQHDRes || small4KRes) && !basicMeta.includes(key)) { + value = ''; + key = ''; + matchCount++; + } + txtRec = gr.MeasureString(value, grid_val_ft, 0, 0, col2Width, this.wh); + const cellHeight = txtRec.Height + 5; + this.gridTop -= cellHeight * matchCount; } - txtRec = gr.MeasureString(value, grid_val_ft, 0, 0, col2Width, wh); - const cellHeight = txtRec.Height + 5; - gridTop -= cellHeight * matchCount; } - } - txtRec = gr.MeasureString(value, grid_val_ft, 0, 0, col2Width, wh); - - if (gridTop + txtRec.Height < albumArtSize.y + albumArtSize.h) { - const borderWidth = SCALE(0.5); - const cellHeight = txtRec.Height + 5; - const keyFontSize = pref[`gridKeyFontSize_${pref.layout}`]; - const valFontSize = pref[`gridValueFontSize_${pref.layout}`] + SCALE(1); - const showReleaseFlagOnly = pref[`showGridReleaseFlags_${pref.layout}`] === 'logo'; - const showCodecLogoOnly = pref[`showGridCodecLogo_${pref.layout}`] === 'logo'; - const showChannelLogoOnly = pref[`showGridChannelLogo_${pref.layout}`] === 'logo'; - const flag = showReleaseFlagOnly && key === 'Rel. Country'; - const codec = showCodecLogoOnly && key === 'Codec'; - const channels = showChannelLogoOnly && key === 'Channels'; - const ratingLinux = detectWine && key === 'Rating'; - - // * Apply better anti-aliasing on smaller font sizes in HD res - gr.SetTextRenderingHint(!RES_4K && (keyFontSize < 17 || valFontSize < 18) ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); - - if (dropShadow) { - gr.DrawString(value, grid_val_ft, col.darkAccent_50, Math.round(col2Left + borderWidth), Math.round(gridTop + borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); - gr.DrawString(value, grid_val_ft, col.darkAccent_50, Math.round(col2Left - borderWidth), Math.round(gridTop + borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); - gr.DrawString(value, grid_val_ft, col.darkAccent_50, Math.round(col2Left + borderWidth), Math.round(gridTop - borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); - gr.DrawString(value, grid_val_ft, col.darkAccent_50, Math.round(col2Left - borderWidth), Math.round(gridTop - borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); - } - gr.DrawString(key, grid_key_ft, col.detailsText, marginLeft, Math.round(gridTop), col1Width, cellHeight, g_string_format.trim_ellipsis_char); - gr.DrawString(flag || codec || channels ? '' : value, grid_val_ft, grid_val_col, col2Left, Math.round(gridTop), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); - - // * Last.fm logo - if (playCountVerifiedByLastFm && showLastFmImage) { - const lastFmImg = gdi.Image(paths.lastFmImageRed); - const lastFmWhiteImg = gdi.Image(paths.lastFmImageWhite); - const lastFmLogo = ColorDistance(col.primary, RGB(185, 0, 0), false) < 133 ? lastFmWhiteImg : lastFmImg; - const heightRatio = (cellHeight - 12) / lastFmLogo.Height; - if (txtRec.Width + SCALE(12) + Math.round(lastFmLogo.Width * heightRatio) < col2Width) { - gr.DrawImage(lastFmLogo, col2Left + txtRec.Width + SCALE(12), gridTop + 3, - Math.round(lastFmLogo.Width * heightRatio), cellHeight - 12, 0, 0, lastFmLogo.Width, lastFmLogo.Height); + txtRec = gr.MeasureString(value, grid_val_ft, 0, 0, col2Width, this.wh); + + if (this.gridTop + txtRec.Height < this.albumArtSize.y + this.albumArtSize.h) { + const borderWidth = SCALE(0.5); + const cellHeight = txtRec.Height + 5; + const keyFontSize = grSet[`gridKeyFontSize_${grSet.layout}`]; + const valFontSize = grSet[`gridValueFontSize_${grSet.layout}`] + SCALE(1); + const showReleaseFlagOnly = grSet[`showGridReleaseFlags_${grSet.layout}`] === 'logo'; + const showCodecLogoOnly = grSet[`showGridCodecLogo_${grSet.layout}`] === 'logo'; + const showChannelLogoOnly = grSet[`showGridChannelLogo_${grSet.layout}`] === 'logo'; + const flag = showReleaseFlagOnly && key === 'Rel. Country'; + const codec = showCodecLogoOnly && key === 'Codec'; + const channels = showChannelLogoOnly && key === 'Channels'; + const ratingLinux = Detect.Wine && key === 'Rating'; + + // * Apply better anti-aliasing on smaller font sizes in HD res + gr.SetTextRenderingHint(!RES._4K && (keyFontSize < 17 || valFontSize < 18) ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); + + if (dropShadow) { + gr.DrawString(value, grid_val_ft, grCol.darkAccent_50, Math.round(col2Left + borderWidth), Math.round(this.gridTop + borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); + gr.DrawString(value, grid_val_ft, grCol.darkAccent_50, Math.round(col2Left - borderWidth), Math.round(this.gridTop + borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); + gr.DrawString(value, grid_val_ft, grCol.darkAccent_50, Math.round(col2Left + borderWidth), Math.round(this.gridTop - borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); + gr.DrawString(value, grid_val_ft, grCol.darkAccent_50, Math.round(col2Left - borderWidth), Math.round(this.gridTop - borderWidth), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); } - } - // * Release flags - if (showReleaseFlagImage && releaseFlagImg) { - const sizeCorr = txtRec.Lines === 4 ? 4 : txtRec.Lines === 3 ? 3 : txtRec.Lines === 2 ? 2 : 1; - const yCorr = txtRec.Lines === 4 ? cellHeight / 4 : txtRec.Lines === 3 ? cellHeight / 3 : 0; - const heightRatio = (cellHeight) / releaseFlagImg.Height; - if ((!showReleaseFlagOnly ? txtRec.Width + SCALE(8) : 0) + Math.round(releaseFlagImg.Width * heightRatio) < col2Width) { - gr.DrawImage(releaseFlagImg, showReleaseFlagOnly && key === 'Rel. Country' ? col2Left : col2Left + txtRec.Width + SCALE(8), gridTop - 3 + yCorr, - Math.round(releaseFlagImg.Width * heightRatio / sizeCorr), cellHeight / sizeCorr, 0, 0, releaseFlagImg.Width, releaseFlagImg.Height); + gr.DrawString(key, grid_key_ft, grCol.detailsText, marginLeft, Math.round(this.gridTop), col1Width, cellHeight, Stringformat.trim_ellipsis_char); + gr.DrawString(flag || codec || channels ? '' : value, grid_val_ft, grid_val_col, col2Left, Math.round(this.gridTop), col2Width + (ratingLinux ? SCALE(20) : 0), cellHeight, StringFormat(0, 0, 4)); + + // * Last.fm logo + if (this.playCountVerifiedByLastFm && showLastFmImage) { + const lastFmImg = gdi.Image(grPath.lastFmImageRed); + const lastFmWhiteImg = gdi.Image(grPath.lastFmImageWhite); + const lastFmLogo = ColorDistance(grCol.primary, RGB(185, 0, 0), false) < 133 ? lastFmWhiteImg : lastFmImg; + const heightRatio = (cellHeight - 12) / lastFmLogo.Height; + if (txtRec.Width + SCALE(12) + Math.round(lastFmLogo.Width * heightRatio) < col2Width) { + gr.DrawImage(lastFmLogo, col2Left + txtRec.Width + SCALE(12), this.gridTop + 3, + Math.round(lastFmLogo.Width * heightRatio), cellHeight - 12, 0, 0, lastFmLogo.Width, lastFmLogo.Height); + } } - } - // * Codec logo - if (showGridCodecLogoImage) { - loadCodecLogo(); - const heightRatio = codecLogo != null ? (cellHeight - 4) / codecLogo.Height : ''; - if (codecLogo != null && (!showCodecLogoOnly ? txtRec.Width + SCALE(8) : 0) + Math.round(codecLogo.Width * heightRatio) < col2Width) { - gr.DrawImage(codecLogo, showCodecLogoOnly && key === 'Codec' ? col2Left : col2Left + txtRec.Width + SCALE(8), gridTop - 1, - Math.round(codecLogo.Width * heightRatio), cellHeight - 4, 0, 0, codecLogo.Width, codecLogo.Height); + // * Release flags + if (showReleaseFlagImage && this.releaseFlagImg) { + const sizeCorr = txtRec.Lines === 4 ? 4 : txtRec.Lines === 3 ? 3 : txtRec.Lines === 2 ? 2 : 1; + const yCorr = txtRec.Lines === 4 ? cellHeight / 4 : txtRec.Lines === 3 ? cellHeight / 3 : 0; + const heightRatio = (cellHeight) / this.releaseFlagImg.Height; + if ((!showReleaseFlagOnly ? txtRec.Width + SCALE(8) : 0) + Math.round(this.releaseFlagImg.Width * heightRatio) < col2Width) { + gr.DrawImage(this.releaseFlagImg, showReleaseFlagOnly && key === 'Rel. Country' ? col2Left : col2Left + txtRec.Width + SCALE(8), this.gridTop - 3 + yCorr, + Math.round(this.releaseFlagImg.Width * heightRatio / sizeCorr), cellHeight / sizeCorr, 0, 0, this.releaseFlagImg.Width, this.releaseFlagImg.Height); + } } - } - // * Channel logo - if (showGridChannelLogoImage) { - loadChannelLogo(); - const heightRatio = channelLogo != null ? (cellHeight - 4) / channelLogo.Height : ''; - if (channelLogo != null && (!showChannelLogoOnly ? txtRec.Width + SCALE(8) : 0) + Math.round(channelLogo.Width * heightRatio) < col2Width) { - gr.DrawImage(channelLogo, showChannelLogoOnly && key === 'Channels' ? col2Left : col2Left + txtRec.Width + SCALE(8), gridTop - 1, - Math.round(channelLogo.Width * heightRatio), cellHeight - 4, 0, 0, channelLogo.Width, channelLogo.Height); + // * Codec logo + if (showGridCodecLogoImage) { + this.loadCodecLogo(); + const heightRatio = this.codecLogo != null ? (cellHeight - 4) / this.codecLogo.Height : ''; + if (this.codecLogo != null && (!showCodecLogoOnly ? txtRec.Width + SCALE(8) : 0) + Math.round(this.codecLogo.Width * heightRatio) < col2Width) { + gr.DrawImage(this.codecLogo, showCodecLogoOnly && key === 'Codec' ? col2Left : col2Left + txtRec.Width + SCALE(8), this.gridTop - 1, + Math.round(this.codecLogo.Width * heightRatio), cellHeight - 4, 0, 0, this.codecLogo.Width, this.codecLogo.Height); + } } + // * Channel logo + if (showGridChannelLogoImage) { + this.loadChannelLogo(); + const heightRatio = this.channelLogo != null ? (cellHeight - 4) / this.channelLogo.Height : ''; + if (this.channelLogo != null && (!showChannelLogoOnly ? txtRec.Width + SCALE(8) : 0) + Math.round(this.channelLogo.Width * heightRatio) < col2Width) { + gr.DrawImage(this.channelLogo, showChannelLogoOnly && key === 'Channels' ? col2Left : col2Left + txtRec.Width + SCALE(8), this.gridTop - 1, + Math.round(this.channelLogo.Width * heightRatio), cellHeight - 4, 0, 0, this.channelLogo.Width, this.channelLogo.Height); + } + } + this.gridTop += cellHeight + 5; } - gridTop += cellHeight + 5; } } } - } - if (drawMetadataGridProfiler) drawMetadataGridProfiler.Print(); -} + if (drawMetadataGridProfiler) drawMetadataGridProfiler.Print(); + } + /** + * Draws the band logo on the bottom left side in the Details panel. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDetailsBandLogo(gr) { + if (!fb.IsPlaying || !this.albumArt || grSet.layout !== 'default' || !this.displayDetails || + grSet.lyricsLayout === 'full' && this.displayLyrics) { + return; + } -/** - * Draws the band logo on the bottom left side in the Details panel. - * @param {GdiGraphics} gr - */ -function drawDetailsBandLogo(gr) { - const displayDetails = pref.layout === 'default' && !displayPlaylist && !displayLibrary && !displayBiography; - if (!fb.IsPlaying || !albumArt || !displayDetails || pref.lyricsLayout === 'full' && pref.displayLyrics) return; + const drawBandLogoProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> band logo'); + const availableSpace = this.albumArtSize.y + this.albumArtSize.h - this.gridTop; + const lightBg = new Color(grCol.detailsText).brightness < 140; + const logo = lightBg || this.noAlbumArtStub ? (this.bandLogoInverted || this.bandLogo) : this.bandLogo; - const drawBandLogoProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> band logo'); - const availableSpace = albumArtSize.y + albumArtSize.h - gridTop; - const lightBg = new Color(col.detailsText).brightness < 140; - const logo = lightBg || noAlbumArtStub ? (invertedBandLogo || bandLogo) : bandLogo; + if (logo && availableSpace > 75) { + let logoWidth = Math.min(RES._4K ? logo.Width : logo.Width / 2, this.albumArtSize.x - this.ww * 0.05); + const heightScale = Math.min(logoWidth / logo.Width, availableSpace / logo.Height); + logoWidth = logo.Width * heightScale; // Adjust logoWidth after heightScale is potentially updated - if (logo && availableSpace > 75) { - let logoWidth = Math.min(RES_4K ? logo.Width : logo.Width / 2, albumArtSize.x - ww * 0.05); - const heightScale = Math.min(logoWidth / logo.Width, availableSpace / logo.Height); - logoWidth = logo.Width * heightScale; // Adjust logoWidth after heightScale is potentially updated + const logoX = Math.round(this.isStreaming ? SCALE(40) : this.albumArtSize.x / 2 - logoWidth / 2); + const logoY = Math.round(this.albumArtSize.y + this.albumArtSize.h - (logo.Height * heightScale)) - (RES._4K ? 24 : 4); + const logoW = Math.round(logoWidth); + const logoH = Math.round(logo.Height * heightScale); - const logoX = Math.round(isStreaming ? SCALE(40) : albumArtSize.x / 2 - logoWidth / 2); - const logoY = Math.round(albumArtSize.y + albumArtSize.h - (logo.Height * heightScale)) - (RES_4K ? 24 : 4); - const logoW = Math.round(logoWidth); - const logoH = Math.round(logo.Height * heightScale); + gr.DrawImage(logo, logoX, logoY, logoW, logoH, 0, 0, logo.Width, logo.Height, 0); + } - gr.DrawImage(logo, logoX, logoY, logoW, logoH, 0, 0, logo.Width, logo.Height, 0); + if (drawBandLogoProfiler) drawBandLogoProfiler.Print(); } - if (drawBandLogoProfiler) drawBandLogoProfiler.Print(); -} + /** + * Draws the label logo on the bottom right side in the Details panel. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDetailsLabelLogo(gr) { + if (!fb.IsPlaying || !this.albumArt || grSet.layout !== 'default' || !this.displayDetails || + grSet.lyricsLayout === 'full' && this.displayLyrics) { + return; + } + const drawLabelLogoProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> label logo'); + + if (this.recordLabels.length > 0) { + const lightBg = grSet.labelArtOnBg ? new Color(grCol.bg).brightness > 140 : new Color(grCol.detailsText).brightness < 140; + const labels = lightBg || this.noAlbumArtStub ? (this.recordLabelsInverted.length ? this.recordLabelsInverted : this.recordLabels) : this.recordLabels; + const rightSideGap = 20; // How close last label is to right edge + const leftEdgeGap = (this.albumArtOffCenter ? 20 : 40) * (RES._4K ? 1.8 : 1); // Space between art and label + const leftEdgeWidth = RES._4K ? 45 : 30; // How far label background extends on left + const maxLabelWidth = SCALE(200); + let leftEdge = 0; + let topEdge = 0; + let totalLabelWidth = 0; + let labelAreaWidth = 0; + let labelSpacing = 0; + let labelWidth; + let labelHeight; -/** - * Draws the label logo on the bottom right side in the Details panel. - * @param {GdiGraphics} gr - */ -function drawDetailsLabelLogo(gr) { - const displayDetails = pref.layout === 'default' && !displayPlaylist && !displayLibrary && !displayBiography; - const drawLabelLogoProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> label logo'); - if (!fb.IsPlaying || !albumArt || !displayDetails || pref.lyricsLayout === 'full' && pref.displayLyrics) return; - - if (recordLabels.length > 0) { - const lightBg = pref.labelArtOnBg ? new Color(col.bg).brightness > 140 : new Color(col.detailsText).brightness < 140; - const labels = lightBg || noAlbumArtStub ? (recordLabelsInverted.length ? recordLabelsInverted : recordLabels) : recordLabels; - const rightSideGap = 20; // How close last label is to right edge - const leftEdgeGap = (artOffCenter ? 20 : 40) * (RES_4K ? 1.8 : 1); // Space between art and label - const leftEdgeWidth = RES_4K ? 45 : 30; // How far label background extends on left - const maxLabelWidth = SCALE(200); - let leftEdge = 0; - let topEdge = 0; - let totalLabelWidth = 0; - let labelAreaWidth = 0; - let labelSpacing = 0; - let labelWidth; - let labelHeight; - - for (let i = 0; i < labels.length; i++) { - if (labels[i].Width > maxLabelWidth) { - totalLabelWidth += maxLabelWidth; - } else { - totalLabelWidth += RES_4K && labels[i].Width < 200 ? labels[i].Width * 2 : labels[i].Width; - } - } - if (!lastLeftEdge) { // We don't want to recalculate this every screen refresh - DebugLog('recalculating lastLeftEdge'); - labelShadowImg = null; - labelWidth = Math.round(totalLabelWidth / labels.length); - labelHeight = Math.round(labels[0].Height * labelWidth / labels[0].Width); // Might be recalc'd below - if (albumArt) { - if (discArt && pref.displayDiscArt) { - leftEdge = Math.round(Math.max(albumArtSize.x + albumArtScaled.Width + 5, ww * 0.975 - totalLabelWidth + 1)); - const discCenter = {}; - discCenter.x = Math.round(discArtSize.x + discArtSize.w / 2); - discCenter.y = Math.round(discArtSize.y + discArtSize.h / 2); - const radius = discCenter.y - discArtSize.y; - - while (true) { - const allLabelsWidth = Math.max(Math.min(Math.round((ww - leftEdge - rightSideGap) / labels.length), maxLabelWidth), 50); - //console.log("leftEdge = " + leftEdge + ", ww-leftEdge-10 = " + (ww-leftEdge-10) + ", allLabelsWidth=" + allLabelsWidth); - const maxWidth = RES_4K && labels[0].Width < 200 ? labels[0].Width * 2 : labels[0].Width; - labelWidth = (allLabelsWidth > maxWidth) ? maxWidth : allLabelsWidth; - labelHeight = Math.round(labels[0].Height * labelWidth / labels[0].Width); // Width is based on height scale - topEdge = Math.round(albumArtSize.y + albumArtSize.h - labelHeight); - - const a = topEdge - discCenter.y + 1; // Adding 1 to a and b so that the border just touches the edge of the discArt - const b = leftEdge - discCenter.x + 1; - - if ((a * a + b * b) > radius * radius) { - break; + for (let i = 0; i < labels.length; i++) { + if (labels[i].Width > maxLabelWidth) { + totalLabelWidth += maxLabelWidth; + } else { + totalLabelWidth += RES._4K && labels[i].Width < 200 ? labels[i].Width * 2 : labels[i].Width; + } + } + if (!this.lastLeftEdge) { // We don't want to recalculate this every screen refresh + DebugLog('recalculating lastLeftEdge'); + this.shadowImgLabel = null; + labelWidth = Math.round(totalLabelWidth / labels.length); + labelHeight = Math.round(labels[0].Height * labelWidth / labels[0].Width); // Might be recalc'd below + if (this.albumArt) { + if (this.discArt && grSet.displayDiscArt) { + leftEdge = Math.round(Math.max(this.albumArtSize.x + this.albumArtScaled.Width + 5, this.ww * 0.975 - totalLabelWidth + 1)); + const discCenter = {}; + discCenter.x = Math.round(this.discArtSize.x + this.discArtSize.w / 2); + discCenter.y = Math.round(this.discArtSize.y + this.discArtSize.h / 2); + const radius = discCenter.y - this.discArtSize.y; + + while (true) { + const allLabelsWidth = Math.max(Math.min(Math.round((this.ww - leftEdge - rightSideGap) / labels.length), maxLabelWidth), 50); + //console.log("leftEdge = " + leftEdge + ", this.ww-leftEdge-10 = " + (this.ww-leftEdge-10) + ", allLabelsWidth=" + allLabelsWidth); + const maxWidth = RES._4K && labels[0].Width < 200 ? labels[0].Width * 2 : labels[0].Width; + labelWidth = (allLabelsWidth > maxWidth) ? maxWidth : allLabelsWidth; + labelHeight = Math.round(labels[0].Height * labelWidth / labels[0].Width); // Width is based on height scale + topEdge = Math.round(this.albumArtSize.y + this.albumArtSize.h - labelHeight); + + const a = topEdge - discCenter.y + 1; // Adding 1 to a and b so that the border just touches the edge of the discArt + const b = leftEdge - discCenter.x + 1; + + if ((a * a + b * b) > radius * radius) { + break; + } + leftEdge += 4; } - leftEdge += 4; + } else { + leftEdge = Math.round(Math.max(this.albumArtSize.x + this.albumArtSize.w + leftEdgeWidth + leftEdgeGap, this.ww * 0.975 - totalLabelWidth + 1)); } } else { - leftEdge = Math.round(Math.max(albumArtSize.x + albumArtSize.w + leftEdgeWidth + leftEdgeGap, ww * 0.975 - totalLabelWidth + 1)); + leftEdge = Math.round(this.ww * 0.975 - totalLabelWidth); } - } else { - leftEdge = Math.round(ww * 0.975 - totalLabelWidth); - } - labelAreaWidth = ww - leftEdge - rightSideGap; - lastLeftEdge = leftEdge; - lastLabelHeight = labelHeight; - } - else { // Already calculated - leftEdge = lastLeftEdge; - labelHeight = lastLabelHeight; - labelAreaWidth = ww - leftEdge - rightSideGap; - } - if (labelAreaWidth >= SCALE(50)) { - if (labels.length > 1) { - labelSpacing = Math.min(12, Math.max(3, Math.round((labelAreaWidth / (labels.length - 1)) * 0.048))); // Spacing should be proportional, and between 3 and 12 pixels - } - // console.log('labelAreaWidth = ' + labelAreaWidth + ", labelSpacing = " + labelSpacing); - const allLabelsWidth = Math.max(Math.min(Math.round((labelAreaWidth - (labelSpacing * (labels.length - 1))) / labels.length), maxLabelWidth), 50); // allLabelsWidth must be between 50 and 200 pixels wide - const origLabelHeight = labelHeight; - let labelX = leftEdge; - topEdge = albumArtSize.y + albumArtSize.h - labelHeight - 20; - - if (!pref.labelArtOnBg && !pref.noDiscArtBg || pref.noDiscArtBg && pref.displayDiscArt && discArt) { - if (!['black', 'nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme)) { - if (!labelShadowImg) { - labelShadowImg = ShadowRect(geo.discArtShadow, geo.discArtShadow, ww - labelX + leftEdgeWidth, labelHeight + 40, geo.discArtShadow, col.shadow); + labelAreaWidth = this.ww - leftEdge - rightSideGap; + this.lastLeftEdge = leftEdge; + this.lastLabelHeight = labelHeight; + } + else { // Already calculated + leftEdge = this.lastLeftEdge; + labelHeight = this.lastLabelHeight; + labelAreaWidth = this.ww - leftEdge - rightSideGap; + } + if (labelAreaWidth >= SCALE(50)) { + if (labels.length > 1) { + labelSpacing = Math.min(12, Math.max(3, Math.round((labelAreaWidth / (labels.length - 1)) * 0.048))); // Spacing should be proportional, and between 3 and 12 pixels + } + // console.log('labelAreaWidth = ' + labelAreaWidth + ", labelSpacing = " + labelSpacing); + const allLabelsWidth = Math.max(Math.min(Math.round((labelAreaWidth - (labelSpacing * (labels.length - 1))) / labels.length), maxLabelWidth), 50); // allLabelsWidth must be between 50 and 200 pixels wide + const origLabelHeight = labelHeight; + let labelX = leftEdge; + topEdge = this.albumArtSize.y + this.albumArtSize.h - labelHeight - 20; + + if (!grSet.labelArtOnBg && !grSet.noDiscArtBg || grSet.noDiscArtBg && grSet.displayDiscArt && this.discArt) { + if (!['black', 'nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme)) { + if (!this.shadowImgLabel) { + this.shadowImgLabel = ShadowRect(this.discArtShadow, this.discArtShadow, this.ww - labelX + leftEdgeWidth, labelHeight + 40, this.discArtShadow, grCol.shadow); + } + gr.DrawImage(this.shadowImgLabel, labelX - leftEdgeWidth - this.discArtShadow, topEdge - 20 - this.discArtShadow, this.ww - labelX + leftEdgeWidth + 2 * this.discArtShadow, labelHeight + 40 + 2 * this.discArtShadow, + 0, 0, this.shadowImgLabel.Width, this.shadowImgLabel.Height); } - gr.DrawImage(labelShadowImg, labelX - leftEdgeWidth - geo.discArtShadow, topEdge - 20 - geo.discArtShadow, ww - labelX + leftEdgeWidth + 2 * geo.discArtShadow, labelHeight + 40 + 2 * geo.discArtShadow, - 0, 0, labelShadowImg.Width, labelShadowImg.Height); + gr.SetSmoothingMode(SmoothingMode.None); // Disable smoothing + gr.FillSolidRect(labelX - leftEdgeWidth, topEdge - 20, this.ww - labelX + leftEdgeWidth, labelHeight + 40, grCol.detailsBg); + gr.DrawRect(labelX - leftEdgeWidth, topEdge - 20, this.ww - labelX + leftEdgeWidth, labelHeight + 40 - 1, 1, grCol.shadow); + gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); } - gr.SetSmoothingMode(SmoothingMode.None); // Disable smoothing - gr.FillSolidRect(labelX - leftEdgeWidth, topEdge - 20, ww - labelX + leftEdgeWidth, labelHeight + 40, col.detailsBg); - gr.DrawRect(labelX - leftEdgeWidth, topEdge - 20, ww - labelX + leftEdgeWidth, labelHeight + 40 - 1, 1, col.shadow); - gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); - } - for (let i = 0; i < labels.length; i++) { - // allLabelsWidth can never be greater than 200, so if a label image is 161 pixels wide, never draw it wider than 161 - const maxWidth = RES_4K && labels[i].Width < 200 ? labels[i].Width * 2 : labels[i].Width; - labelWidth = (allLabelsWidth > maxWidth) ? maxWidth : allLabelsWidth; - labelHeight = Math.round(labels[i].Height * labelWidth / labels[i].Width); // Width is based on height scale - - gr.DrawImage(labels[i], labelX, Math.round(topEdge + origLabelHeight / 2 - labelHeight / 2), labelWidth, labelHeight, 0, 0, recordLabels[i].Width, recordLabels[i].Height); - // gr.DrawRect(labelX, topEdge, labelWidth, labelHeight, 1, RGB(255,0,0)); // Shows bounding rect of record labels - labelX += labelWidth + labelSpacing; + for (let i = 0; i < labels.length; i++) { + // allLabelsWidth can never be greater than 200, so if a label image is 161 pixels wide, never draw it wider than 161 + const maxWidth = RES._4K && labels[i].Width < 200 ? labels[i].Width * 2 : labels[i].Width; + labelWidth = (allLabelsWidth > maxWidth) ? maxWidth : allLabelsWidth; + labelHeight = Math.round(labels[i].Height * labelWidth / labels[i].Width); // Width is based on height scale + + gr.DrawImage(labels[i], labelX, Math.round(topEdge + origLabelHeight / 2 - labelHeight / 2), labelWidth, labelHeight, 0, 0, this.recordLabels[i].Width, this.recordLabels[i].Height); + // gr.DrawRect(labelX, topEdge, labelWidth, labelHeight, 1, RGB(255,0,0)); // Shows bounding rect of record labels + labelX += labelWidth + labelSpacing; + } + labelHeight = origLabelHeight; // Restore } - labelHeight = origLabelHeight; // Restore } - } - if (drawLabelLogoProfiler) drawLabelLogoProfiler.Print(); -} + if (drawLabelLogoProfiler) drawLabelLogoProfiler.Print(); + } + /** + * Draws the lyrics on the album art in the Lyrics panel. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawLyrics(gr) { + if (!this.displayLyrics || !fb.IsPlaying) return; -/** - * Draws the lyrics on the album art in the Lyrics panel. - * @param {GdiGraphics} gr - */ -function drawLyrics(gr) { - if (!pref.displayLyrics || !fb.IsPlaying) return; + const fullW = grSet.layout === 'default' && grSet.lyricsLayout === 'full' && this.displayLyrics && this.noAlbumArtStub || grSet.layout === 'artwork'; - const fullW = pref.layout === 'default' && pref.lyricsLayout === 'full' && pref.displayLyrics && noAlbumArtStub || pref.layout === 'artwork'; + gr.SetSmoothingMode(SmoothingMode.None); + gr.FillSolidRect(fullW ? 0 : this.albumArtSize.x, fullW ? this.topMenuHeight : this.albumArtSize.y, + fullW ? this.ww : this.albumArtSize.w, fullW ? this.wh - this.topMenuHeight - this.lowerBarHeight : this.albumArtSize.h, + grSet.lyricsAlbumArt ? RGBA(0, 0, 0, 170) : pl.col.bg); - gr.SetSmoothingMode(SmoothingMode.None); - gr.FillSolidRect(fullW ? 0 : albumArtSize.x, fullW ? geo.topMenuHeight : albumArtSize.y, - fullW ? ww : albumArtSize.w, fullW ? wh - geo.topMenuHeight - geo.lowerBarHeight : albumArtSize.h, - pref.lyricsAlbumArt ? RGBA(0, 0, 0, 170) : g_pl_colors.bg); + if (grm.lyrics) grm.lyrics.drawLyrics(gr); + } - if (lyrics) lyrics.drawLyrics(gr); -} + /** + * Draws the activated styles from Options > Styles. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawStyles(gr) { + const drawStylesProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> theme styles'); + + if (grSet.styleBevel) { + gr.SetSmoothingMode(SmoothingMode.None); + if ((grSet.layout === 'default' && (this.displayPlaylist || this.displayLibrary) && !this.displayBiography || + grSet.layout === 'artwork' && (!this.displayPlaylistArtwork && !this.displayLibrary && !this.displayBiography)) && fb.IsPlaying) { + // Fill gap when album art or player size is not proportional + gr.FillSolidRect(-1, this.topMenuHeight, grSet.layout === 'default' ? this.albumArtSize.w + this.albumArtSize.x + 1 : this.ww + 1, (this.displayLibrary && grSet.libraryLayout === 'full' ? 0 : this.albumArtSize.y) - this.topMenuHeight - 1, RGBtoRGBA(grCol.styleBevel, 40)); + } + if (!['black', 'nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) && !grSet.styleNighttime && !grSet.styleBlackAndWhite2 && !grSet.styleRebornBlack || (grSet.styleNighttime && fb.IsPlaying)) { + const customTheme = grSet.theme.startsWith('custom'); + gr.FillGradRect(-1, 0, this.ww + 1, this.topMenuHeight, 90, 0, RGBtoRGBA(grCol.styleBevel, customTheme ? 255 : 40)); // Top + gr.FillGradRect(-1, this.wh - this.lowerBarHeight - 1, this.ww + 1, this.lowerBarHeight + 1, -88, RGBtoRGBA(grCol.styleBevel, customTheme ? 255 : 80), 0); // Bottom + } else { + gr.FillGradRect(-1, 0, this.ww + 1, this.topMenuHeight, 90, grSet.styleBlackReborn ? 0 : RGBtoRGBA(grCol.styleBevel, 200), grSet.styleBlackReborn ? RGBtoRGBA(grCol.styleBevel, 200) : 0); + gr.FillGradRect(-1, this.wh - this.lowerBarHeight - 1, this.ww + 1, this.lowerBarHeight + 1, -90, RGBtoRGBA(grCol.styleBevel, 255), 0); + } + } + if (grSet.styleBlend2 && this.albumArt && grCol.imgBlended) { + gr.DrawImage(grCol.imgBlended, -1, 0, this.ww + 1, this.wh, 0, -this.wh + this.topMenuHeight - 1, grCol.imgBlended.Width, grCol.imgBlended.Height, 180); + gr.DrawImage(grCol.imgBlended, 0, this.wh - this.lowerBarHeight, this.ww, this.wh, 0, this.wh * 0.5, grCol.imgBlended.Width, grCol.imgBlended.Height); + } + if (grSet.styleGradient || grSet.styleGradient2) { + gr.FillGradRect(-0.5, 0, this.ww, this.topMenuHeight, grSet.styleGradient2 ? -200 : (grSet.styleNighttime || grSet.styleRebornBlack) ? -180 : 0, grSet.styleGradient2 || grSet.styleNighttime || grSet.styleRebornBlack ? 0 : grCol.styleGradient, grSet.styleGradient2 || grSet.styleNighttime || grSet.styleRebornBlack ? grCol.styleGradient2 : 0, 0.5); + gr.FillGradRect(-0.5, this.wh - this.lowerBarHeight, this.ww, this.lowerBarHeight, grSet.styleGradient2 ? -200 : grSet.styleRebornBlack || grSet.styleNighttime ? -180 : 0, grSet.styleGradient2 || grSet.styleNighttime || grSet.styleRebornBlack ? 0 : grCol.styleGradient, grSet.styleGradient2 || grSet.styleNighttime || grSet.styleRebornBlack ? grCol.styleGradient2 : 0, 0.5); + } + if ((grSet.styleAlternative || grSet.styleAlternative2) && (['black', 'nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme))) { + gr.FillGradRect(0, 0, this.ww, this.topMenuHeight, grSet.styleAlternative2 ? -87 : -87, grCol.styleAlternative, 0); + gr.FillGradRect(0, this.wh - this.lowerBarHeight, this.ww, this.lowerBarHeight, grSet.styleAlternative2 ? 87 : -87, 0, grCol.styleAlternative); + } + if (drawStylesProfiler) drawStylesProfiler.Print(); + } -/** - * Draws the activated styles from Options > Styles. - * @param {GdiGraphics} gr - */ -function drawStyles(gr) { - const drawStylesProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> theme styles'); + /** + * Draws a theme notification as a popup. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawThemeNotification(gr) { + if (grSet.themeSetupDay || grSet.themeSetupNight) { + const timeOfDay = grSet.themeSetupDay ? 'daytime' : 'nighttime'; + this.themeNotification = `Theme setup for ${timeOfDay} is active:\n\nPlease select your theme and styles\nfor ${timeOfDay} usage.\n\nAfter configuration,\nrevisit the theme day/night menu\nto save changes.`; + } - if (pref.styleBevel) { - gr.SetSmoothingMode(SmoothingMode.None); - if ((pref.layout === 'default' && (displayPlaylist || displayLibrary) && !displayBiography || - pref.layout === 'artwork' && (!displayPlaylistArtwork && !displayLibrary && !displayBiography)) && fb.IsPlaying) { - // Fill gap when album art or player size is not proportional - gr.FillSolidRect(-1, geo.topMenuHeight, pref.layout === 'default' ? albumArtSize.w + albumArtSize.x + 1 : ww + 1, (displayLibrary && pref.libraryLayout === 'full' ? 0 : albumArtSize.y) - geo.topMenuHeight - 1, RGBtoRGBA(col.styleBevel, 40)); - } - if (!['black', 'nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) && !pref.styleNighttime && !pref.styleBlackAndWhite2 && !pref.styleRebornBlack || (pref.styleNighttime && fb.IsPlaying)) { - const customTheme = pref.theme.startsWith('custom'); - gr.FillGradRect(-1, 0, ww + 1, geo.topMenuHeight, 90, 0, RGBtoRGBA(col.styleBevel, customTheme ? 255 : 40)); // Top - gr.FillGradRect(-1, wh - geo.lowerBarHeight - 1, ww + 1, geo.lowerBarHeight + 1, -88, RGBtoRGBA(col.styleBevel, customTheme ? 255 : 80), 0); // Bottom - } else { - gr.FillGradRect(-1, 0, ww + 1, geo.topMenuHeight, 90, pref.styleBlackReborn ? 0 : RGBtoRGBA(col.styleBevel, 200), pref.styleBlackReborn ? RGBtoRGBA(col.styleBevel, 200) : 0); - gr.FillGradRect(-1, wh - geo.lowerBarHeight - 1, ww + 1, geo.lowerBarHeight + 1, -90, RGBtoRGBA(col.styleBevel, 255), 0); + if (this.themeNotification === '' && this.themePresetName === '' || + !this.themePresetIndicator || !grSet.presetIndicator || !['off', 'dblclick'].includes(grSet.presetAutoRandomMode)) { + return; } - } - if (pref.styleBlend2 && albumArt && blendedImg) { - gr.DrawImage(blendedImg, -1, 0, ww + 1, wh, 0, -wh + geo.topMenuHeight - 1, blendedImg.Width, blendedImg.Height, 180); - gr.DrawImage(blendedImg, 0, wh - geo.lowerBarHeight, ww, wh, 0, wh * 0.5, blendedImg.Width, blendedImg.Height); - } - if (pref.styleGradient || pref.styleGradient2) { - gr.FillGradRect(-0.5, 0, ww, geo.topMenuHeight, pref.styleGradient2 ? -200 : (pref.styleNighttime || pref.styleRebornBlack) ? -180 : 0, pref.styleGradient2 || pref.styleNighttime || pref.styleRebornBlack ? 0 : col.styleGradient, pref.styleGradient2 || pref.styleNighttime || pref.styleRebornBlack ? col.styleGradient2 : 0, 0.5); - gr.FillGradRect(-0.5, wh - geo.lowerBarHeight, ww, geo.lowerBarHeight, pref.styleGradient2 ? -200 : pref.styleRebornBlack || pref.styleNighttime ? -180 : 0, pref.styleGradient2 || pref.styleNighttime || pref.styleRebornBlack ? 0 : col.styleGradient, pref.styleGradient2 || pref.styleNighttime || pref.styleRebornBlack ? col.styleGradient2 : 0, 0.5); - } - if ((pref.styleAlternative || pref.styleAlternative2) && (['black', 'nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme))) { - gr.FillGradRect(0, 0, ww, geo.topMenuHeight, pref.styleAlternative2 ? -87 : -87, col.styleAlternative, 0); - gr.FillGradRect(0, wh - geo.lowerBarHeight, ww, geo.lowerBarHeight, pref.styleAlternative2 ? 87 : -87, 0, col.styleAlternative); + + const themePresetText = this.themePresetMatchMode ? `Active styles matching:\n\n${this.themePresetName}` : this.themePresetName; + const text = this.themeNotification === '' ? themePresetText : this.themeNotification; + + const arc = SCALE(6); + const padding = SCALE(20); + const lines = text.split('\n'); + const lineH = gr.CalcTextHeight('Ag', grFont.notification); + const maxWidth = Math.max(...lines.map(line => gr.CalcTextWidth(line, grFont.notification))); + const boxW = maxWidth + padding * 3; + const boxH = lineH * lines.length + padding * 2; + + const fullW = + grSet.layout === 'default' + && + (this.displayPlaylist && grSet.playlistLayout === 'full' + || + this.displayLibrary && grSet.libraryLayout === 'full' + || + this.displayBiography && grSet.biographyLayout === 'full' + || + this.displayLyrics && grSet.lyricsLayout === 'full'); + + const cover = fb.IsPlaying && this.albumArt && grSet.layout !== 'compact' && !fullW; + const noCoverDefault = grSet.layout === 'default' && !fullW && !grSet.panelWidthAuto; + + const x = Math.round((cover ? this.albumArtSize.x + this.albumArtSize.w * 0.5 : noCoverDefault ? this.ww * 0.25 : this.ww * 0.5) - boxW * 0.5); + const y = Math.round((cover ? this.topMenuHeight + this.albumArtSize.h * 0.5 : ((this.wh - this.topMenuHeight - this.lowerBarHeight) * 0.5) + this.topMenuHeight) - boxH * 0.5); + + gr.SetSmoothingMode(SmoothingMode.AntiAlias); + gr.FillRoundRect(x, y, boxW, boxH, arc, arc, grCol.popupBg); + gr.DrawRoundRect(x, y, boxW, boxH, arc, arc, SCALE(2), 0x64000000); + gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); + gr.DrawString(text, grFont.notification, grCol.popupText, x, y, boxW, boxH, StringFormat(1, 1, 4)); } - if (drawStylesProfiler) drawStylesProfiler.Print(); -} + /** + * Draws all the shadows for album art and panels. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawPanelShadows(gr) { + const drawPanelShadowsProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> panel shadows'); + // * SHADOWS FOR ALBUM ART, noAlbumArtStub AND DETAILS * // + const layoutDefault = grSet.layout === 'default' && + (this.displayDetails || !this.displayBiography && (grSet.playlistLayout !== 'full' && this.displayPlaylist || grSet.libraryLayout === 'normal' && this.displayLibrary)); -/** - * Draws a theme notification as a popup. - * @param {GdiGraphics} gr - */ -function drawThemeNotification(gr) { - if (pref.themeSetupDay || pref.themeSetupNight) { - const timeOfDay = pref.themeSetupDay ? 'daytime' : 'nighttime'; - themeNotification = `Theme setup for ${timeOfDay} is active:\n\nPlease select your theme and styles\nfor ${timeOfDay} usage.\n\nAfter configuration,\nrevisit the theme day/night menu\nto save changes.`; - } - - if (themeNotification === '' && themePresetName === '' || !pref.presetIndicator || !themePresetIndicator || !['off', 'dblclick'].includes(pref.presetAutoRandomMode)) { - return; - } - - const themePresetText = themePresetMatchMode ? `Active styles matching:\n\n${themePresetName}` : themePresetName; - const text = themeNotification === '' ? themePresetText : themeNotification; - - const arc = SCALE(6); - const padding = SCALE(20); - const lines = text.split('\n'); - const lineH = gr.CalcTextHeight('Ag', ft.notification); - const maxWidth = Math.max(...lines.map(line => gr.CalcTextWidth(line, ft.notification))); - const boxW = maxWidth + padding * 3; - const boxH = lineH * lines.length + padding * 2; - - const fullW = - pref.layout === 'default' - && - displayPlaylist && pref.playlistLayout === 'full' - || - displayLibrary && pref.libraryLayout === 'full' - || - displayBiography && pref.biographyLayout === 'full' - || - pref.displayLyrics && pref.lyricsLayout === 'full'; - - const cover = fb.IsPlaying && albumArt && pref.layout !== 'compact' && !fullW; - const noCoverDefault = pref.layout === 'default' && !fullW && !pref.panelWidthAuto; - - const x = Math.round((cover ? albumArtSize.x + albumArtSize.w * 0.5 : noCoverDefault ? ww * 0.25 : ww * 0.5) - boxW * 0.5); - const y = Math.round((cover ? geo.topMenuHeight + albumArtSize.h * 0.5 : ((wh - geo.topMenuHeight - geo.lowerBarHeight) * 0.5) + geo.topMenuHeight) - boxH * 0.5); - - gr.SetSmoothingMode(SmoothingMode.AntiAlias); - gr.FillRoundRect(x, y, boxW, boxH, arc, arc, col.popupBg); - gr.DrawRoundRect(x, y, boxW, boxH, arc, arc, SCALE(2), 0x64000000); - gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); - gr.DrawString(text, ft.notification, col.popupText, x, y, boxW, boxH, StringFormat(1, 1, 4)); -} + const layoutArtwork = grSet.layout === 'artwork' && + !this.displayPlaylistArtwork && !this.displayLibrary && !this.displayBiography; + const displayAlbumArtDetailsShadows = fb.IsPlaying && + (this.albumArt && this.albumArtScaled || this.noAlbumArtStub) && !this.displayLibrarySplit() && (layoutDefault || layoutArtwork); -/** - * Draws all the shadows for album art and panels. - * @param {GdiGraphics} gr - */ -function drawPanelShadows(gr) { - const drawPanelShadowsProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> panel shadows'); + const displayDetails = (!this.discArt || !grSet.displayDiscArt) && this.displayDetails; - // * SHADOWS FOR ALBUM ART, noAlbumArtStub AND DETAILS * // - const layoutDefault = pref.layout === 'default' && - (!displayPlaylist && !displayLibrary && !displayBiography || - !displayBiography && (pref.playlistLayout !== 'full' && displayPlaylist || pref.libraryLayout === 'normal' && displayLibrary)); + const noDefaultLayout = grSet.layout !== 'default'; - const layoutArtwork = pref.layout === 'artwork' && !displayPlaylistArtwork && !displayLibrary && !displayBiography; + if (displayAlbumArtDetailsShadows) { + gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); - const displayAlbumArtDetailsShadows = fb.IsPlaying && - (albumArt && albumArtScaled || noAlbumArtStub) && !displayLibrarySplit() && (layoutDefault || layoutArtwork); + // Top shadow + gr.FillGradRect(0, libSet.albumArtShow && grSet.libraryLayout === 'full' && this.displayLibrary ? lib.ui.y - (RES._4K ? 10 : 6) : this.albumArtSize.y - (RES._4K ? 10 : 6), + displayDetails && grSet.noDiscArtBg || noDefaultLayout ? this.ww : this.albumArtSize.x + this.albumArtSize.w, RES._4K ? 10 : 6, 90, 0, grCol.shadow); - const displayDetails = (!discArt || !pref.displayDiscArt) && - (pref.layout === 'artwork' ? !displayPlaylistArtwork : !displayPlaylist) && !displayLibrary && !displayBiography; + if (displayDetails && !grSet.noDiscArtBg && !this.noAlbumArtStub) { + // Middle shadow + gr.FillGradRect(this.noAlbumArtStub ? this.ww * 0.5 - 4 : this.albumArtSize.x + this.albumArtSize.w, this.noAlbumArtStub ? this.topMenuHeight : this.albumArtSize.y - 3, + 4, this.noAlbumArtStub ? this.wh - this.topMenuHeight - this.lowerBarHeight : this.albumArtSize.h + 5, 0.5, + this.noAlbumArtStub ? 0 : grSet.styleBlackAndWhite ? RGB(0, 0, 0) : grCol.shadow, this.noAlbumArtStub ? grSet.styleBlackAndWhite ? RGB(0, 0, 0) : grCol.shadow : 0); + } + // Bottom shadow + gr.FillGradRect(0, libSet.albumArtShow && grSet.libraryLayout === 'full' && this.displayLibrary ? lib.ui.y + lib.ui.h + (RES._4K ? 0 : -1) : this.albumArtSize.y + this.albumArtSize.h + (RES._4K ? 0 : -1), + displayDetails && grSet.noDiscArtBg || noDefaultLayout ? this.ww : this.albumArtSize.x + this.albumArtSize.w, SCALE(5), 90, grCol.shadow, 0); + } - const noDefaultLayout = pref.layout !== 'default'; + // * SHADOWS FOR ALL PANELS * // + const panelLayoutNormal = grSet.layout === 'default' && + (grSet.playlistLayout === 'normal' && this.displayPlaylist && !this.displayBiography || + grSet.libraryLayout === 'normal' && this.displayLibrary || + grSet.biographyLayout === 'normal' && this.displayBiography || + this.displayLibrarySplit()); + + const displayPanelShadows = + grSet.layout !== 'artwork' && this.displayPlaylist || this.displayPlaylistArtwork || this.displayLibrary || this.displayBiography || this.displayCustomThemeMenu && !fb.IsPlaying; + + if (displayPanelShadows) { + const x = + this.displayLibrarySplit() || this.displayBiography || this.displayCustomThemeMenu && !fb.IsPlaying || grSet.layout !== 'default' || !panelLayoutNormal ? 0 : + grSet.panelWidthAuto ? this.albumArtSize.x + this.albumArtSize.w : this.ww * 0.5; + + gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); + + // Top shadow + gr.FillGradRect(x, this.topMenuHeight - (RES._4K ? 10 : 6), this.ww, RES._4K ? 10 : 6, 90, 0, grCol.shadow); + + if (panelLayoutNormal && pl.playlist.x !== 0 && !grSet.hideMiddlePanelShadow) { + // Middle shadow for playlist + gr.FillGradRect(grSet.panelWidthAuto ? this.displayLibrarySplit() ? this.wh - this.topMenuHeight - this.lowerBarHeight - 4 : this.albumArtSize.x + this.albumArtSize.w - 4 : this.ww * 0.5 - 4, this.topMenuHeight, 4, this.wh - this.topMenuHeight - this.lowerBarHeight, 0.5, 0, + grSet.styleBlackAndWhite && this.noAlbumArtStub ? RGB(0, 0, 0) : grSet.styleNighttime || grSet.styleBlackAndWhite2 || grSet.styleRebornBlack ? RGBA(0, 0, 0, 30) : grCol.shadow); + // Middle shadow for album art + if (this.albumArt && this.albumArtSize.w !== this.ww * 0.5 && !this.displayLibrarySplit() && !this.displayBiography && !this.noAlbumArtStub) { + gr.FillGradRect(this.albumArtSize.x + this.albumArtSize.w - 2, this.albumArtSize.y, 4, this.albumArtSize.h, 0.5, grSet.styleBlackAndWhite ? RGB(0, 0, 0) : grCol.shadow, 0); + } + } + // Bottom shadow + gr.FillGradRect(x, this.wh - this.lowerBarHeight + (RES._4K ? 0 : -1), this.ww, SCALE(5), 90, grCol.shadow, 0); + } - if (displayAlbumArtDetailsShadows) { - gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); + if (drawPanelShadowsProfiler) drawPanelShadowsProfiler.Print(); + } + + /** + * Draws the top menu bar. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawTopMenuBar(gr) { + const drawTopMenuBarProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> top menu bar'); + + for (const i in this.btn) { // Can't replace for..in until non-numeric indexes are removed + const btn = this.btn[i]; + const { x, y, w, h, img } = btn; + const disabled = btn.isEnabled ? !btn.isEnabled() : false; + const alpha = disabled ? 140 : 255; - // Top shadow - gr.FillGradRect(0, ppt.albumArtShow && pref.libraryLayout === 'full' && displayLibrary ? ui.y - (RES_4K ? 10 : 6) : albumArtSize.y - (RES_4K ? 10 : 6), - displayDetails && pref.noDiscArtBg || noDefaultLayout ? ww : albumArtSize.x + albumArtSize.w, RES_4K ? 10 : 6, 90, 0, col.shadow); + if ((i === 'back' || i === 'forward') && !this.displayPlaylist) { + continue; + } - if (displayDetails && !pref.noDiscArtBg && !noAlbumArtStub) { - // Middle shadow - gr.FillGradRect(noAlbumArtStub ? ww * 0.5 - 4 : albumArtSize.x + albumArtSize.w, noAlbumArtStub ? geo.topMenuHeight : albumArtSize.y - 3, - 4, noAlbumArtStub ? wh - geo.topMenuHeight - geo.lowerBarHeight : albumArtSize.h + 5, 0.5, - noAlbumArtStub ? 0 : pref.styleBlackAndWhite ? RGB(0, 0, 0) : col.shadow, noAlbumArtStub ? pref.styleBlackAndWhite ? RGB(0, 0, 0) : col.shadow : 0); + if (img) { + gr.DrawImage(img[0], x, y, w, h, 0, 0, w, h, 0, alpha); // Normal + if (!disabled) { + btn.hoverAlpha && gr.DrawImage(img[1], x, y, w, h, 0, 0, w, h, 0, btn.hoverAlpha); + btn.downAlpha && gr.DrawImage(img[2], x, y, w, h, 0, 0, w, h, 0, btn.downAlpha); + btn.enabled && img[3] && gr.DrawImage(img[3], x, y, w, h, 0, 0, w, h, 0, 255); + } + } } - // Bottom shadow - gr.FillGradRect(0, ppt.albumArtShow && pref.libraryLayout === 'full' && displayLibrary ? ui.y + ui.h + (RES_4K ? 0 : -1) : albumArtSize.y + albumArtSize.h + (RES_4K ? 0 : -1), - displayDetails && pref.noDiscArtBg || noDefaultLayout ? ww : albumArtSize.x + albumArtSize.w, SCALE(5), 90, col.shadow, 0); - } - // * SHADOWS FOR ALL PANELS * // - const panelLayoutNormal = pref.layout === 'default' && - (pref.playlistLayout === 'normal' && displayPlaylist && !displayBiography || - pref.libraryLayout === 'normal' && displayLibrary || - pref.biographyLayout === 'normal' && displayBiography || - displayLibrarySplit()); + if (drawTopMenuBarProfiler) drawTopMenuBarProfiler.Print(); + } - const displayPanelShadows = - pref.layout !== 'artwork' && displayPlaylist || displayPlaylistArtwork || displayLibrary || displayBiography || displayCustomThemeMenu && !fb.IsPlaying; + /** + * Draws the lower bar. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawLowerBar(gr) { + const drawLowerBarProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> lower bar'); + const lowerBarTop = this.wh - this.lowerBarHeight + (grSet.layout === 'default' ? (RES._4K ? 65 : 35) : (RES._4K ? 33 : 18)); + const lowerMargin = SCALE(grSet.layout === 'compact' || grSet.layout === 'artwork' ? 80 : grSet.showTransportControls_default ? 80 : 120); + const lowerBarFontSize = grSet[`lowerBarFontSize_${grSet.layout}`]; + const showLowerBarArtist = grSet[`showLowerBarArtist_${grSet.layout}`]; + const showLowerBarTrackNum = grSet[`showLowerBarTrackNum_${grSet.layout}`]; + const showLowerBarTitle = grSet[`showLowerBarTitle_${grSet.layout}`]; + const showLowerBarComposer = grSet[`showLowerBarComposer_${grSet.layout}`]; + const showLowerBarArtistFlags = grSet[`showLowerBarArtistFlags_${grSet.layout}`]; + const showLowerBarPlaybackTime = grSet[`showPlaybackTime_${grSet.layout}`]; + const showProgressBar = grSet[`showProgressBar_${grSet.layout}`]; + const showWaveformBar = grSet[`showWaveformBar_${grSet.layout}`]; + const showPeakmeterBar = grSet[`showPeakmeterBar_${grSet.layout}`]; + + const flagSize = + this.flagImgs.length >= 6 ? SCALE(84 + lowerBarFontSize * 6) : + this.flagImgs.length === 5 ? SCALE(70 + lowerBarFontSize * 5) : + this.flagImgs.length === 4 ? SCALE(56 + lowerBarFontSize * 4) : + this.flagImgs.length === 3 ? SCALE(42 + lowerBarFontSize * 3) : + this.flagImgs.length === 2 ? SCALE(28 + lowerBarFontSize * 2) : + this.flagImgs.length === 1 ? SCALE(14 + lowerBarFontSize) : ''; + const availableFlags = showLowerBarArtistFlags && this.flagImgs.length ? flagSize : 0; + + // * Calculate all transport buttons width + const showPlaybackOrderBtn = grSet[`showPlaybackOrderBtn_${grSet.layout}`]; + const showReloadBtn = grSet[`showReloadBtn_${grSet.layout}`]; + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; + const transportBtnSize = grSet[`transportButtonSize_${grSet.layout}`]; + const transportBtnSpacing = grSet[`transportButtonSpacing_${grSet.layout}`]; + const buttonSize = SCALE(transportBtnSize); + const buttonSpacing = SCALE(transportBtnSpacing); + const buttonCount = 4 + (showPlaybackOrderBtn ? 1 : 0) + (showReloadBtn ? 1 : 0) + (showVolumeBtn ? 1 : 0); + + // * Setup time area width + const timeAreaWidth = this.ww > 400 ? grStr.disc !== '' && grSet.layout === 'default' ? gr.CalcTextWidth(`${grStr.disc} ${grStr.time} ${grStr.length}`, grFont.lowerBarTitle) : gr.CalcTextWidth(` ${grStr.time} ${grStr.length}`, grFont.lowerBarTitle) : 0; + + // * Setup width for artist and song title + const playbackTime = grSet[`showPlaybackTime_${grSet.layout}`]; + const availableWidth = grSet.layout === 'default' && grSet.showTransportControls_default && (grSet.showLowerBarArtist_default || grSet.showLowerBarTitle_default) ? + Math.round(this.ww * 0.5 - lowerMargin - availableFlags - ((buttonSize * buttonCount + buttonSpacing * buttonCount) / 2)) : Math.round(this.ww - lowerMargin - availableFlags - (playbackTime ? timeAreaWidth : 0)); + + // * Measure width and height for artist, orig artist and song title + const artistWidth = gr.MeasureString(grStr.artist, grFont.lowerBarArtist, 0, 0, 0, 0).Width; + const artistHeight = gr.CalcTextHeight(grStr.artist, grFont.lowerBarArtist); + const trackNumWidth = Math.ceil(gr.MeasureString(grStr.tracknum === '' ? '00.' : grStr.tracknum, grFont.lowerBarTitle, 0, 0, 0, 0).Width); + const titleMeasurements = gr.MeasureString(showLowerBarComposer ? grStr.titleLower + grStr.composer : grStr.titleLower, grFont.lowerBarTitle, 0, 0, 0, 0); + // const titleWidth = trackNumWidth + gr.MeasureString(showLowerBarComposer ? grStr.titleLower + grStr.composer : grStr.titleLower, grFont.lowerBarTitle, 0, 0, 0, 0).Width; + const titleHeight = gr.CalcTextHeight(grStr.titleLower, grFont.lowerBarTitle); + const artistTitleWidth = gr.MeasureString(grStr.artist, grFont.lowerBarArtist, 0, 0, 0, 0).Width + trackNumWidth + gr.MeasureString(showLowerBarComposer ? grStr.titleLower + grStr.composer : grStr.titleLower, grFont.lowerBarTitle, 0, 0, 0, 0).Width + gr.MeasureString(grStr.original_artist, grFont.lowerBarTitle, 0, 0, 0, 0).Width; + const oneLine = artistTitleWidth < availableWidth || !showLowerBarTitle; + const twoLines = artistTitleWidth > availableWidth && showLowerBarTitle; + const lineCorrection = SCALE(grSet.customThemeFonts ? RES._4K ? 0 : 4 : RES._4K ? 0 : 2); + + // * Adjustments + const flagWidth = showLowerBarArtistFlags && this.flagImgs.length && grStr.tracknum < 100 ? SCALE(14) + SCALE(lowerBarFontSize) : trackNumWidth + SCALE(6); + const heightAdjustment = grSet.customThemeFonts ? 0 : ((lowerBarFontSize === 12 || lowerBarFontSize === 14) && !RES._4K || (lowerBarFontSize === 16 || lowerBarFontSize === 18 || lowerBarFontSize === 20 || lowerBarFontSize === 22) && RES._4K) ? 1 : 0; + const trackNumAdjustment = gr.MeasureString('\u2013', grFont.lowerBarTitle, 0, 0, 0, 0).Width; + const titleAdjustment = gr.MeasureString(grSet.customThemeFonts ? '\u2013.' : 'M', grFont.lowerBarTitle, 0, 0, 0, 0).Width; + const titleAdjustment2 = gr.MeasureString('-', grFont.lowerBarTitle, 0, 0, 0, 0).Width; + + // * Setup artist, track number and title + const artist = showLowerBarArtist ? grStr.artist : ''; + const artistX = twoLines ? grm.progBar.x + availableFlags : Math.round(grm.progBar.x + availableFlags - (grSet.layout === 'default' ? SCALE(1) : 0)); + + const artistY = twoLines ? Math.round(lowerBarTop - lineCorrection - artistHeight + (grSet.customThemeFonts ? artistHeight * 0.125 : 0) + (lowerBarFontSize < 18 ? SCALE(-2) : lowerBarFontSize > 18 ? SCALE(RES._QHD ? 1 : 3) : 0)) : + Math.round(lowerBarTop - lineCorrection); + + const trackNum = twoLines ? showLowerBarTrackNum && showLowerBarTitle || !showLowerBarTitle && !fb.IsPlaying ? grStr.tracknum : '' : + showLowerBarTrackNum && showLowerBarTitle || (!showLowerBarTrackNum || !showLowerBarTitle) && !fb.IsPlaying ? grStr.tracknum === '' ? '-' : grStr.tracknum : + !showLowerBarTrackNum && showLowerBarArtist && fb.IsPlaying ? '-' : ''; + + const trackNumX = twoLines ? grm.progBar.x : + showLowerBarArtist && fb.IsPlaying && fb.PlaybackLength > 0 ? Math.floor(grm.progBar.x + availableFlags + artistWidth + (!showLowerBarTrackNum || grStr.tracknum === '' ? titleAdjustment2 * 0.5 : trackNumAdjustment)) : grm.progBar.x; + + const trackNumY = Math.round(lowerBarTop - lineCorrection - heightAdjustment); + + const title = twoLines ? showLowerBarTitle || !showLowerBarTitle && !fb.IsPlaying ? showLowerBarComposer && fb.IsPlaying ? grStr.titleLower + grStr.original_artist + grStr.composer : grStr.titleLower : '' : + showLowerBarTitle || !showLowerBarTitle && !fb.IsPlaying ? showLowerBarComposer && fb.IsPlaying ? grStr.titleLower + grStr.composer : grStr.titleLower : ''; + + const titleX = twoLines ? !showLowerBarTrackNum || grStr.tracknum === '' ? grm.progBar.x : Math.round(grm.progBar.x + flagWidth) : + // When not playing or stopped + !fb.IsPlaying ? Math.round(grm.progBar.x + trackNumWidth) : + // Artist and no track number displayed + showLowerBarArtist ? !showLowerBarTrackNum || grStr.tracknum === '' ? Math.floor(grm.progBar.x + availableFlags + artistWidth + titleAdjustment) : + // Artist with track number displayed + Math.round(grm.progBar.x + availableFlags + artistWidth + trackNumWidth + (fb.PlaybackLength > 0 ? titleAdjustment : 0)) : + // No artist and no track number displayed + !showLowerBarTrackNum || grStr.tracknum === '' ? grm.progBar.x : + // No artist with track number displayed + Math.round(grm.progBar.x + trackNumWidth + (fb.PlaybackLength > 0 ? titleAdjustment : 0)); + + const titleY = trackNumY; + + // * Apply better anti-aliasing on smaller font sizes in HD res + gr.SetTextRenderingHint(!RES._4K && lowerBarFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); + + // * Artist, tracknum, title + if (oneLine || twoLines && grSet.layout === 'default') DrawString(gr, artist, grFont.lowerBarArtist, grCol.lowerBarArtist, artistX, artistY, availableWidth, artistHeight, Stringformat.trim_ellipsis_char); + gr.DrawString(trackNum, grFont.lowerBarTitle, grCol.lowerBarTitle, trackNumX, trackNumY, trackNumWidth - timeAreaWidth, titleHeight, StringFormat(0, 0, 4, 0x00001000)); + DrawString(gr, title, grFont.lowerBarTitle, grCol.lowerBarTitle, titleX, titleY, fb.IsPlaying ? availableWidth + (twoLines ? availableFlags : 0) : this.ww, titleHeight, Stringformat.trim_ellipsis_char); + + // * Artist flags + if (showLowerBarArtist && showLowerBarArtistFlags && (grSet.layout === 'default' || grSet.layout !== 'default' && !twoLines)) { + const maxFlags = Math.min(this.flagImgs.length, 6); + const marginLeft = SCALE(grSet.layout !== 'default' ? 20 : 40); + let flagsLeft = marginLeft - (RES._4K ? 1 : 0); + for (let i = 0; i < maxFlags; i++) { + gr.DrawImage(this.flagImgs[i], flagsLeft, Math.round(artistY - (this.flagImgs[i].Height / (artistHeight + SCALE(2))) - (RES._4K ? 1 : 0)), this.flagImgs[i].Width + SCALE(lowerBarFontSize) - SCALE(26), artistHeight + SCALE(2), 0, 0, this.flagImgs[i].Width, this.flagImgs[i].Height); + flagsLeft += this.flagImgs[i].Width + SCALE(lowerBarFontSize) - SCALE(18); + } + } - if (displayPanelShadows) { - const x = - displayLibrarySplit() || displayBiography || displayCustomThemeMenu && !fb.IsPlaying || pref.layout !== 'default' || !panelLayoutNormal ? 0 : - pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : ww * 0.5; + // * Playback time, length, disc number + if (this.ww > 400) { + gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); + let width = gr.CalcTextWidth(` ${grStr.length}`, grFont.lowerBarLength); + const lowerBarVersionW = gr.CalcTextWidth(` ${grStr.time}`, grFont.lowerBarLength); + const lowerBarVersionH = Math.ceil(titleMeasurements.Height); + const lowerBarVersionX = this.ww - SCALE(grSet.layout !== 'default' ? 20 : 40) - lowerBarVersionW; + const lowerBarVersionY = Math.round(lowerBarTop - lineCorrection - heightAdjustment); + const lowerBarLengthX = this.ww - SCALE(grSet.layout !== 'default' ? 20 : 40) - width; + const lowerBarLengthY = lowerBarVersionY; + const lowerBarLengthW = width; + const lowerBarLengthH = lowerBarVersionH; + this.lowerBarTimeX = this.ww - SCALE(grSet.layout !== 'default' ? 20 : 40) - (this.isStreaming ? width : width * 2); + this.lowerBarTimeY = Math.round(lowerBarTop - lineCorrection); + this.lowerBarTimeW = lowerBarLengthW; + this.lowerBarTimeH = lowerBarVersionH; + const lowerBarDiscW = gr.CalcTextWidth(` ${grStr.disc}`, grFont.lowerBarDisc); + const lowerBarDiscH = lowerBarVersionH; + const lowerBarDiscY = lowerBarVersionY; + const lowerBarDiscX = this.lowerBarTimeX - lowerBarDiscW; + + if (showLowerBarPlaybackTime && fb.PlaybackLength > 0) { // * Playing track + gr.DrawString(grStr.length, grFont.lowerBarLength, grCol.lowerBarLength, lowerBarLengthX, lowerBarLengthY, lowerBarLengthW, lowerBarLengthH, StringFormat(2, 0)); + gr.DrawString(grStr.time, grFont.lowerBarTime, grCol.lowerBarTime, this.lowerBarTimeX, this.lowerBarTimeY, this.lowerBarTimeW, this.lowerBarTimeH, StringFormat(2, 0)); + width += gr.CalcTextWidth(` ${grStr.time}`, grFont.lowerBarTime); + gr.DrawString(grSet.layout !== 'default' ? '' : grStr.disc, grFont.lowerBarDisc, grCol.lowerBarTitle, lowerBarDiscX, lowerBarDiscY, lowerBarDiscW, lowerBarDiscH, StringFormat(2, 0)); + } + else if (showLowerBarPlaybackTime && fb.IsPlaying && this.isStreaming) { // * Streaming, but still want to show time + gr.DrawString(grStr.time, grFont.lowerBarTime, grCol.lowerBarTitle, this.lowerBarTimeX, this.lowerBarTimeY, this.lowerBarTimeW, this.lowerBarTimeH, StringFormat(2, 0)); + } + else { // * Not playing anything, will show theme version or update link if available + let offset = 0; + if (grCfg.updateAvailable && grCfg.updateHyperlink) { + offset = grCfg.updateHyperlink.getWidth(); + grCfg.updateHyperlink.setContainerWidth(this.ww); + grCfg.updateHyperlink.setY(lowerBarTop); + grCfg.updateHyperlink.setXOffset(this.ww - offset - SCALE(grSet.layout !== 'default' ? 20 : 40)); + grCfg.updateHyperlink.draw(gr, grCol.lowerBarTitle); + } + if (showLowerBarPlaybackTime) { + gr.DrawString(grStr.time, grFont.lowerBarLength, grCol.lowerBarTitle, lowerBarVersionX - offset, lowerBarVersionY, lowerBarVersionW, lowerBarVersionH, StringFormat(2, 0)); + } + } + if (showLowerBarPlaybackTime && fb.IsPlaying) { // * Switch to playback time remaining + this.btn.playbackTime = new Button(this.ww - timeAreaWidth - SCALE(grSet.layout !== 'default' ? 20 : 40), this.lowerBarTimeY, + timeAreaWidth, this.lowerBarTimeH, showLowerBarPlaybackTime ? 'PlaybackTime' : '', '', showLowerBarPlaybackTime ? 'Switch playback time' : ''); + } + } - gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); + // * LOWER BAR TOOLTIP * // + if ((grSet.showTooltipMain || grSet.showTooltipTruncated) && grm.lowerTip && fb.IsPlaying) { + grm.lowerTip.draw(gr); + } - // Top shadow - gr.FillGradRect(x, geo.topMenuHeight - (RES_4K ? 10 : 6), ww, RES_4K ? 10 : 6, 90, 0, col.shadow); + // * VOLUME BTN * // + if (showVolumeBtn && this.loadingThemeComplete) { + grm.volBtn.draw(gr); + } - if (panelLayoutNormal && playlist.x !== 0 && !pref.hideMiddlePanelShadow) { - // Middle shadow for playlist - gr.FillGradRect(pref.panelWidthAuto ? displayLibrarySplit() ? wh - geo.topMenuHeight - geo.lowerBarHeight - 4 : albumArtSize.x + albumArtSize.w - 4 : ww * 0.5 - 4, geo.topMenuHeight, 4, wh - geo.topMenuHeight - geo.lowerBarHeight, 0.5, 0, - pref.styleBlackAndWhite && noAlbumArtStub ? RGB(0, 0, 0) : pref.styleNighttime || pref.styleBlackAndWhite2 || pref.styleRebornBlack ? RGBA(0, 0, 0, 30) : col.shadow); - // Middle shadow for album art - if (albumArt && albumArtSize.w !== ww * 0.5 && !displayLibrarySplit() && !displayBiography && !noAlbumArtStub) { - gr.FillGradRect(albumArtSize.x + albumArtSize.w - 2, albumArtSize.y, 4, albumArtSize.h, 0.5, pref.styleBlackAndWhite ? RGB(0, 0, 0) : col.shadow, 0); - } + // * PROGRESS BAR * // + if (showProgressBar && (grSet.seekbar === 'progressbar' || !fb.IsPlaying)) { + this.progressBarY = Math.round(lowerBarTop + titleMeasurements.Height + grm.progBar.h); + grm.progBar.setY(this.progressBarY); + grm.progBar.draw(gr); + } + // * WAVEFORM BAR * // + else if (showWaveformBar && grSet.seekbar === 'waveformbar') { + this.waveformBarY = Math.round(lowerBarTop + titleMeasurements.Height + grm.waveBar.h); + grm.waveBar.setY(this.waveformBarY); + grm.waveBar.draw(gr); + } + // * PEAKMETER BAR * // + else if (showPeakmeterBar && grSet.seekbar === 'peakmeterbar') { + this.peakmeterBarY = Math.round(lowerBarTop + titleMeasurements.Height + grm.peakBar.h * 0.5); + grm.peakBar.setY(this.peakmeterBarY); + grm.peakBar.draw(gr); } - // Bottom shadow - gr.FillGradRect(x, wh - geo.lowerBarHeight + (RES_4K ? 0 : -1), ww, SCALE(5), 90, col.shadow, 0); - } - if (drawPanelShadowsProfiler) drawPanelShadowsProfiler.Print(); -} + if (drawLowerBarProfiler) drawLowerBarProfiler.Print(); + } + /** + * Draws the custom theme menu. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawCustomThemeMenu(gr) { + if (!this.displayCustomThemeMenu || grSet.layout !== 'default') return; -/** - * Draws the top menu bar. - * @param {GdiGraphics} gr - */ -function drawTopMenuBar(gr) { - const drawTopMenuBarProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> top menu bar'); + const x = this.displayBiography || this.displayLyrics ? this.ww * 0.5 : this.displayDetails ? this.albumArtSize.x : 0; + const y = this.topMenuHeight; + const width = !fb.IsPlaying && this.displayDetails || this.displayLyrics && !this.albumArt ? this.ww : this.displayDetails ? this.albumArtSize.w : this.ww * 0.5; + const height = this.wh - this.topMenuHeight - this.lowerBarHeight; - for (const i in btns) { // Can't replace for..in until non-numeric indexes are removed - const btn = btns[i]; - const { x, y, w, h, img } = btn; + gr.SetSmoothingMode(SmoothingMode.None); + gr.FillSolidRect(x, y, width, height, pl.col.bg); + for (const c of CustomMenu.controlList) c.draw(gr); - if ((i === 'back' || i === 'forward') && !displayPlaylist) { - continue; + if (CustomMenu.activeControl && CustomMenu.activeControl instanceof CustomMenuDropDown && CustomMenu.activeControl.isSelectUp) { + CustomMenu.activeControl.draw(gr); } + } - const disabled = btn.isEnabled ? !btn.isEnabled() : false; + /** + * Draws the custom metadata grid menu. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawMetadataGridMenu(gr) { + if (!this.displayMetadataGridMenu || grSet.layout !== 'default' || (this.displayPlaylist || this.displayLibrary || this.displayBiography || this.displayLyrics)) return; - if (img) { - const alpha = disabled ? 140 : 255; - gr.DrawImage(img[0], x, y, w, h, 0, 0, w, h, 0, alpha); // Normal - if (!disabled) { - btn.hoverAlpha && gr.DrawImage(img[1], x, y, w, h, 0, 0, w, h, 0, btn.hoverAlpha); - btn.downAlpha && gr.DrawImage(img[2], x, y, w, h, 0, 0, w, h, 0, btn.downAlpha); - btn.enabled && img[3] && gr.DrawImage(img[3], x, y, w, h, 0, 0, w, h, 0, 255); - } + const x = this.albumArtSize.x - 1; + const y = this.topMenuHeight; + const width = this.ww; + const height = this.wh - this.topMenuHeight - this.lowerBarHeight; + + gr.FillSolidRect(x, y, width, height, pl.col.bg); + for (const c of CustomMenu.controlList) c.draw(gr); + + if (CustomMenu.activeControl && CustomMenu.activeControl instanceof CustomMenuDropDown && CustomMenu.activeControl.isSelectUp) { + CustomMenu.activeControl.draw(gr); } } - if (drawTopMenuBarProfiler) drawTopMenuBarProfiler.Print(); -} + /** + * Draws styled tooltips that will make standard tooltips look fancy. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawStyledTooltips(gr) { + if (this.styledTooltipText === '' || !this.styledTooltipReady || !grSet.showStyledTooltips) return; + + const drawStyledTooltipsProfiler = this.showExtraDrawTiming && fb.CreateProfiler('on_paint -> styled tooltips'); + const tooltipFontSize = grSet[`tooltipFontSize_${grSet.layout}`]; + const offset = SCALE(30); + const padding = SCALE(15); + const edgeSpace = padding * 0.5; + const arc = SCALE(6); + const w = Math.min(gr.MeasureString(this.styledTooltipText, grFont.tooltip, 0, 0, 0, 0).Width + padding + 1, this.ww - (this.state.mouse_x > this.ww * 0.85 ? this.state.mouse_x - this.ww * 0.15 : this.state.mouse_x) - edgeSpace); + const h = Math.min(gr.MeasureString(this.styledTooltipText, grFont.tooltip, 0, 0, w, this.wh).Height + padding, this.wh - (this.state.mouse_y > this.wh * 0.85 ? this.state.mouse_y - this.wh * 0.15 : this.state.mouse_y) - edgeSpace - offset); + const x = this.state.mouse_x > this.ww * 0.85 ? this.state.mouse_x - w : this.state.mouse_x; // * When tooltip is too close to the right edge, it will be drawn on the left side of the mouse cursor + const y = this.state.mouse_y > this.wh * 0.85 ? this.state.mouse_y - h : this.state.mouse_y + offset; // * When tooltip is too close to the bottom edge, it will be drawn on the top side of the mouse cursor + const throttleRepaintRect = _Throttle((x, y, w, h, force = false) => window.RepaintRect(x, y, w, h, force), 50); + + // * Apply better anti-aliasing on smaller font sizes in HD res + gr.SetTextRenderingHint(!RES._4K && tooltipFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); + gr.FillRoundRect(x, y, w, h, arc, arc, RGBtoRGBA(grCol.popupBg, 220)); + gr.DrawRoundRect(x, y, w, h, arc, arc, SCALE(2), 0x64000000); + gr.DrawString(this.styledTooltipText, grFont.tooltip, grCol.popupText, x + padding * 0.5, y + padding * 0.5, w - padding, h - padding, StringFormat(0, 0, 4)); + throttleRepaintRect(x - offset * 0.5, y - offset * 0.5, w + offset, h + offset); + + if (drawStyledTooltipsProfiler) drawStyledTooltipsProfiler.Print(); + } + /** + * Draws the startup background until everything in the theme is loaded. + * Pseudo delay background logo mask when loading the theme, otherwise it will show ugly repaints when initializing since smp v1.6.1. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawStartupBackground(gr) { + if (this.loadingThemeComplete) return; + gr.FillSolidRect(0, 0, this.ww, this.wh, grCol.loadingThemeBg); + if (grSet.showPreloaderLogo) drawLogo(gr); + } -/** - * Draws the lower bar. - * @param {GdiGraphics} gr - */ -function drawLowerBar(gr) { - const drawLowerBarProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> lower bar'); - const lowerBarTop = wh - geo.lowerBarHeight + (pref.layout === 'default' ? (RES_4K ? 65 : 35) : (RES_4K ? 33 : 18)); - const lowerMargin = SCALE(pref.layout === 'compact' || pref.layout === 'artwork' ? 80 : pref.showTransportControls_default ? 80 : 120); - const lowerBarFontSize = pref[`lowerBarFontSize_${pref.layout}`]; - const showLowerBarArtist = pref[`showLowerBarArtist_${pref.layout}`]; - const showLowerBarTrackNum = pref[`showLowerBarTrackNum_${pref.layout}`]; - const showLowerBarTitle = pref[`showLowerBarTitle_${pref.layout}`]; - const showLowerBarComposer = pref[`showLowerBarComposer_${pref.layout}`]; - const showLowerBarArtistFlags = pref[`showLowerBarArtistFlags_${pref.layout}`]; - const showLowerBarPlaybackTime = pref[`showPlaybackTime_${pref.layout}`]; - const showProgressBar = pref[`showProgressBar_${pref.layout}`]; - const showWaveformBar = pref[`showWaveformBar_${pref.layout}`]; - const showPeakmeterBar = pref[`showPeakmeterBar_${pref.layout}`]; - - const flagSize = - flagImgs.length >= 6 ? SCALE(84 + lowerBarFontSize * 6) : - flagImgs.length === 5 ? SCALE(70 + lowerBarFontSize * 5) : - flagImgs.length === 4 ? SCALE(56 + lowerBarFontSize * 4) : - flagImgs.length === 3 ? SCALE(42 + lowerBarFontSize * 3) : - flagImgs.length === 2 ? SCALE(28 + lowerBarFontSize * 2) : - flagImgs.length === 1 ? SCALE(14 + lowerBarFontSize) : ''; - const availableFlags = showLowerBarArtistFlags && flagImgs.length ? flagSize : 0; - - // * Calculate all transport buttons width - const showPlaybackOrderBtn = pref[`showPlaybackOrderBtn_${pref.layout}`]; - const showReloadBtn = pref[`showReloadBtn_${pref.layout}`]; - const showVolumeBtn = pref[`showVolumeBtn_${pref.layout}`]; - const transportBtnSize = pref[`transportButtonSize_${pref.layout}`]; - const transportBtnSpacing = pref[`transportButtonSpacing_${pref.layout}`]; - const buttonSize = SCALE(transportBtnSize); - const buttonSpacing = SCALE(transportBtnSpacing); - const buttonCount = 4 + (showPlaybackOrderBtn ? 1 : 0) + (showReloadBtn ? 1 : 0) + (showVolumeBtn ? 1 : 0); - - // * Setup time area width - const timeAreaWidth = ww > 400 ? str.disc !== '' && pref.layout === 'default' ? gr.CalcTextWidth(`${str.disc} ${str.time} ${str.length}`, ft.lower_bar_title) : gr.CalcTextWidth(` ${str.time} ${str.length}`, ft.lower_bar_title) : 0; - - // * Setup width for artist and song title - const playbackTime = pref[`showPlaybackTime_${pref.layout}`]; - const availableWidth = pref.layout === 'default' && pref.showTransportControls_default && (pref.showLowerBarArtist_default || pref.showLowerBarTitle_default) ? - Math.round(ww * 0.5 - lowerMargin - availableFlags - ((buttonSize * buttonCount + buttonSpacing * buttonCount) / 2)) : Math.round(ww - lowerMargin - availableFlags - (playbackTime ? timeAreaWidth : 0)); - - // * Measure width and height for artist, orig artist and song title - const artistWidth = gr.MeasureString(str.artist, ft.lower_bar_artist, 0, 0, 0, 0).Width; - const artistHeight = gr.CalcTextHeight(str.artist, ft.lower_bar_artist); - const trackNumWidth = Math.ceil(gr.MeasureString(str.tracknum === '' ? '00.' : str.tracknum, ft.lower_bar_title, 0, 0, 0, 0).Width); - const titleMeasurements = gr.MeasureString(showLowerBarComposer ? str.title_lower + str.composer : str.title_lower, ft.lower_bar_title, 0, 0, 0, 0); - const titleWidth = trackNumWidth + gr.MeasureString(showLowerBarComposer ? str.title_lower + str.composer : str.title_lower, ft.lower_bar_title, 0, 0, 0, 0).Width; - const titleHeight = gr.CalcTextHeight(str.title_lower, ft.lower_bar_title); - const artistTitleWidth = gr.MeasureString(str.artist, ft.lower_bar_artist, 0, 0, 0, 0).Width + trackNumWidth + gr.MeasureString(showLowerBarComposer ? str.title_lower + str.composer : str.title_lower, ft.lower_bar_title, 0, 0, 0, 0).Width + gr.MeasureString(str.original_artist, ft.lower_bar_title, 0, 0, 0, 0).Width; - const oneLine = artistTitleWidth < availableWidth || !showLowerBarTitle; - const twoLines = artistTitleWidth > availableWidth && showLowerBarTitle; - const lineCorrection = SCALE(pref.customThemeFonts ? RES_4K ? 0 : 4 : RES_4K ? 0 : 2); - - // * Adjustments - const flagWidth = showLowerBarArtistFlags && flagImgs.length && str.tracknum < 100 ? SCALE(14) + SCALE(lowerBarFontSize) : trackNumWidth + SCALE(6); - const heightAdjustment = pref.customThemeFonts ? 0 : ((lowerBarFontSize === 12 || lowerBarFontSize === 14) && !RES_4K || (lowerBarFontSize === 16 || lowerBarFontSize === 18 || lowerBarFontSize === 20 || lowerBarFontSize === 22) && RES_4K) ? 1 : 0; - const trackNumAdjustment = gr.MeasureString('\u2013', ft.lower_bar_title, 0, 0, 0, 0).Width; - const titleAdjustment = gr.MeasureString(pref.customThemeFonts ? '\u2013.' : 'M', ft.lower_bar_title, 0, 0, 0, 0).Width; - const titleAdjustment2 = gr.MeasureString('-', ft.lower_bar_title, 0, 0, 0, 0).Width; - - // * Setup artist, track number and title - const artist = showLowerBarArtist ? str.artist : ''; - const artistX = twoLines ? progressBar.x + availableFlags : Math.round(progressBar.x + availableFlags - (pref.layout === 'default' ? SCALE(1) : 0)); - - const artistY = twoLines ? Math.round(lowerBarTop - lineCorrection - artistHeight + (pref.customThemeFonts ? artistHeight * 0.125 : 0) + (lowerBarFontSize < 18 ? SCALE(-2) : lowerBarFontSize > 18 ? SCALE(RES_QHD ? 1 : 3) : 0)) : - Math.round(lowerBarTop - lineCorrection); - - const trackNum = twoLines ? showLowerBarTrackNum && showLowerBarTitle || !showLowerBarTitle && !fb.IsPlaying ? str.tracknum : '' : - showLowerBarTrackNum && showLowerBarTitle || (!showLowerBarTrackNum || !showLowerBarTitle) && !fb.IsPlaying ? str.tracknum === '' ? '-' : str.tracknum : - !showLowerBarTrackNum && showLowerBarArtist && fb.IsPlaying ? '-' : ''; - - const trackNumX = twoLines ? progressBar.x : - showLowerBarArtist && fb.IsPlaying ? Math.floor(progressBar.x + availableFlags + artistWidth + (!showLowerBarTrackNum || str.tracknum === '' ? titleAdjustment2 * 0.5 : trackNumAdjustment)) : progressBar.x; - - const trackNumY = Math.round(lowerBarTop - lineCorrection - heightAdjustment); - - const title = twoLines ? showLowerBarTitle || !showLowerBarTitle && !fb.IsPlaying ? showLowerBarComposer && fb.IsPlaying ? str.title_lower + str.original_artist + str.composer : str.title_lower : '' : - showLowerBarTitle || !showLowerBarTitle && !fb.IsPlaying ? showLowerBarComposer && fb.IsPlaying ? str.title_lower + str.composer : str.title_lower : ''; - - const titleX = twoLines ? !showLowerBarTrackNum || str.tracknum === '' ? progressBar.x : Math.round(progressBar.x + flagWidth) : - // When not playing or stopped - !fb.IsPlaying ? Math.round(progressBar.x + trackNumWidth) : - // Artist and no track number displayed - showLowerBarArtist ? !showLowerBarTrackNum || str.tracknum === '' ? Math.floor(progressBar.x + availableFlags + artistWidth + titleAdjustment) : - // Artist with track number displayed - Math.round(progressBar.x + availableFlags + artistWidth + trackNumWidth + titleAdjustment) : - // No artist and no track number displayed - !showLowerBarTrackNum || str.tracknum === '' ? progressBar.x : - // No artist with track number displayed - Math.round(progressBar.x + trackNumWidth + titleAdjustment2); - - const titleY = trackNumY; - - // * Apply better anti-aliasing on smaller font sizes in HD res - gr.SetTextRenderingHint(!RES_4K && lowerBarFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); - - // * Artist, tracknum, title - if (oneLine || twoLines && pref.layout === 'default') DrawString(gr, artist, ft.lower_bar_artist, col.lowerBarArtist, artistX, artistY, availableWidth, artistHeight, g_string_format.trim_ellipsis_char); - gr.DrawString(trackNum, ft.lower_bar_title, col.lowerBarTitle, trackNumX, trackNumY, trackNumWidth - timeAreaWidth, titleHeight, StringFormat(0, 0, 4, 0x00001000)); - DrawString(gr, title, ft.lower_bar_title, col.lowerBarTitle, titleX, titleY, fb.IsPlaying ? availableWidth + (twoLines ? availableFlags : 0) : ww, titleHeight, g_string_format.trim_ellipsis_char); - - // * Artist flags - if (showLowerBarArtist && showLowerBarArtistFlags && (pref.layout === 'default' || pref.layout !== 'default' && !twoLines)) { - const maxFlags = Math.min(flagImgs.length, 6); - const marginLeft = SCALE(pref.layout !== 'default' ? 20 : 40); - let flagsLeft = marginLeft - (RES_4K ? 1 : 0); - for (let i = 0; i < maxFlags; i++) { - gr.DrawImage(flagImgs[i], flagsLeft, Math.round(artistY - (flagImgs[i].Height / (artistHeight + SCALE(2))) - (RES_4K ? 1 : 0)), flagImgs[i].Width + SCALE(lowerBarFontSize) - SCALE(26), artistHeight + SCALE(2), 0, 0, flagImgs[i].Width, flagImgs[i].Height); - flagsLeft += flagImgs[i].Width + SCALE(lowerBarFontSize) - SCALE(18); - } - } - - // * Playback time, length, disc number - if (ww > 400) { - gr.SetSmoothingMode(SmoothingMode.AntiAliasGridFit); - let width = gr.CalcTextWidth(` ${str.length}`, ft.lower_bar_length); - const lowerBarVersionW = gr.CalcTextWidth(` ${str.time}`, ft.lower_bar_length); - const lowerBarVersionH = Math.ceil(titleMeasurements.Height); - const lowerBarVersionX = ww - SCALE(pref.layout !== 'default' ? 20 : 40) - lowerBarVersionW; - const lowerBarVersionY = Math.round(lowerBarTop - lineCorrection - heightAdjustment); - const lowerBarLengthX = ww - SCALE(pref.layout !== 'default' ? 20 : 40) - width; - const lowerBarLengthY = lowerBarVersionY; - const lowerBarLengthW = width; - const lowerBarLengthH = lowerBarVersionH; - lowerBarTimeX = ww - SCALE(pref.layout !== 'default' ? 20 : 40) - (isStreaming ? width : width * 2); - lowerBarTimeY = Math.round(lowerBarTop - lineCorrection); - lowerBarTimeW = lowerBarLengthW; - lowerBarTimeH = lowerBarVersionH; - const lowerBarDiscW = gr.CalcTextWidth(` ${str.disc}`, ft.lower_bar_disc); - const lowerBarDiscH = lowerBarVersionH; - const lowerBarDiscY = lowerBarVersionY; - const lowerBarDiscX = lowerBarTimeX - lowerBarDiscW; - - if (showLowerBarPlaybackTime && fb.PlaybackLength > 0) { // * Playing track - gr.DrawString(str.length, ft.lower_bar_length, col.lowerBarLength, lowerBarLengthX, lowerBarLengthY, lowerBarLengthW, lowerBarLengthH, StringFormat(2, 0)); - gr.DrawString(str.time, ft.lower_bar_time, col.lowerBarTime, lowerBarTimeX, lowerBarTimeY, lowerBarTimeW, lowerBarTimeH, StringFormat(2, 0)); - width += gr.CalcTextWidth(` ${str.time}`, ft.lower_bar_time); - gr.DrawString(pref.layout !== 'default' ? '' : str.disc, ft.lower_bar_disc, col.lowerBarTitle, lowerBarDiscX, lowerBarDiscY, lowerBarDiscW, lowerBarDiscH, StringFormat(2, 0)); - } - else if (showLowerBarPlaybackTime && fb.IsPlaying) { // * Streaming, but still want to show time - gr.DrawString(str.time, ft.lower_bar_time, col.lowerBarTitle, lowerBarTimeX, lowerBarTimeY, lowerBarTimeW, lowerBarTimeH, StringFormat(2, 0)); - } - else { // * Not playing anything, will show theme version or update link if available - let offset = 0; - if (updateAvailable && updateHyperlink) { - offset = updateHyperlink.getWidth(); - updateHyperlink.setContainerWidth(ww); - updateHyperlink.set_y(lowerBarTop); - updateHyperlink.set_xOffset(ww - offset - SCALE(pref.layout !== 'default' ? 20 : 40)); - updateHyperlink.draw(gr, col.lowerBarTitle); - } - if (showLowerBarPlaybackTime) { - gr.DrawString(str.time, ft.lower_bar_length, col.lowerBarTitle, lowerBarVersionX - offset, lowerBarVersionY, lowerBarVersionW, lowerBarVersionH, StringFormat(2, 0)); - } - } - if (showLowerBarPlaybackTime && fb.IsPlaying) { // * Switch to playback time remaining - btns.playbackTime = new Button(ww - timeAreaWidth - SCALE(pref.layout !== 'default' ? 20 : 40), lowerBarTimeY, - timeAreaWidth, lowerBarTimeH, showLowerBarPlaybackTime ? 'PlaybackTime' : '', '', showLowerBarPlaybackTime ? 'Switch playback time' : ''); - } - } - - // * LOWER BAR TOOLTIP * // - if ((pref.showTooltipMain || pref.showTooltipTruncated) && str.lowerBar_tt && fb.IsPlaying) { - str.lowerBar_tt.draw(gr); - } - - // * VOLUME BTN * // - if (showVolumeBtn && loadingThemeComplete) { - volumeBtn.draw(gr); - } - - // * PROGRESS BAR * // - if (showProgressBar && (pref.seekbar === 'progressbar' || !fb.IsPlaying)) { - progressBarY = Math.round(lowerBarTop + titleMeasurements.Height + progressBar.h); - progressBar.setY(progressBarY); - progressBar.draw(gr); - } - // * WAVEFORM BAR * // - else if (showWaveformBar && pref.seekbar === 'waveformbar') { - waveformBarY = Math.round(lowerBarTop + titleMeasurements.Height + waveformBar.h); - waveformBar.setY(waveformBarY); - waveformBar.draw(gr); - } - // * PEAKMETER BAR * // - else if (showPeakmeterBar && pref.seekbar === 'peakmeterbar') { - peakmeterBarY = Math.round(lowerBarTop + titleMeasurements.Height + peakmeterBar.h * 0.5); - peakmeterBar.setY(peakmeterBarY); - peakmeterBar.draw(gr); - } - - if (drawLowerBarProfiler) drawLowerBarProfiler.Print(); -} + /** + * Draws the debug theme overlay in the album art area when `Enable debug theme overlay` in Developer tools is active. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDebugThemeOverlay(gr) { + if (!grCfg.settings.showDebugThemeOverlay) return; + + const fullW = grSet.layout === 'default' && grSet.lyricsLayout === 'full' && this.displayLyrics && this.noAlbumArtStub || grSet.layout === 'artwork'; + const titleWidth = this.albumArtSize.w - SCALE(80); + const titleHeight = gr.CalcTextHeight(' ', grFont.popup); + const lineSpacing = titleHeight * 1.5; + const logColor = RGB(255, 255, 255); + const x = this.albumArtSize.x + SCALE(grSet.layout !== 'default' ? 20 : 40); + let y = this.albumArtSize.y; + + const createBlock = (obj) => Object.keys(obj).find(key => obj[key]) || ''; + + const tsBlock0 = createBlock({ + 'Nighttime,': grSet.styleNighttime + }); + + const tsBlock1 = createBlock({ + 'Bevel,': grSet.styleBevel + }); + + const tsBlock2 = createBlock({ + 'Blend,': grSet.styleBlend, + 'Blend 2,': grSet.styleBlend2, + 'Gradient,': grSet.styleGradient, + 'Gradient 2,': grSet.styleGradient2 + }); + + const tsBlock3 = createBlock({ + 'Alternative ': grSet.styleAlternative, + 'Alternative 2': grSet.styleAlternative2, + 'Black and white': grSet.styleBlackAndWhite, + 'Black and white 2': grSet.styleBlackAndWhite2, + 'Black and white reborn': grSet.styleBlackAndWhiteReborn, + 'Black reborn': grSet.styleBlackReborn, + 'Reborn white': grSet.styleRebornWhite, + 'Reborn black': grSet.styleRebornBlack, + 'Reborn fusion': grSet.styleRebornFusion, + 'Reborn fusion 2': grSet.styleRebornFusion2, + 'Reborn fusion accent': grSet.styleRebornFusionAccent, + 'Random pastel': grSet.styleRandomPastel, + 'Random dark': grSet.styleRandomDark + }); + + const tsTopMenuButtons = grSet.styleTopMenuButtons !== 'default' ? CapitalizeString(`${grSet.styleTopMenuButtons}`) : ''; + const tsTransportButtons = grSet.styleTransportButtons !== 'default' ? CapitalizeString(`${grSet.styleTransportButtons}`) : ''; + const tsProgressBar1 = grSet.styleProgressBarDesign === 'rounded' ? 'Rounded,' : ''; + const tsProgressBar2 = grSet.styleProgressBar !== 'default' ? `Bg: ${CapitalizeString(`${grSet.styleProgressBar},`)}` : ''; + const tsProgressBar3 = grSet.styleProgressBarFill !== 'default' ? `Fill: ${CapitalizeString(`${grSet.styleProgressBarFill}`)}` : ''; + const tsVolumeBar1 = grSet.styleVolumeBarDesign === 'rounded' ? 'Rounded,' : ''; + const tsVolumeBar2 = grSet.styleVolumeBar !== 'default' ? `Bg: ${CapitalizeString(`${grSet.styleVolumeBar},`)}` : ''; + const tsVolumeBar3 = grSet.styleVolumeBarFill !== 'default' ? `Fill: ${CapitalizeString(`${grSet.styleVolumeBarFill}`)}` : ''; + + const propertiesLog = [ + { prop: this.selectedPrimaryColor, log: `Primary color: ${this.selectedPrimaryColor}` }, + { prop: this.selectedPrimaryColor2, log: `Primary 2 color: ${this.selectedPrimaryColor2}` }, + { prop: grCol.colBrightness, log: `Primary color brightness: ${grCol.colBrightness}` }, + { prop: grCol.colBrightness2, log: `Primary 2 color brightness: ${grCol.colBrightness2}` }, + { prop: grCol.imgBrightness, log: `Image brightness: ${grCol.imgBrightness}` }, + { prop: grSet.styleBlend || grSet.styleBlend2, log: `Image blur: ${this.blendedImgBlur}` }, + { prop: grSet.styleBlend || grSet.styleBlend2, log: `Image alpha: ${this.blendedImgAlpha}` }, + { prop: grSet.preset, log: `Theme preset: ${grSet.preset}` }, + { prop: grSet.themeBrightness !== 'default', log: `Theme brightness: ${grSet.themeBrightness}%` }, + { prop: tsBlock0 || tsBlock1 || tsBlock2 || tsBlock3, log: `Styles: ${tsBlock0} ${tsBlock1} ${tsBlock2} ${tsBlock3}` }, + { prop: tsTopMenuButtons, log: `Top menu button style: ${tsTopMenuButtons}` }, + { prop: tsTransportButtons, log: `Transport button style: ${tsTransportButtons}` }, + { prop: tsProgressBar1 || tsProgressBar2 || tsProgressBar3, log: tsProgressBar1 || tsProgressBar2 || tsProgressBar3 ? `Progressbar styles: ${tsProgressBar1} ${tsProgressBar2} ${tsProgressBar3}` : '' }, + { prop: tsVolumeBar1 || tsVolumeBar2 || tsVolumeBar3, log: `Volumebar styles: ${tsVolumeBar1} ${tsVolumeBar2} ${tsVolumeBar3}` } + ]; + gr.SetSmoothingMode(SmoothingMode.None); + gr.FillSolidRect(fullW ? 0 : this.albumArtSize.x, fullW ? this.topMenuHeight : this.albumArtSize.y, fullW ? this.ww : this.albumArtSize.w, fullW ? this.wh - this.topMenuHeight - this.lowerBarHeight : this.albumArtSize.h, RGBA(0, 0, 0, 180)); + gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); -/** - * Draws styled tooltips that will make standard tooltips look fancy. - * @param {GdiGraphics} gr - */ -function drawStyledTooltips(gr) { - if (styledTooltipText === '' || !styledTooltipReady || !pref.showStyledTooltips) return; - - const drawStyledTooltipsProfiler = timings.showExtraDrawTiming && fb.CreateProfiler('on_paint -> styled tooltips'); - const tooltipFontSize = pref[`tooltipFontSize_${pref.layout}`]; - const offset = SCALE(30); - const padding = SCALE(15); - const edgeSpace = padding * 0.5; - const arc = SCALE(6); - const w = Math.min(gr.MeasureString(styledTooltipText, ft.tooltip, 0, 0, 0, 0).Width + padding + 1, ww - (state.mouse_x > ww * 0.85 ? state.mouse_x - ww * 0.15 : state.mouse_x) - edgeSpace); - const h = Math.min(gr.MeasureString(styledTooltipText, ft.tooltip, 0, 0, w, wh).Height + padding, wh - (state.mouse_y > wh * 0.85 ? state.mouse_y - wh * 0.15 : state.mouse_y) - edgeSpace - offset); - const x = state.mouse_x > ww * 0.85 ? state.mouse_x - w : state.mouse_x; // * When tooltip is too close to the right edge, it will be drawn on the left side of the mouse cursor - const y = state.mouse_y > wh * 0.85 ? state.mouse_y - h : state.mouse_y + offset; // * When tooltip is too close to the bottom edge, it will be drawn on the top side of the mouse cursor - const throttleRepaintRect = _Throttle((x, y, w, h, force = false) => window.RepaintRect(x, y, w, h, force), 50); - - // * Apply better anti-aliasing on smaller font sizes in HD res - gr.SetTextRenderingHint(!RES_4K && tooltipFontSize < 18 ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAliasGridFit); - gr.FillRoundRect(x, y, w, h, arc, arc, RGBtoRGBA(col.popupBg, 220)); - gr.DrawRoundRect(x, y, w, h, arc, arc, SCALE(2), 0x64000000); - gr.DrawString(styledTooltipText, ft.tooltip, col.popupText, x + padding * 0.5, y + padding * 0.5, w - padding, h - padding, StringFormat(0, 0, 4)); - throttleRepaintRect(x - offset * 0.5, y - offset * 0.5, w + offset, h + offset); - - if (drawStyledTooltipsProfiler) drawStyledTooltipsProfiler.Print(); -} + const drawString = (str) => { + y += lineSpacing; + gr.DrawString(str, grFont.popup, logColor, x, y, titleWidth, titleHeight, StringFormat(0, 0, 4)); + }; + for (const { prop, log } of propertiesLog) { + if (prop) drawString(log); + if (prop === grCol.imgBrightness) y += lineSpacing; + } + } -/** - * Draws the startup background until everything in the theme is loaded. - * Pseudo delay background logo mask when loading the theme, otherwise it will show ugly repaints when initializing since smp v1.6.1. - * @param {GdiGraphics} gr - */ -function drawStartupBackground(gr) { - if (loadingThemeComplete) return; - gr.FillSolidRect(0, 0, ww, wh, col.loadingThemeBg); - if (pref.showPreloaderLogo) drawLogo(gr); -} + /** + * Draws the draw timing in the console when `Show extra draw timing` in Developer tools is active. + * @param {Date} drawTimingStart - The start time of the operation. + */ + drawDebugTiming(drawTimingStart) { + if (!drawTimingStart) return; + + const drawTimingEnd = new Date(); + const duration = drawTimingEnd - drawTimingStart; + const hours = String(drawTimingStart.getHours()).padStart(2, '0'); + const minutes = String(drawTimingStart.getMinutes()).padStart(2, '0'); + const seconds = String(drawTimingStart.getSeconds()).padStart(2, '0'); + const milliseconds = String(drawTimingStart.getMilliseconds()).padStart(3, '0'); + const time = `${hours}:${minutes}:${seconds}.${milliseconds}`; + const repaintRectCalls = this.repaintRectCount > 1 ? ` - ${this.repaintRectCount} repaintRect calls` : ''; + + console.log(`${time}: on_paint total: ${duration}ms${repaintRectCalls}`); + } + + /** + * Draws red rectangles for debugging to show all painted areas in all panels when `Show draw areas` in Developer tools is active. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + drawDebugRectAreas(gr) { + if (!this.repaintRects.length) return; + + try { + for (const rect of this.repaintRects) { + gr.DrawRect(rect.x, rect.y, rect.w, rect.h, SCALE(2), RGBA(255, 0, 0, 200)); + } + this.repaintRects = []; + } catch (e) {} + } + // #endregion + + // * MAIN - PUBLIC METHODS - INITIALIZATION * // + // #region PUBLIC METHODS - INITIALIZATION + /** + * Initializes all needed fonts for the theme and displays error messages if fonts are missing. + * @returns {boolean} True if all fonts are installed, otherwise false. + */ + initFonts() { + /** @type {string[]} */ + const fontList = [ + grFont.fontDefault, grFont.fontSegoeUISymbol, grFont.fontTopMenu, grFont.fontTopMenuCaption, + grFont.fontGuiFx, grFont.fontAwesome, grFont.fontLowerBarArtist, grFont.fontLowerBarTitle, grFont.fontLowerBarDisc, grFont.fontLowerBarTime, grFont.fontLowerBarLength, grFont.fontLowerBarWave, + grFont.fontNotification, grFont.fontPopup, grFont.fontTooltip, + grFont.fontGridArtist, grFont.fontGridTitle, grFont.fontGridTitleBold, grFont.fontGridAlbum, grFont.fontGridKey, grFont.fontGridValue, + grFont.fontLibrary, grFont.fontBiography, grFont.fontLyrics + ]; + + /** @type {boolean} The state of installed fonts on system, will return false if one is missing. */ + const fontsInstalled = fontList.every((fontName) => TestFont(fontName)); + + if (!fontsInstalled) { + fb.ShowPopupMessage('Georgia-ReBORN WAS UNABLE TO LOAD SOME FONTS\n\n' + + 'Be sure all fonts from\nfoobar2000\\profile\\georgia-reborn\\fonts\nare correctly installed in these directories:\n\n' + + 'For Windows: C:\\Windows\\Fonts\\\nFor Linux: /usr/share/fonts or ~/.local/share/fonts\n\n' + + 'If you use custom fonts, all your custom fonts need to have\nthe exact font name / font family name in your\n' + + 'foobar\\profile\\georgia-reborn\\configs\\georgia-reborn-config.jsonc config file.\n\n' + + 'You can also check foobar\'s console ( Top menu > View > Console ),\nit will show font errors with its wrong font names.', 'FONT ERROR WARNING'); + } + + if (grSet.customThemeFonts) { + console.log('\nUser\'s set custom fonts are being used:\n\n' + + `Panel default: ${grCfg.customFont.fontDefault}\n` + + `Top menu: ${grCfg.customFont.fontTopMenu}\n` + + `Lower bar artist: ${grCfg.customFont.fontLowerBarArtist}\n` + + `Lower bar title: ${grCfg.customFont.fontLowerBarTitle}\n` + + `Lower bar disc: ${grCfg.customFont.fontLowerBarDisc}\n` + + `Lower bar time: ${grCfg.customFont.fontLowerBarTime}\n` + + `Lower bar length: ${grCfg.customFont.fontLowerBarLength}\n` + + `Lower bar waveform bar: ${grCfg.customFont.fontLowerBarWave}\n` + + `Notification: ${grCfg.customFont.fontNotification}\n` + + `Popup: ${grCfg.customFont.fontPopup}\n` + + `Tooltip: ${grCfg.customFont.fontTooltip}\n` + + `Grid artist: ${grCfg.customFont.fontGridArtist}\n` + + `Grid title: ${grCfg.customFont.fontGridTitle}\n` + + `Grid title bold: ${grCfg.customFont.fontGridTitleBold}\n` + + `Grid album: ${grCfg.customFont.fontGridAlbum}\n` + + `Grid key: ${grCfg.customFont.fontGridKey}\n` + + `Grid value: ${grCfg.customFont.fontGridValue}\n` + + `Playlist artist normal: ${grCfg.customFont.playlistArtistNormal}\n` + + `Playlist artist playing: ${grCfg.customFont.playlistArtistPlaying}\n` + + `Playlist artist normal compact: ${grCfg.customFont.playlistArtistNormalCompact}\n` + + `Playlist artist playing compact: ${grCfg.customFont.playlistArtistPlayingCompact}\n` + + `Playlist title normal: ${grCfg.customFont.playlistTitleNormal}\n` + + `Playlist title selected: ${grCfg.customFont.playlistTitleSelected}\n` + + `Playlist title playing: ${grCfg.customFont.playlistTitlePlaying}\n` + + `Playlist album: ${grCfg.customFont.playlistAlbum}\n` + + `Playlist date: ${grCfg.customFont.playlistDate}\n` + + `Playlist date compact: ${grCfg.customFont.playlistDateCompact}\n` + + `Playlist info: ${grCfg.customFont.playlistInfo}\n` + + `Playlist cover: ${grCfg.customFont.playlistCover}\n` + + `Playlist playcount: ${grCfg.customFont.playlistPlaycount}\n` + + `Library: ${grCfg.customFont.fontLibrary}\n` + + `Biography: ${grCfg.customFont.fontBiography}\n` + + `Lyrics: ${grCfg.customFont.fontLyrics}\n\n` + ); + } + + return fontsInstalled; + } + + /** + * Initializes and sets the sizes and positions for various UI elements. + */ + initMetrics() { + this.pauseSize = SCALE(100); + this.discArtShadow = SCALE(6); + this.gridTooltipHeight = SCALE(100); + this.timelineHeight = Math.round(this.progressBarH * 0.66); + this.topMenuHeight = SCALE(40); + this.lowerBarHeight = SCALE(120); + this.progressBarH = SCALE(grSet.layout !== 'default' && (grSet.styleProgressBarDesign === 'default' || grSet.styleProgressBarDesign === 'rounded') ? 10 : 12) + (this.ww > 1920 ? 2 : 0); + this.peakmeterBarH = SCALE(grSet.layout !== 'default' ? 16 : 26) + (this.ww > 1920 ? 2 : 0); + this.waveformBarH = SCALE(grSet.layout !== 'default' ? 16 : 26) + (this.ww > 1920 ? 2 : 0); + } + + /** + * Initializes the theme on startup or reload. + */ + initMain() { + // * Init variables + console.log('initMain()'); + this.loadingTheme = true; + this.clearUIVariables(); + this.ww = window.Width; + this.wh = window.Height; + + // * Layout safety check + if (!['default', 'artwork', 'compact'].includes(grSet.layout)) { + window.SetProperty('Georgia-ReBORN - 05. Layout', 'default'); + grSet.layout = 'default'; + grm.display.layoutDefault(); + } + + // * Do auto-delete cache if enabled + if (grSet.libraryAutoDelete) DeleteLibraryCache(); + if (grSet.biographyAutoDelete) DeleteBiographyCache(); + if (grSet.lyricsAutoDelete) DeleteLyrics(); + if (grSet.waveformBarAutoDelete) DeleteWaveformBarCache(); + + this.lastAlbumFolder = ''; + this.lastPlaybackOrder = fb.PlaybackOrder; + this.displayPanelControl(); + grm.color.setThemeColors(); + this.themeColorSet = true; + + if (grSet.asyncThemePreloader) { + on_size(); // Needed when loading async, otherwise just needed in fb.IsPlaying conditional + } + + this.initMetrics(); + + if (fb.IsPlaying && fb.GetNowPlaying()) { + on_playback_new_track(fb.GetNowPlaying()); + } + + // * Workaround so we can use the Edit menu or run fb.RunMainMenuCommand("Edit/Something...") + // * when the panel has focus and a dedicated playlist viewer doesn't. + plman.SetActivePlaylistContext(); // Once on startup + + // * Init panels + if (!lib.initialized) { + this.initLibraryPanel(); + this.setLibrarySize(); + setTimeout(() => { + lib.lib.initialise(); + this.initLibraryLayout(); + }, 1); + } + if (!bio.initialized) { + this.initBiographyPanel(); + this.setBiographySize(); + } + if (grSet.panelWidthAuto) { + this.initPanelWidthAuto(); + } + + if (grSet.lyricsRememberPanelState) { + this.displayLyrics = grSet.lyricsPanelState; + } + else if (this.displayLyrics && grSet.lyricsLayout === 'full') { + this.displayPlaylist = !this.displayPlaylist; + this.resizeArtwork(true); + } + if (this.displayLyrics) { + this.displayLyricsOnStart(); + } + + // * Init colors + if (grSet.theme === 'random' && grSet.randomThemeAutoColor !== 'off') { + grm.color.getRandomThemeAutoColor(); + } + + this.initThemeFull = true; + if (grSet.theme.startsWith('custom')) this.initCustomTheme(); + this.initTheme(); + DebugLog('\n>>> initTheme -> initMain <<<\n'); + this.loadingTheme = false; + + // * Restore backup workaround to successfully restore playlist files after foobar installation + if (grSet.restoreBackupPlaylist) { + setTimeout(() => { + RestoreBackupPlaylist(); + }, !this.loadingTheme); + } + + // * Hide loading screen + setTimeout(() => { + this.loadingThemeComplete = true; + window.Repaint(); + }, 100); + } + + /** + * Initializes size and position when noAlbumArtStub is being displayed. + */ + initNoAlbumArtSize() { + const noAlbumArtSize = this.wh - this.topMenuHeight - this.lowerBarHeight; + + this.albumArtSize.x = + grSet.layout === 'default' && this.displayCustomThemeMenu && this.displayDetails ? this.ww * 0.3 : + grSet.layout === 'default' && !this.displayCustomThemeMenu && this.displayDetails || + grSet.layout === 'artwork' && this.displayPlaylist ? this.ww : + grSet.panelWidthAuto ? + grSet.albumArtAlign === 'left' ? 0 : + grSet.albumArtAlign === 'leftMargin' ? this.ww / this.wh > 1.8 ? SCALE(40) : 0 : + grSet.albumArtAlign === 'center' ? Math.floor(this.ww * 0.25 - noAlbumArtSize * 0.5) : + Math.floor(this.ww * 0.5 - noAlbumArtSize) : + 0; + + this.albumArtSize.y = this.topMenuHeight; + + this.albumArtSize.w = + grSet.panelWidthAuto && this.noAlbumArtStub ? !fb.IsPlaying ? 0 : noAlbumArtSize : + this.ww * 0.5; + + this.albumArtSize.h = noAlbumArtSize; + } + + /** + * Initializes everything necessary in all panels without the need of a reload. + */ + initPanels() { + // * Update Main + this.createFonts(); + this.initMetrics(); + grm.timeline = new Timeline(this.timelineHeight); + grm.gridTip = new MetadataGridTooltip(this.gridTooltipHeight); + grm.lowerTip = new LowerBarTooltip(); + grm.jSearch = new JumpSearch(this.ww, this.wh); + grm.volBtn = new VolumeButton(); + grm.progBar = new ProgressBar(this.ww, this.wh); + grm.peakBar = new PeakmeterBar(this.ww, this.wh); + grm.peakBar.on_size(this.ww, this.wh); + grm.waveBar = new WaveformBar(this.ww, this.wh); + grm.waveBar.updateBar(); + this.createButtonImages(); + this.createButtonObjects(this.ww, this.wh); + this.resizeArtwork(true); + grm.button.initButtonState(); + + if (fb.GetNowPlaying()) on_metadb_changed(); // Refresh panel + + setTimeout(() => { + // * Update Playlist + PlaylistRescale(true); + this.initPlaylist(); + pl.call.on_size(this.ww, this.wh); + + // * Update Library + this.setLibrarySize(); + lib.panel.tree.y = lib.panel.search.h; + lib.pop.createImages(); + lib.panel.zoomReset(); + this.initLibraryLayout(); + + // * Update Biography + this.setBiographySize(); + bio.ui.setSbar(); + bio.but.createImages(); + bio.but.resetZoom(); + grm.theme.initBiographyColors(); + this.initBiographyLayout(); + }, this.loadingThemeComplete); + } + + /** + * Initializes size and position of the current panel when using pref.panelWidthAuto. + */ + initPanelWidthAuto() { + this.resizeArtwork(true); + + if (this.displayLibrarySplit() || this.displayLibrary && grSet.libraryLayout === 'full') return; + + if (this.displayPlaylist && (this.noAlbumArtStub || pl.playlist.x !== this.albumArtSize.x + this.albumArtSize.w)) { + DebugLog('initPanelWidthAuto -> Playlist'); + pl.call.on_size(this.ww, this.wh); + } + if (this.displayLibrary && (this.noAlbumArtStub || lib.ui.x !== this.albumArtSize.x + this.albumArtSize.w)) { + DebugLog('initPanelWidthAuto -> Library'); + this.setLibrarySize(); + } + if (this.displayBiography && (this.noAlbumArtStub || bio.ui.x + bio.ui.w !== this.albumArtSize.x + this.albumArtSize.w)) { + DebugLog('initPanelWidthAuto -> Biography'); + this.setBiographySize(); + } + } + + /** + * Initializes the theme when updating colors. + */ + initTheme() { + const themeProfiler = this.showDebugTiming && fb.CreateProfiler('initTheme'); + + const fullInit = + this.initThemeFull || grSet.themeBrightness !== 'default' + || + libSet.theme !== 0 || bioSet.theme !== 0 + || + grSet.theme === 'reborn' || grSet.theme === 'random' + || + grSet.styleBlackAndWhiteReborn || grSet.styleBlackReborn; + + // * INIT THEME PREFERENCES VALUES * // + grm.theme.initThemePrefVals(); + grm.style.initThemePrefVals(); + + // * SETUP COLORS * // + grm.color.setImageBrightness(); + if (grSet.styleBlackAndWhiteReborn) { + grm.style.initBlackAndWhiteReborn(); + } + if (grSet.theme === 'random' && !this.isStreaming && !this.isPlayingCD) { + grm.color.getRandomThemeColor(); + } + if (this.noAlbumArtStub || this.isStreaming || this.isPlayingCD) { + grm.color.setNoAlbumArtColors(); + } + if ((grSet.styleBlend || grSet.styleBlend2 || grSet.styleProgressBarFill === 'blend') && this.albumArt) { + grm.color.setStyleBlend(); + } + if (grSet.themeDayNightMode && !grSet.themeSetupDay && !grSet.themeSetupNight) { + this.initThemeDayNightState(); + } + grm.color.setBackgroundColorDefinition(); + + // * INIT COLORS * // + grm.theme.initPlaylistColors(); + grm.theme.initLibraryColors(); + grm.theme.initBiographyColors(); + grm.theme.initMainColors(); + grm.theme.initChronflowColors(); + grm.style.initStyleColors(); + + // * POST-INIT COLOR ADJUSTMENTS * // + grm.theme.themeColorAdjustments(); + if (!fullInit) { + return; + } + if (grSet.themeBrightness !== 'default') { + grm.color.adjustThemeBrightness(grSet.themeBrightness); + } + if (grSet.playlistRowHover) { + pl.playlist.title_color_change(); + } + if (libImg.labels.overlayDark) { + lib.ui.getItemColours(); + } + bio.txt.artCalc(); + bio.txt.albCalc(); + + // * UPDATE BUTTONS * // + pl.playlist.initScrollbar(); + lib.sbar.setCol(); + lib.pop.createImages(); + lib.but.createImages(); + lib.but.refresh(true); + bio.alb_scrollbar.setCol(); + bio.art_scrollbar.setCol(); + bio.but.createImages('all'); + bio.img.createImages(); + this.createButtonImages(); + this.createButtonObjects(this.ww, this.wh); + grm.button.initButtonState(); + + // * REFRESH * // + window.Repaint(); + + if (themeProfiler) themeProfiler.Print(); + } + + /** + * Initializes the theme day and night state. + * - Aborts if `pref.themeDayNightMode` is falsy or if any custom GR theme tags are detected. + * - Restores the day or night theme based on `pref.themeDayNightTime` if the current theme does not match the expected day or night theme. + * - Sets an interval to check and update the theme every 10 minutes based on the time of day. + */ + initThemeDayNightState() { + const customTheme = $('[%GR_THEME%]'); + const customStyle = $('[%GR_STYLE%]'); + const customPreset = $('[%GR_PRESET%]'); + + if (!grSet.themeDayNightMode || customTheme || customStyle || customPreset) { + return; + } + + // * Restore day or night theme after custom GR theme tags usage + if (grSet.theme !== grSet.theme_day && grSet.themeDayNightTime === 'day' || + grSet.theme !== grSet.theme_night && grSet.themeDayNightTime === 'night') { + this.resetTheme(); + initThemeDayNightMode(new Date()); + this.initThemeFull = true; + return; + } + + // * Check every 10 minutes if it is day or night for the entire play session + if (!this.themeDayNightModeTimer) { + this.themeDayNightModeTimer = setInterval(() => { + initThemeDayNightMode(new Date()); + this.initThemeFull = true; + this.initTheme(); + DebugLog('\n>>> initTheme -> fetchNewArtwork -> on_playback_new_track -> themeDayNightModeTimer <<<\n'); + }, 600000); + } + } + + /** + * Initializes %GR_THEME%, %GR_STYLE%, %GR_PRESET% tags in music files and sets them, used in on_playback_new_track. + */ + initThemeTags() { + this.themePresetIndicator = false; + const customTheme = $('[%GR_THEME%]'); + const customStyle = $('[%GR_STYLE%]'); + const customPreset = $('[%GR_PRESET%]'); + + const themeStyles = { + 'nighttime' : () => { grSet.styleNighttime = true; }, + 'bevel' : () => { grSet.styleBevel = true; }, + 'blend' : () => { grSet.styleBlend = true; }, + 'blend2' : () => { grSet.styleBlend2 = true; }, + 'gradient' : () => { grSet.styleGradient = true; }, + 'gradient2' : () => { grSet.styleGradient2 = true; }, + 'alternative' : () => { grSet.styleAlternative = true; }, + 'alternative2' : () => { grSet.styleAlternative2 = true; }, + 'blackAndWhite' : () => { grSet.styleBlackAndWhite = true; }, + 'blackAndWhite2' : () => { grSet.styleBlackAndWhite2 = true; }, + 'blackReborn' : () => { grSet.styleBlackReborn = true; }, + 'rebornWhite' : () => { grSet.styleRebornWhite = true; }, + 'rebornBlack' : () => { grSet.styleRebornBlack = true; }, + 'rebornFusion' : () => { grSet.styleRebornFusion = true; }, + 'rebornFusion2' : () => { grSet.styleRebornFusion2 = true; }, + 'randomPastel' : () => { grSet.styleRandomPastel = true; }, + 'randomDark' : () => { grSet.styleRandomDark = true; }, + 'rebornFusionAccent' : () => { grSet.styleRebornFusionAccent = true; }, + 'topMenuButtons=filled' : () => { grSet.styleTopMenuButtons = 'filled'; }, + 'topMenuButtons=bevel' : () => { grSet.styleTopMenuButtons = 'bevel'; }, + 'topMenuButtons=inner' : () => { grSet.styleTopMenuButtons = 'inner'; }, + 'topMenuButtons=emboss' : () => { grSet.styleTopMenuButtons = 'emboss'; }, + 'topMenuButtons=minimal' : () => { grSet.styleTopMenuButtons = 'minimal'; }, + 'transportButtons=bevel' : () => { grSet.styleTransportButtons = 'bevel'; }, + 'transportButtons=inner' : () => { grSet.styleTransportButtons = 'inner'; }, + 'transportButtons=emboss' : () => { grSet.styleTransportButtons = 'emboss'; }, + 'transportButtons=minimal' : () => { grSet.styleTransportButtons = 'minimal'; }, + 'progressBarDesign=rounded' : () => { grSet.styleProgressBarDesign = 'rounded'; }, + 'progressBarDesign=lines' : () => { grSet.styleProgressBarDesign = 'lines'; }, + 'progressBarDesign=blocks' : () => { grSet.styleProgressBarDesign = 'blocks'; }, + 'progressBarDesign=dots' : () => { grSet.styleProgressBarDesign = 'dots'; }, + 'progressBarDesign=thin' : () => { grSet.styleProgressBarDesign = 'thin'; }, + 'progressBarBg=bevel' : () => { grSet.styleProgressBar = 'bevel'; }, + 'progressBarBg=inner' : () => { grSet.styleProgressBar = 'inner'; }, + 'progressBarFill=bevel' : () => { grSet.styleProgressBarFill = 'bevel'; }, + 'progressBarFill=inner' : () => { grSet.styleProgressBarFill = 'inner'; }, + 'progressBarFill=blend' : () => { grSet.styleProgressBarFill = 'blend'; }, + 'volumeBarDesign=rounded' : () => { grSet.styleVolumeBarDesign = 'rounded'; }, + 'volumeBarBg=bevel' : () => { grSet.styleVolumeBar = 'bevel'; }, + 'volumeBarBg=inner' : () => { grSet.styleVolumeBar = 'inner'; }, + 'volumeBarFill=bevel' : () => { grSet.styleVolumeBarFill = 'bevel'; }, + 'volumeBarFill=inner' : () => { grSet.styleVolumeBarFill = 'bevel'; } + }; + + // * Restore last theme state + if (grSet.presetSelectMode === 'default' && this.themeRestoreState) { + DebugLog('\n>>> initThemeTags restore <<<\n'); + this.resetStyle('all'); + this.resetTheme(); + this.restoreThemeStylePreset(); // * Retore saved pref settings + if (grSet.savedPreset !== false) grm.preset.setThemePreset(grSet.savedPreset); + if (grSet.theme.startsWith('custom')) this.initCustomTheme(); + this.initStyleState(); + this.themeRestoreState = false; + } + + // * Skip also restore on next call + if (grSet.theme === grSet.savedTheme && !customTheme && !customStyle && !customPreset) { + DebugLog('\n>>> initThemeTags skipped <<<\n'); + this.restoreThemeStylePreset(true); // * Reset saved pref settings + this.themeRestoreState = false; + return; + } + + // * 1. Set preset + if (customPreset.length) { + DebugLog('\n>>> initThemeTags -> %GR_PRESET% loaded <<<'); + grSet.preset = customPreset; + grm.preset.setThemePreset(customPreset); + this.themeRestoreState = true; + } + // * 2. Set theme + else if (customTheme.length) { + DebugLog('\n>>> initThemeTags -> %GR_THEME% loaded <<<'); + grSet.theme = customTheme; + this.resetTheme(); + this.themeRestoreState = true; + } + // * 3. Set styles + if (customStyle.length && !customPreset.length) { + DebugLog('\n>>> initThemeTags -> %GR_STYLE% loaded <<<'); + this.resetStyle('all'); + for (const style of customStyle.split(/(?:,|;| )+/)) { + const setStyle = themeStyles[style]; + if (setStyle) setStyle(); + } + this.themeRestoreState = true; + } + + // * 4. Update theme + if (!customPreset.length) { // Prevent double initialization for theme presets to save performance, grMain.ui.updateStyle() already handled in setThemePreset() + this.updateStyle(); + } + } + + /** + * Initializes the custom themes to check if any are currently active. + */ + initCustomTheme() { + const customThemes = { + custom01: grCfg.customTheme01, + custom02: grCfg.customTheme02, + custom03: grCfg.customTheme03, + custom04: grCfg.customTheme04, + custom05: grCfg.customTheme05, + custom06: grCfg.customTheme06, + custom07: grCfg.customTheme07, + custom08: grCfg.customTheme08, + custom09: grCfg.customTheme09, + custom10: grCfg.customTheme10 + }; + + if (grSet.theme in customThemes) { + grCfg.cTheme = customThemes[grSet.theme]; + } + } + + /** + * Initializes styles to check if any are currently active, used in top menu Options > Style. + */ + initStyleState() { + const styles = [ + grSet.styleNighttime, + grSet.styleBevel, + grSet.styleBlend, + grSet.styleBlend2, + grSet.styleGradient, + grSet.styleGradient2, + grSet.styleAlternative, + grSet.styleAlternative2, + grSet.styleBlackAndWhite, + grSet.styleBlackAndWhite2, + grSet.styleBlackAndWhiteReborn, + grSet.styleBlackReborn, + grSet.styleRebornWhite, + grSet.styleRebornBlack, + grSet.styleRebornFusion, + grSet.styleRebornFusion2, + grSet.styleRebornFusionAccent, + grSet.styleRandomPastel, + grSet.styleRandomDark, + grSet.styleTopMenuButtons !== 'default', + grSet.styleTransportButtons !== 'default', + grSet.styleProgressBarDesign !== 'default', + grSet.styleProgressBar !== 'default', + grSet.styleProgressBarFill !== 'default', + grSet.styleVolumeBarDesign !== 'default', + grSet.styleVolumeBar !== 'default', + grSet.styleVolumeBarFill !== 'default' + ]; + + grSet.styleDefault = !styles.some(style => style); + } + + /** + * Sets the theme to a factory reset state, used on the very first foobar start after installation or when resetting the theme. + */ + async systemFirstLaunch() { + if (!grSet.systemFirstLaunch) return; + + await this.initMain(); + await grm.settings.setThemeSettings(false, false, true); + await this.initMain(); + await grm.display.autoDetectRes(); + + grSet.systemFirstLaunch = false; + } + // #endregion + + // * MAIN - PUBLIC METHODS - COMMON * // + // #region MAIN - PUBLIC METHODS - COMMON + /** + * Clears all now playing related UI strings. + */ + clearUIVariables() { + const showLowerBarVersion = grSet[`showLowerBarVersion_${grSet.layout}`]; + grStr.artist = ''; + grStr.tracknum = $(showLowerBarVersion ? grSet.layout !== 'default' ? grCfg.settings.stoppedString1acr : grCfg.settings.stoppedString1 : ' ', undefined, true); + grStr.titleLower = $(showLowerBarVersion ? ` ${grCfg.settings.stoppedString2}` : ' ', undefined, true); + grStr.year = ''; + grStr.grid = []; + grStr.time = showLowerBarVersion || grCfg.updateAvailable ? grCfg.lowerBarStoppedTime : ' '; + } + + /** + * Creates and sets all theme fonts. + */ + createFonts() { + // * TOOLTIPS * // + this.ttip = window.Tooltip; + if (this.ttip) { + this.ttip.Text = ''; + this.ttip.SetFont(grFont.fontDefault, SCALE(15)); + this.ttip.SetMaxWidth(SCALE(grSet.layout !== 'default' ? 600 : 800)); + } + + // * FONT SIZES * // + const menuFontSize = grSet[`menuFontSize_${grSet.layout}`]; + const menuCaptionFontSize = grSet[`menuFontSize_${grSet.layout}`] + 1; + const lowerBarFontSize = grSet[`lowerBarFontSize_${grSet.layout}`]; + const notificationFontSize = grSet[`notificationFontSize_${grSet.layout}`]; + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const tooltipFontSize = grSet[`tooltipFontSize_${grSet.layout}`]; + + const guiFxBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 2; + const pboDefaultBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 1.6; + const pboReplayBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 2; + const pboShuffleBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 1.65; + const reloadBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 1.5; + const addTrackBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 1.5; + const volumeBtnFontSize = grSet[`transportButtonSize_${grSet.layout}`] / 1.33; + + const gridArtistFontSize = grSet[`gridArtistFontSize_${grSet.layout}`]; + const gridTrackNumFontSize = grSet[`gridTrackNumFontSize_${grSet.layout}`]; + const gridTitleFontSize = grSet[`gridTitleFontSize_${grSet.layout}`]; + const gridAlbumFontSize = grSet[`gridAlbumFontSize_${grSet.layout}`]; + const gridKeyFontSize = grSet[`gridKeyFontSize_${grSet.layout}`]; + const gridValueFontSize = grSet[`gridValueFontSize_${grSet.layout}`] + 1; + + const playlistFontSize = grSet[`playlistFontSize_${grSet.layout}`]; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`]; + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`]; + const lyricsFontSize = grSet[`lyricsFontSize_${grSet.layout}`]; + + // * STYLE CHANGE * // + const artistTitle = grSet.showGridArtist_default && grSet.showGridTitle_default || grSet.showGridArtist_artwork && grSet.showGridTitle_artwork; + + // * TOP MENU BUTTONS * // + grFont.topMenu = Font(grFont.fontTopMenu, menuFontSize, 0); + grFont.topMenuCaption = Font(grFont.fontTopMenuCaption, menuCaptionFontSize, 0); + grFont.topMenuCompact = Font(grFont.fontAwesome, menuFontSize, 0); + + // * LOWER BAR * // + grFont.lowerBarArtist = Font(grFont.fontLowerBarArtist, lowerBarFontSize, grSet.customThemeFonts ? FontStyle.bold : 0); + grFont.lowerBarTitle = Font(grFont.fontLowerBarTitle, lowerBarFontSize, 0); + grFont.lowerBarDisc = Font(grFont.fontLowerBarDisc, lowerBarFontSize, 0); + grFont.lowerBarTime = Font(grFont.fontLowerBarTime, lowerBarFontSize, grSet.customThemeFonts ? FontStyle.bold : 0); + grFont.lowerBarLength = Font(grFont.fontLowerBarLength, lowerBarFontSize, 0); + grFont.lowerBarWave = Font(grFont.fontLowerBarWave, lowerBarFontSize - 6, grSet.customThemeFonts ? FontStyle.bold : 0); + + if (grCfg.updateHyperlink) grCfg.updateHyperlink.setFont(grFont.lowerBarTitle); + + // * LOWER BAR TRANSPORT BUTTONS * // + grFont.guifx = Font(grFont.fontGuiFx, Math.floor(guiFxBtnFontSize), 0); + grFont.pboDefault = Font(grFont.fontGuiFx, Math.floor(pboDefaultBtnFontSize), 0); + grFont.pboRepeatPlaylist = Font(grFont.fontAwesome, Math.floor(pboReplayBtnFontSize), 0); + grFont.pboRepeatTrack = Font(grFont.fontAwesome, Math.floor(pboReplayBtnFontSize), 0); + grFont.pboShuffle = Font(grFont.fontGuiFx, Math.floor(pboShuffleBtnFontSize), 0); + grFont.guifxReload = Font(grFont.fontGuiFx, Math.floor(reloadBtnFontSize), 0); + grFont.guifxAddTrack = Font(grFont.fontGuiFx, Math.floor(addTrackBtnFontSize), 0); + grFont.guifxVolume = Font(grFont.fontGuiFx, Math.floor(volumeBtnFontSize), 0); + + // * MISC * // + grFont.noAlbumArtStub = Font(grFont.fontAwesome, 160, 0); + grFont.symbol = Font(grFont.fontSegoeUISymbol, playlistFontSize, 0); + grFont.notification = Font(grFont.fontNotification, notificationFontSize, 0); + grFont.popup = Font(grFont.fontPopup, popupFontSize, 0); + grFont.tooltip = Font(grFont.fontTooltip, tooltipFontSize, 0); + + if (grSet.layout === 'compact') return; // These fonts below are not available in Compact layout, so skip these to prevent errors + + // * DETAILS METADATA GRID * // + grFont.gridArtist = Font(grFont.fontGridArtist, gridArtistFontSize, grSet.customThemeFonts ? FontStyle.bold : 0); + grFont.gridTrackNumber = Font(artistTitle ? grFont.fontGridTitle : grFont.fontGridTitleBold, gridTrackNumFontSize, 0); + grFont.gridTitle = Font(artistTitle ? grFont.fontGridTitle : grFont.fontGridTitleBold, gridTitleFontSize, 0); + grFont.gridAlbum = Font(grFont.fontGridAlbum, gridAlbumFontSize, grSet.customThemeFonts ? FontStyle.bold : 0); + grFont.gridKey = Font(grFont.fontGridKey, gridKeyFontSize, 0); + grFont.gridVal = Font(grFont.fontGridValue, gridValueFontSize, 0); + + // * LIBRARY * // + grFont.library = Font(grFont.fontLibrary, libraryFontSize, 0); + + // * BIOGRAPHY * // + grFont.biography = Font(grFont.fontBiography, biographyFontSize, 0); + + // * LYRICS * // + grFont.lyrics = Font(grFont.fontLyrics, lyricsFontSize, 1); + grFont.lyricsHighlight = Font(grFont.fontLyrics, lyricsFontSize * 1.5, 1); + } + + /** + * Repaints rectangles on the seekbar for real time update. + */ + refreshSeekbar() { + // * Time + window.RepaintRect(this.lowerBarTimeX, this.lowerBarTimeY, this.lowerBarTimeW, this.lowerBarTimeH, grSet.spinDiscArt && !this.displayLyrics); + + if (grSet.seekbar === 'waveformbar') return; + + // * Progress bar + const x = grSet.layout !== 'default' ? SCALE(18) : SCALE(38); + const y = (grSet.seekbar === 'peakmeterbar' ? this.peakmeterBarY - SCALE(4) : this.progressBarY) - SCALE(2); + const w = grSet.layout !== 'default' ? this.ww - SCALE(36) : this.ww - SCALE(76); + const h = (grSet.seekbar === 'peakmeterbar' ? this.peakmeterBarH + SCALE(8) : this.progressBarH) + SCALE(4); + window.RepaintRect(x, y, w, h, grSet.spinDiscArt && !this.displayLyrics); + } + + /** + * Sets a given timer interval to update the progress bar. + */ + setProgressBarRefresh() { + DebugLog('setProgressBarRefresh()'); + if (fb.PlaybackLength > 0) { + const refreshRate = { + // We want to update the progress bar for every pixel so divide total time by number of pixels in progress bar + variable: Math.abs(Math.ceil(1000 / ((this.ww - SCALE(80)) / fb.PlaybackLength))), + 1000: 1000, + 500: 500, + 333: 333, + 250: 250, + 200: 200, + 150: 150, + 120: 120, + 100: 100, + 80: 80, + 60: 60, + 30: 30 + }; + + this.progressBarTimerInterval = refreshRate[grSet.seekbar === 'peakmeterbar' ? grSet.peakmeterBarRefreshRate : grSet.progressBarRefreshRate]; + + if (grSet.progressBarRefreshRate === 'variable') { + while (this.progressBarTimerInterval > 500) { // We want even multiples of the base progressBarTimerInterval, so that the progress bar always updates as smoothly as possible + this.progressBarTimerInterval = Math.floor(this.progressBarTimerInterval / 2); + } + while (this.progressBarTimerInterval < 32) { // Roughly 30fps + this.progressBarTimerInterval *= 2; + } + } + } + else { // * Radio streaming + this.progressBarTimerInterval = 1000; + } + + if (this.showDebugTiming) console.log(`Progress bar will update every ${this.progressBarTimerInterval}ms or ${1000 / this.progressBarTimerInterval} times per second.`); + + if (this.progressBarTimer) clearInterval(this.progressBarTimer); + this.progressBarTimer = null; + + if (!fb.IsPaused) { + this.progressBarTimer = setInterval(() => { + this.refreshSeekbar(); + }, this.progressBarTimerInterval || 1000); + } + } + + /** + * Resets the current player size, used in top menu Options > Player size. + */ + resetPlayerSize() { + grSet.playerSize_HD_small = false; + grSet.playerSize_HD_normal = false; + grSet.playerSize_HD_large = false; + grSet.playerSize_QHD_small = false; + grSet.playerSize_QHD_normal = false; + grSet.playerSize_QHD_large = false; + grSet.playerSize_4K_small = false; + grSet.playerSize_4K_normal = false; + grSet.playerSize_4K_large = false; + } + + /** + * Resets the theme when changing to a different one, used in top menu Options > Theme. + */ + resetTheme() { + this.initThemeFull = true; + + const invalidNighttimeStyle = grSet.theme !== 'reborn' && grSet.theme !== 'random' && !grSet.theme.startsWith('custom') || grSet.styleRebornWhite || grSet.styleRebornBlack; + const invalidWhiteThemeStyle = grSet.theme !== 'white' && (grSet.styleBlackAndWhite || grSet.styleBlackAndWhite2 || grSet.styleBlackAndWhiteReborn); + const invalidBlackThemeStyle = grSet.theme !== 'black' && grSet.styleBlackReborn; + const invalidRebornThemeStyle = grSet.theme !== 'reborn' && (grSet.styleRebornWhite || grSet.styleRebornBlack || grSet.styleRebornFusion || grSet.styleRebornFusion2 || grSet.styleRebornFusionAccent); + const invalidGradientStyle = !['reborn', 'random', 'blue', 'darkblue', 'red'].includes(grSet.theme) && !grSet.theme.startsWith('custom') && (grSet.styleGradient || grSet.styleGradient2); + + // * Disable style nighttime for themes that do not support it + if (invalidNighttimeStyle) { + grSet.styleNighttime = false; + this.initStyleState(); + } + + // * Reset themes that do not support specific styles to the default style + if (invalidWhiteThemeStyle || invalidBlackThemeStyle || invalidRebornThemeStyle || invalidGradientStyle) { + this.resetStyle('all'); + } + + grm.color.getThemeColors(this.albumArt); + + // * Update default theme colors when nothing is playing or when changing themes + if (!fb.IsPlaying) grm.color.setThemeColors(); + } + + /** + * Resets all styles or grouped styles when changing styles. Used in top menu Options > Style. + * @param {string} group - Specifies which group of styles to reset: + * - 'group_one' + * - 'group_two' + * - 'all' + * - 'all_theme_day_night' + */ + resetStyle(group) { + const _day_night = grSet.themeSetupDay ? '_day' : '_night'; + + if (group === 'group_one') { + grSet.styleBlend = false; + grSet.styleBlend2 = false; + grSet.styleGradient = false; + grSet.styleGradient2 = false; + } + else if (group === 'group_two') { + grSet.styleAlternative = false; + grSet.styleAlternative2 = false; + grSet.styleBlackAndWhite = false; + grSet.styleBlackAndWhite2 = false; + grSet.styleBlackAndWhiteReborn = false; + grSet.styleBlackReborn = false; + grSet.styleRebornWhite = false; + grSet.styleRebornBlack = false; + grSet.styleRebornFusion = false; + grSet.styleRebornFusion2 = false; + grSet.styleRebornFusionAccent = false; + grSet.styleRandomPastel = false; + grSet.styleRandomDark = false; + } + else if (group === 'all') { + this.initThemeFull = true; + grSet.styleDefault = true; + grSet.styleNighttime = false; + grSet.styleBevel = false; + grSet.styleBlend = false; + grSet.styleBlend2 = false; + grSet.styleGradient = false; + grSet.styleGradient2 = false; + grSet.styleAlternative = false; + grSet.styleAlternative2 = false; + grSet.styleBlackAndWhite = false; + grSet.styleBlackAndWhite2 = false; + grSet.styleBlackAndWhiteReborn = false; + grSet.styleBlackReborn = false; + grSet.styleRebornWhite = false; + grSet.styleRebornBlack = false; + grSet.styleRebornFusion = false; + grSet.styleRebornFusion2 = false; + grSet.styleRebornFusionAccent = false; + grSet.styleRandomPastel = false; + grSet.styleRandomDark = false; + grSet.styleRandomAutoColor = 'off'; + grSet.styleTopMenuButtons = 'default'; + grSet.styleTransportButtons = 'default'; + grSet.styleProgressBarDesign = 'default'; + grSet.styleProgressBar = 'default'; + grSet.styleProgressBarFill = 'default'; + grSet.styleVolumeBarDesign = 'default'; + grSet.styleVolumeBar = 'default'; + grSet.styleVolumeBarFill = 'default'; + grSet.themeBrightness = 'default'; + } + else if (group === 'all_theme_day_night') { + this.initThemeFull = true; + grSet.styleDefault = true; + grSet[`styleNighttime${_day_night}`] = false; + grSet[`styleBevel${_day_night}`] = false; + grSet[`styleBlend${_day_night}`] = false; + grSet[`styleBlend2${_day_night}`] = false; + grSet[`styleGradient${_day_night}`] = false; + grSet[`styleGradient2${_day_night}`] = false; + grSet[`styleAlternative${_day_night}`] = false; + grSet[`styleAlternative2${_day_night}`] = false; + grSet[`styleBlackAndWhite${_day_night}`] = false; + grSet[`styleBlackAndWhite2${_day_night}`] = false; + grSet[`styleBlackAndWhiteReborn${_day_night}`] = false; + grSet[`styleBlackReborn${_day_night}`] = false; + grSet[`styleRebornWhite${_day_night}`] = false; + grSet[`styleRebornBlack${_day_night}`] = false; + grSet[`styleRebornFusion${_day_night}`] = false; + grSet[`styleRebornFusion2${_day_night}`] = false; + grSet[`styleRebornFusionAccent${_day_night}`] = false; + grSet[`styleRandomPastel${_day_night}`] = false; + grSet[`styleRandomDark${_day_night}`] = false; + grSet[`styleRandomAutoColor${_day_night}`] = 'off'; + grSet[`styleTopMenuButtons${_day_night}`] = 'default'; + grSet[`styleTransportButtons${_day_night}`] = 'default'; + grSet[`styleProgressBarDesign${_day_night}`] = 'default'; + grSet[`styleProgressBar${_day_night}`] = 'default'; + grSet[`styleProgressBarFill${_day_night}`] = 'default'; + grSet[`styleVolumeBarDesign${_day_night}`] = 'default'; + grSet[`styleVolumeBar${_day_night}`] = 'default'; + grSet[`styleVolumeBarFill${_day_night}`] = 'default'; + grSet[`themeBrightness${_day_night}`] = 'default'; + } + } + + /** + * Restores theme, style, preset after custom %GR_THEME%, %GR_STYLE%, %GR_PRESET% usage or in theme sandbox. + * Used in initThemeTags() and theme sandbox options. + * @param {boolean} reset - Determines whether to reset the theme style preset or restore it. + */ + restoreThemeStylePreset(reset) { + /** + * Sets or resets an individual setting based on the reset flag. + * When reset is true, the value of prefKey is saved to savedKey. + * When reset is false, the saved value in savedKey is restored to prefKey. + * @param {object} prefObj - The preferences object containing the settings. + * @param {string} prefKey - The key for the current setting to be set or restored. + * @param {string} savedKey - The key for the saved setting to set or restore from. + * @private + */ + const _setSetting = (prefObj, prefKey, savedKey) => { + if (reset) { + prefObj[savedKey] = prefObj[prefKey]; + } else { + prefObj[prefKey] = prefObj[savedKey]; + } + } + + _setSetting(grSet, 'theme', 'savedTheme'); + _setSetting(grSet, 'styleNighttime', 'savedStyleNighttime'); + _setSetting(grSet, 'styleBevel', 'savedStyleBevel'); + _setSetting(grSet, 'styleBlend', 'savedStyleBlend'); + _setSetting(grSet, 'styleBlend2', 'savedStyleBlend2'); + _setSetting(grSet, 'styleGradient', 'savedStyleGradient'); + _setSetting(grSet, 'styleGradient2', 'savedStyleGradient2'); + _setSetting(grSet, 'styleAlternative', 'savedStyleAlternative'); + _setSetting(grSet, 'styleAlternative2', 'savedStyleAlternative2'); + _setSetting(grSet, 'styleBlackAndWhite', 'savedStyleBlackAndWhite'); + _setSetting(grSet, 'styleBlackAndWhite2', 'savedStyleBlackAndWhite2'); + _setSetting(grSet, 'styleBlackAndWhiteReborn', 'savedStyleBlackAndWhiteReborn'); + _setSetting(grSet, 'styleBlackReborn', 'savedStyleBlackReborn'); + _setSetting(grSet, 'styleRebornWhite', 'savedStyleRebornWhite'); + _setSetting(grSet, 'styleRebornBlack', 'savedStyleRebornBlack'); + _setSetting(grSet, 'styleRebornFusion', 'savedStyleRebornFusion'); + _setSetting(grSet, 'styleRebornFusion2', 'savedStyleRebornFusion2'); + _setSetting(grSet, 'styleRebornFusionAccent', 'savedStyleRebornFusionAccent'); + _setSetting(grSet, 'styleRandomPastel', 'savedStyleRandomPastel'); + _setSetting(grSet, 'styleRandomDark', 'savedStyleRandomDark'); + _setSetting(grSet, 'styleRandomAutoColor', 'savedStyleRandomAutoColor'); + _setSetting(grSet, 'styleTopMenuButtons', 'savedStyleTopMenuButtons'); + _setSetting(grSet, 'styleTransportButtons', 'savedStyleTransportButtons'); + _setSetting(grSet, 'styleProgressBarDesign', 'savedStyleProgressBarDesign'); + _setSetting(grSet, 'styleProgressBar', 'savedStyleProgressBar'); + _setSetting(grSet, 'styleProgressBarFill', 'savedStyleProgressBarFill'); + _setSetting(grSet, 'styleVolumeBarDesign', 'savedStyleVolumeBarDesign'); + _setSetting(grSet, 'styleVolumeBar', 'savedStyleVolumeBar'); + _setSetting(grSet, 'styleVolumeBarFill', 'savedStyleVolumeBarFill'); + _setSetting(grSet, 'themeBrightness', 'savedThemeBrightness'); + + if (reset) { + grSet.savedPreset = false; + } else { + grSet.preset = grSet.savedPreset; + } + } + + /** + * Sets the chosen style based by its current state. Used when changing styles in top menu Options > Style. + * @param {string} style - The selected style. + * @param {boolean} state - The state of the selected style will be either activated or deactivated. + * @returns {void} No return value. + */ + setStyle(style, state) { + const _day_night = grSet.themeSetupDay ? '_day' : '_night'; + + if (grSet.themeSetupDay || grSet.themeSetupNight) { + this.resetStyle('all_theme_day_night'); + } + + const themeStyles = { + blend: () => { this.resetStyle('group_one'); grSet[`styleBlend${_day_night}`] = grSet.styleBlend = state; }, + blend2: () => { this.resetStyle('group_one'); grSet[`styleBlend2${_day_night}`] = grSet.styleBlend2 = state; }, + gradient: () => { this.resetStyle('group_one'); grSet[`styleGradient${_day_night}`] = grSet.styleGradient = state; }, + gradient2: () => { this.resetStyle('group_one'); grSet[`styleGradient2${_day_night}`] = grSet.styleGradient2 = state; }, + alternative: () => { this.resetStyle('group_two'); grSet[`styleAlternative${_day_night}`] = grSet.styleAlternative = state; }, + alternative2: () => { this.resetStyle('group_two'); grSet[`styleAlternative2${_day_night}`] = grSet.styleAlternative2 = state; }, + blackAndWhite: () => { this.resetStyle('group_two'); grSet[`styleBlackAndWhite${_day_night}`] = grSet.styleBlackAndWhite = state; }, + blackAndWhite2: () => { this.resetStyle('group_two'); grSet[`styleBlackAndWhite2${_day_night}`] = grSet.styleBlackAndWhite2 = state; }, + blackAndWhiteReborn: () => { this.resetStyle('group_two'); grSet[`styleBlackAndWhiteReborn${_day_night}`] = grSet.styleBlackAndWhiteReborn = state; }, + blackReborn: () => { this.resetStyle('group_two'); grSet[`styleBlackReborn${_day_night}`] = grSet.styleBlackReborn = state; }, + rebornWhite: () => { this.resetStyle('group_two'); grSet[`styleRebornWhite${_day_night}`] = grSet.styleRebornWhite = state; grSet[`themeBrightness${_day_night}`] = grSet.themeBrightness = 'default'; }, + rebornBlack: () => { this.resetStyle('group_two'); grSet[`styleRebornBlack${_day_night}`] = grSet.styleRebornBlack = state; grSet[`themeBrightness${_day_night}`] = grSet.themeBrightness = 'default'; }, + rebornFusion: () => { this.resetStyle('group_two'); grSet[`styleRebornFusion${_day_night}`] = grSet.styleRebornFusion = state; }, + rebornFusion2: () => { this.resetStyle('group_two'); grSet[`styleRebornFusion2${_day_night}`] = grSet.styleRebornFusion2 = state; }, + rebornFusionAccent: () => { this.resetStyle('group_two'); grSet[`styleRebornFusionAccent${_day_night}`] = grSet.styleRebornFusionAccent = state; }, + randomPastel: () => { this.resetStyle('group_two'); grSet[`styleRandomPastel${_day_night}`] = grSet.styleRandomPastel = state; }, + randomDark: () => { this.resetStyle('group_two'); grSet[`styleRandomDark${_day_night}`] = grSet.styleRandomDark = state; } + }; + + return themeStyles[style](); + } + + /** + * Sets a new random theme preset. + */ + setRandomThemePreset() { + if (grSet.presetSelectMode === 'theme') { + this.setThemePresetSelection(false, true); + } + if ((!['off', 'track'].includes(grSet.presetAutoRandomMode) && grSet.presetSelectMode === 'harmonic' || + grSet.presetAutoRandomMode === 'dblclick' && grSet.presetSelectMode === 'theme') && !this.doubleClicked) { + grm.preset.getRandomThemePreset(); + } + } + + /** + * Activates or deactivates all theme presets selection, used in top menu Options > Preset > Select presets. + * @param {boolean} state - The state of theme presets selection will be set to true or false. + * @param {boolean} presetSelectModeTheme - The selection of theme specified presets. + */ + setThemePresetSelection(state, presetSelectModeTheme) { + const presetSelect = { + white: 'presetSelectWhite', + black: 'presetSelectBlack', + reborn: 'presetSelectReborn', + random: 'presetSelectRandom', + blue: 'presetSelectBlue', + darkblue: 'presetSelectDarkblue', + red: 'presetSelectRed', + cream: 'presetSelectCream', + nblue: 'presetSelectNblue', + ngreen: 'presetSelectNgreen', + nred: 'presetSelectNred', + ngold: 'presetSelectNgold', + custom: 'presetSelectCustom' + }; + + for (const key in presetSelect) { + grSet[presetSelect[key]] = state; + } + + if (presetSelectModeTheme) { + const theme = grSet.savedTheme; + if (presetSelect[theme]) { + grSet[presetSelect[theme]] = true; + } else if (theme.startsWith('custom')) { + grSet[presetSelect.custom] = true; + } + } + } + + /** + * Updates the theme when changing styles, used in top menu Options > Style. + */ + updateStyle() { + this.initThemeFull = true; + + if (['white', 'black', 'reborn', 'random'].includes(grSet.theme)) { + // * Update grCol.primary for dynamic themes + if (fb.IsPlaying) { + grm.color.getThemeColors(this.albumArt); + } else { + grm.color.setThemeColors(); + } + } + + this.initTheme(); + DebugLog('\n>>> initTheme -> updateStyle <<<\n'); + if (grSet.theme === 'random' && grSet.randomThemeAutoColor !== 'off') grm.color.getRandomThemeAutoColor(); + this.initStyleState(); + grm.preset.initThemePresetState(); + grm.button.initButtonState(); + } + // #endregion + + // * MAIN - PUBLIC METHODS - CONTROLS * // + // #region MAIN - PUBLIC METHODS - CONTROLS + /** + * Displays the panel, mostly used for the custom menu. + * @param {string} panel - The panel to display: + * - 'playlist' + * - 'details' + * - 'library' + * - 'biography' + * - 'lyrics' + */ + displayPanel(panel) { + this.displayPlaylist = false; + this.displayDetails = false; + this.displayLibrary = false; + this.displayBiography = false; + this.displayLyrics = false; + + const panelActions = { + playlist: () => { this.displayPlaylist = true; }, + details: () => { this.displayDetails = true; }, + library: () => { this.displayLibrary = true; }, + biography: () => { this.displayBiography = true; }, + lyrics: () => { this.displayPlaylist = true; this.displayLyrics = true; } + }; + + if (panelActions[panel]) { + panelActions[panel](); + } + + this.resizeArtwork(true); + grm.button.initButtonState(); + } + + /** + * Displays and controls the user set panel state on startup and when playback is being started or stopped. + * This method is used for: + * - Options > Player controls > Panel > Show panel on startup. + * - Options > Player controls > Panel > Return to home on playback stop. + */ + displayPanelControl() { + if (!grSet.returnToHomeOnPlaybackStop && !grSet.showPanelOnStartup) { + return; + } + + this.displayPlaylist = false; + this.displayPlaylistArtwork = false; + this.displayDetails = false; + this.displayLibrary = false; + this.displayBiography = false; + this.displayLyrics = false; + + const panelActions = { + cover: () => { // Artwork layout only + if (grSet.layout === 'default') { + grSet.showPanelOnStartup = 'playlist'; + this.displayPlaylist = true; + } + }, + playlist: () => { + if (grSet.layout === 'artwork') { + this.displayPlaylistArtwork = true; + } else { + this.displayPlaylist = true; + } + }, + details: () => { + if (grSet.layout === 'artwork') { + this.displayPlaylist = true; + if (pl.playlist) pl.playlist.x = this.ww; // Move hidden Playlist offscreen to disable Playlist mouse functions in Details + this.resizeArtwork(true); + } + this.displayDetails = true; + }, + library: () => { + this.displayPlaylist = this.displayLibrarySplit(); + this.displayLibrary = true; + }, + biography: () => { + this.displayPlaylist = true; + this.displayBiography = true; + }, + lyrics: () => { + this.displayPlaylist = grSet.layout === 'default'; + this.displayLyrics = true; + } + }; + + if (panelActions[grSet.showPanelOnStartup]) { + panelActions[grSet.showPanelOnStartup](); + } + + if (grSet.layout === 'compact') { // Override, needs to be always Playlist panel for Compact layout + this.displayPlaylist = true; + } + + grm.button.initButtonState(); + window.Repaint(); + } + + /** + * Displays the Library and Playlist side by side, called when Library layout is in split mode. + * @param {boolean} control - Limits the area to the width and height of the playlist panel. + * @returns {boolean} True if Library and Playlist are being displayed. + */ + displayLibrarySplit(control) { + return grSet.layout === 'default' && grSet.libraryLayout === 'split' && this.displayLibrary && this.displayPlaylist && + (control ? this.state.mouse_x > pl.playlist.x && this.state.mouse_x <= pl.playlist.x + pl.playlist.w && + this.state.mouse_y > pl.playlist.y && this.state.mouse_y <= pl.playlist.y + pl.playlist.h : this.ww); + } + + /** + * Displays Lyrics on startup or when remembering the Lyrics panel state. + */ + displayLyricsOnStart() { + fb.Play(); + this.displayPlaylist = grSet.layout === 'default'; + setTimeout(() => { + if (!grSet.lyricsRememberPanelState) { + grSet.lyricsPanelState = false; + } + this.displayLyrics = true; + grm.lyrics.initLyrics(); + grm.button.initButtonState(); + }, 500); + } + + /** + * Restores the Lyrics layout to full width. + */ + restoreLyricsLayout() { + if (!this.displayLyrics || !this.lyricsLayoutFullWidth) return; + if (!this.displayBiography) this.displayPlaylist = false; + grSet.lyricsLayout = 'full'; + } + // #endregion + + // * MAIN - PUBLIC METHODS - GRAPHICS * // + // #region MAIN - PUBLIC METHODS - GRAPHICS + /** + * Creates the top menu and lower bar button images for button state 'Enabled', 'Hovered', 'Down'. + */ + createButtonImages() { + const createButtonProfiler = this.showExtraDrawTiming && fb.CreateProfiler('createButtonImages'); + const transportCircleSize = Math.round(grSet[`transportButtonSize_${grSet.layout}`] * 0.93333); + let btns = {}; + + try { + btns = { + Stop: { + ico: Guifx.stop, + font: grFont.guifx, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + Previous: { + ico: Guifx.previous, + font: grFont.guifx, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + Play: { + ico: Guifx.play, + font: grFont.guifx, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + Pause: { + ico: Guifx.pause, + font: grFont.guifx, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + Next: { + ico: Guifx.next, + font: grFont.guifx, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + PlaybackDefault: { + ico: Guifx.right, + font: grFont.pboDefault, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + PlaybackRepeatPlaylist: { + ico: '\uf01e', + font: grFont.pboRepeatPlaylist, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + PlaybackRepeatTrack: { + ico: '\uf021', + font: grFont.pboRepeatTrack, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + PlaybackShuffle: { + ico: Guifx.shuffle, + font: grFont.pboShuffle, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + ShowVolume: { + ico: Guifx.volume_down, + font: grFont.guifxVolume, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + Reload: { + ico: Guifx.power, + font: grFont.guifxReload, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + AddTracks: { + ico: Guifx.medical, + font: grFont.guifxAddTrack, + type: 'transport', + w: transportCircleSize, + h: transportCircleSize + }, + Minimize: { + ico: '0', + font: grFont.topMenuCaption, + type: 'window', + w: 22, + h: 22 + }, + Maximize: { + ico: '2', + font: grFont.topMenuCaption, + type: 'window', + w: 22, + h: 22 + }, + Close: { + ico: 'r', + font: grFont.topMenuCaption, + type: 'window', + w: 22, + h: 22 + }, + Hamburger: { + ico: '\uf0c9', + font: grFont.topMenuCompact, + type: 'compact' + }, + TopMenu: { + ico: 'Menu', + font: grFont.topMenu, + type: 'compact' + }, + File: { + ico: 'File', + font: grFont.topMenu, + type: 'menu' + }, + Edit: { + ico: 'Edit', + font: grFont.topMenu, + type: 'menu' + }, + View: { + ico: 'View', + font: grFont.topMenu, + type: 'menu' + }, + Playback: { + ico: 'Playback', + font: grFont.topMenu, + type: 'menu' + }, + MediaLibrary: { + ico: 'Media', + font: grFont.topMenu, + type: 'menu' + }, + Help: { + ico: 'Help', + font: grFont.topMenu, + type: 'menu' + }, + Playlists: { + ico: 'Playlists', + font: grFont.topMenu, + type: 'menu' + }, + Options: { + ico: 'Options', + font: grFont.topMenu, + type: 'menu' + }, + Details: { + ico: 'Details', + font: grFont.topMenu, + type: 'menu' + }, + PlaylistArtworkLayout: { + ico: 'Playlist', + font: grFont.topMenu, + type: 'menu' + }, + Library: { + ico: 'Library', + font: grFont.topMenu, + type: 'menu' + }, + Lyrics: { + ico: 'Lyrics', + font: grFont.topMenu, + type: 'menu' + }, + Biography: { + ico: 'Biography', + font: grFont.topMenu, + type: 'menu' + }, + Rating: { + ico: 'Rating', + font: grFont.topMenu, + type: 'menu' + }, + Properties: { + ico: 'Properties', + font: grFont.topMenu, + type: 'menu' + }, + Settings: { + ico: 'Settings', + font: grFont.topMenu, + type: 'menu' + }, + Back: { + ico: '\uE00E', + type: 'backforward', + font: grFont.symbol, + w: 22, + h: 22 + }, + Forward: { + ico: '\uE00F', + type: 'backforward', + font: grFont.symbol, + w: 22, + h: 22 + } + }; + } catch (e) { + console.log('**********************************'); + console.log('ATTENTION: Buttons could not be created'); + console.log(`Make sure you installed the theme correctly to ${fb.ProfilePath}.`); + console.log('**********************************'); + } + + this.btnImg = []; + + for (const i in btns) { + if (btns[i].type === 'menu') { + const img = gdi.CreateImage(100, 100); + const g = img.GetGraphics(); + const measurements = g.MeasureString(btns[i].ico, btns[i].font, 0, 0, 0, 0); + + btns[i].w = Math.ceil(measurements.Width + 20); + btns[i].h = Math.ceil(measurements.Height + 5); + img.ReleaseGraphics(g); + } + + if (btns[i].type === 'compact') { + const img = gdi.CreateImage(100, 100); + const g = img.GetGraphics(); + const measurements = g.MeasureString(btns[i].ico, btns[i].font, 0, 0, 0, 0); + + btns[i].w = Math.ceil(measurements.Width + (RES._4K ? 32 : 41)); + btns[i].h = Math.ceil(measurements.Height + (RES._4K ? -2 : 5)); + img.ReleaseGraphics(g); + } + + // const { x, y } = btns[i]; + let { w, h } = btns[i]; + const lineW = SCALE(2); + + if (RES._4K && btns[i].type === 'transport') { + w *= 2; + h *= 2; + } else if (RES._4K && btns[i].type !== 'menu') { + w = Math.round(btns[i].w * 1.5); + h = Math.round(btns[i].h * 1.6); + } else if (RES._4K) { + w += 20; + h += 10; + } + + const stateImages = []; // 0=ButtonState.Default, 1=hover, 2=down, 3=Enabled; + for (let state = 0; state < Object.keys(ButtonState).length; state++) { + const btn = btns[i]; + if (state === 3 && btn.type !== 'image') break; + const img = gdi.CreateImage(w, h); + const g = img.GetGraphics(); + g.SetSmoothingMode(SmoothingMode.AntiAlias); + // * Positions playback icons weirdly on AntiAliasGridFit + if (btns[i].type !== 'transport' && !grSet.customThemeFonts) { + g.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); + } + // * Positions some top menu buttons weirdly when using custom theme fonts on AntiAliasGridFit and vertical/horizontal centered font alignment, i.e StringFormat(1, 1); + else if ((btns[i].type === 'menu' || btn.type === 'compact') && grSet.customThemeFonts || btns[i].type === 'transport') { + g.SetTextRenderingHint(TextRenderingHint.AntiAlias); + } + + let menuTextColor = grCol.menuTextNormal; + let menuRectColor = grCol.menuRectNormal; + let menuBgColor = grCol.menuBgColor; + let transportIconColor = grCol.transportIconNormal; + let transportEllipseColor = grCol.transportEllipseNormal; + // let iconAlpha = 255; // Used for images only and not used atm + + switch (state) { + case ButtonState.Hovered: + menuTextColor = grCol.menuTextHovered; + menuRectColor = grCol.menuRectHovered; + menuBgColor = grCol.menuBgColor; + transportIconColor = grCol.transportIconHovered; + transportEllipseColor = grCol.transportEllipseHovered; + // iconAlpha = 215; + break; + case ButtonState.Down: + menuTextColor = grCol.menuTextDown; + menuRectColor = grCol.menuRectDown; + menuBgColor = grCol.menuBgColor; + transportIconColor = grCol.transportIconDown; + transportEllipseColor = grCol.transportEllipseDown; + // iconAlpha = 215; + break; + case ButtonState.Enabled: + // iconAlpha = 255; + break; + } + + switch (btn.type) { + case 'menu': case 'window': case 'compact': + if (grSet.styleTopMenuButtons === 'default' || grSet.styleTopMenuButtons === 'filled') { + if (grSet.styleTopMenuButtons === 'filled') state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 3, 3, menuBgColor); + state && g.DrawRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 3, 3, 1, menuRectColor); + } + else if (grSet.styleTopMenuButtons === 'bevel') { + state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, menuBgColor); + state && FillGradRoundRect(g, Math.floor(lineW / 2), Math.floor(lineW / 2) + 1, w, h - 1, 4, 4, 90, 0, grCol.menuStyleBg, 1); + state && g.DrawRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, 1, menuRectColor); + } + else if (grSet.styleTopMenuButtons === 'inner') { + state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, menuBgColor); + state && FillGradRoundRect(g, Math.floor(lineW / 2), Math.floor(lineW / 2) + 1, w, h - 1, 4, 4, 90, 0, grCol.menuStyleBg, 0); + state && g.DrawRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, 1, menuRectColor); + } + else if (grSet.styleTopMenuButtons === 'emboss') { + state && g.FillRoundRect(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW, 4, 4, menuBgColor); + state && FillGradRoundRect(g, Math.floor(lineW / 2), Math.floor(lineW / 2) + 1, w, h - 1, 4, 4, 90, 0, grCol.menuStyleBg, 0.33); + state && g.DrawRoundRect(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 1, 4, 4, 1, grCol.menuRectStyleEmbossTop); + state && g.DrawRoundRect(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2), w - lineW - 2, h - lineW - 1, 4, 4, 1, grCol.menuRectStyleEmbossBottom); + } + if (btn.type === 'compact') { + g.DrawString('\uf0c9', grFont.topMenuCompact, menuTextColor, RES._4K ? -39 : -19, 0, w, h, StringFormat(1, 1)); + g.DrawString(btn.ico, btn.font, menuTextColor, RES._4K ? 20 : 10, RES._4K ? -1 : 0, w, h, StringFormat(1, 1)); + } else { + g.DrawString(btn.ico, btn.font, menuTextColor, 0, 0, w, btn.type === 'window' ? h : h - 1, StringFormat(1, 1)); + } + break; + + case 'transport': + if (grSet.styleTransportButtons === 'default') { + g.DrawEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 2, lineW, transportEllipseColor); + g.FillEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 2, grCol.transportEllipseBg); + } + else if (grSet.styleTransportButtons === 'bevel') { + g.FillEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW - 1, h - lineW - 1, grCol.transportStyleTop); + g.DrawEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW - 1, h - lineW, 1, grCol.transportStyleBottom); + FillGradEllipse(g, Math.floor(lineW / 2) - 0.5, Math.floor(lineW / 2), w + 0.5, h + 0.5, 90, 0, grCol.transportStyleBg, 1); + } + else if (grSet.styleTransportButtons === 'inner') { + g.FillEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2), w - lineW, h - lineW - 1, grCol.transportStyleTop); + g.DrawEllipse(Math.floor(lineW / 2), Math.floor(lineW / 2) - 1, w - lineW, h - lineW + 1, 1, grCol.transportStyleBottom); + FillGradEllipse(g, Math.floor(lineW / 2) - 0.5, Math.floor(lineW / 2), w + 1.5, h + 0.5, 90, 0, grCol.transportStyleBg, 0); + } + else if (grSet.styleTransportButtons === 'emboss') { + g.FillEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 1, w - lineW - 2, h - lineW - 2, grCol.transportEllipseBg); + FillGradEllipse(g, Math.floor(lineW / 2) + 2, Math.floor(lineW / 2) + 2, w - lineW - 2, h - lineW - 2, 90, 0, grCol.transportStyleBg, 0.33); + g.DrawEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2) + 2, w - lineW - 2, h - lineW - 3, lineW, grCol.transportStyleTop); + g.DrawEllipse(Math.floor(lineW / 2) + 1, Math.floor(lineW / 2), w - lineW - 2, h - lineW - 2, lineW, grCol.transportStyleBottom); + } + g.DrawString(btn.ico, btn.font, transportIconColor, 1, (['Stop', 'Reload', 'AddTracks'].includes(i)) ? 0 : 1, w, h, StringFormat(1, 1)); + break; + + case 'backforward': + g.DrawString(btn.ico, btn.font, pl.col.plman_text_hovered, i === 'Back' ? -1 : 0, 0, w, h, StringFormat(1, 1)); + break; + } + + img.ReleaseGraphics(g); + stateImages[state] = img; + } + + this.btnImg[i] = stateImages; + } + + if (createButtonProfiler) createButtonProfiler.Print(); + } + + /** + * Creates the top menu and lower bar transport buttons. + * @param {number} ww - The window.Width. + * @param {number} wh - The window.Height. + */ + createButtonObjects(ww, wh) { + this.btn = []; + const menuFontSize = grSet[`menuFontSize_${grSet.layout}`]; + const showingMinMaxButtons = !!(UIHacks && UIHacks.FrameStyle); + const showTransportControls = grSet[`showTransportControls_${grSet.layout}`]; + + if (ww <= 0 || wh <= 0) { + return; + } else if (this.btnImg.length === 0) { + this.createButtonImages(); + } + + // * TOP MENU BUTTONS * // + /** @type {GdiBitmap[]} */ + let img = this.btnImg.File; + const w = img[0].Width; + const h = img[0].Height; + let x = RES._4K ? 18 : 8; + const y = Math.round(this.topMenuHeight * 0.5 - h * 0.5 - SCALE(1)); + + // Top menu font size X-correction for Artwork and Compact layout + const xOffset = ww > SCALE(grSet.layout === 'compact' ? 570 : 620) ? 0 : + menuFontSize === 13 && !RES._QHD ? SCALE(3) : + menuFontSize === 14 && !RES._QHD ? SCALE(5) : + menuFontSize === 16 ? RES._QHD ? 4 : SCALE(12) : 0; + + const widthCorrection = + RES._4K ? (grSet.customThemeFonts && menuFontSize > 12 && ww < 1080) ? 12 : (grSet.customThemeFonts && menuFontSize > 10 && ww < 1080) ? 6 : 3 : + (grSet.customThemeFonts && menuFontSize > 12 && ww < 600) ? 6 : (grSet.customThemeFonts && menuFontSize > 10 && ww < 600) ? 4 : 0; + const correction = widthCorrection + (grSet.layout !== 'default' ? xOffset : 0); + + // * Top menu compact + if (grSet.showTopMenuCompact) { + img = this.btnImg.TopMenu; + this.btn[19] = new Button(x, y, w + SCALE(41), h, 'Menu', img, 'Open menu'); + } + + // * Default foobar2000 buttons + if (!grSet.showTopMenuCompact) { + img = this.btnImg.File; + this.btn[20] = new Button(x, y, w, h, 'File', img); + } + + // These buttons are not available in Artwork layout + if (grSet.layout !== 'artwork') { + x += img[0].Width - correction; + img = this.btnImg.Edit; + if (!grSet.showTopMenuCompact) this.btn[21] = new Button(x, y, img[0].Width, h, 'Edit', img); + + x += img[0].Width - correction; + img = this.btnImg.View; + if (!grSet.showTopMenuCompact) this.btn[22] = new Button(x, y, img[0].Width, h, 'View', img); + + x += img[0].Width - correction; + img = this.btnImg.Playback; + if (!grSet.showTopMenuCompact) this.btn[23] = new Button(x, y, img[0].Width, h, 'Playback', img); + + x += img[0].Width - correction; + img = this.btnImg.MediaLibrary; + if (!grSet.showTopMenuCompact) this.btn[24] = new Button(x, y, img[0].Width, h, 'Library', img); + + x += img[0].Width - correction; + img = this.btnImg.Help; + if (!grSet.showTopMenuCompact) this.btn[25] = new Button(x, y, img[0].Width, h, 'Help', img); + + x += img[0].Width - correction; + img = this.btnImg.Playlists; + if (!grSet.showTopMenuCompact) this.btn[26] = new Button(x, y, img[0].Width, h, 'Playlists', img); + } + + // * Theme buttons + const showPanelDetails = grSet[`showPanelDetails_${grSet.layout}`]; + const showPanelLibrary = grSet[`showPanelLibrary_${grSet.layout}`]; + const showPanelBiography = grSet[`showPanelBiography_${grSet.layout}`]; + const showPanelLyrics = grSet[`showPanelLyrics_${grSet.layout}`]; + const showPanelRating = grSet[`showPanelRating_${grSet.layout}`]; + + const buttonCount = (showPanelDetails ? 1 : 0) + (showPanelLibrary ? 1 : 0) + (showPanelBiography ? 1 : 0) + (showPanelLyrics ? 1 : 0) + (showPanelRating ? 1 : 0); + const buttonXCorr = 0.33 + (buttonCount === 5 ? 0 : buttonCount === 4 ? 0.3 : buttonCount === 3 ? 0.6 : buttonCount === 2 ? 1.5 : buttonCount === 1 ? 4 : 0); + + x += img[0].Width - widthCorrection; + if (grSet.layout === 'artwork') x -= xOffset; + // Options button is available in all layouts + img = this.btnImg.Options; + if (!grSet.showTopMenuCompact) this.btn[27] = new Button(x, y, img[0].Width, h, 'Options', img, 'Theme options'); + + // These buttons are not available in Compact layout + if (grSet.layout !== 'compact') { + if (grSet.topMenuAlignment === 'center' && ww > SCALE(grSet.layout === 'artwork' ? 600 : 1380) || grSet.showTopMenuCompact) { + const centerMenu = Math.ceil(w * (buttonCount + (grSet.layout === 'artwork' && grSet.topMenuCompact ? 0.5 : 0)) + (menuFontSize * buttonCount * buttonXCorr)); + x = Math.round(ww * 0.5 - centerMenu); + } + + if (showPanelDetails) { + x += img[0].Width - correction; + img = this.btnImg.Details; + this.btn.details = new Button(x, y, img[0].Width, h, 'Details', img, 'Display Details'); + + // Playlist button only available in Artwork layout + if (grSet.layout === 'artwork') { + x += img[0].Width - correction; + img = this.btnImg.PlaylistArtworkLayout; + this.btn.playlistArtworkLayout = new Button(x, y, img[0].Width, h, 'PlaylistArtworkLayout', img, 'Display Playlist'); + } + } + if (showPanelLibrary) { + x += img[0].Width - correction; + img = this.btnImg.Library; + this.btn.library = new Button(x, y, img[0].Width, h, 'library', img, 'Display Library'); + } + if (showPanelBiography) { + x += img[0].Width - correction; + img = this.btnImg.Biography; + this.btn.biography = new Button(x, y, img[0].Width, h, 'Biography', img, 'Display Biography'); + } + if (showPanelLyrics) { + x += img[0].Width - correction; + img = this.btnImg.Lyrics; + this.btn.lyrics = new Button(x, y, img[0].Width, h, 'Lyrics', img, 'Display Lyrics'); + } + if (showPanelRating) { + x += img[0].Width - correction; + img = this.btnImg.Rating; + this.btn.rating = new Button(x, y, img[0].Width, h, 'Rating', img, 'Rate Song'); + } + } + + // * Top menu 🗕 🗖 ✖ caption buttons + if (showingMinMaxButtons) { + const hideClose = UIHacks.FrameStyle === FrameStyle.SmallCaption && UIHacks.FullScreen !== true; + + const w = SCALE(22); + const h = w; + const p = 3; + const x = ww - w * (hideClose ? 2 : 3) - p * (hideClose ? 1 : 2) - (RES._4K ? 21 : 14); + const y = Math.round(this.topMenuHeight * 0.5 - h * 0.5 - SCALE(1)); + + if (grSet.layout === 'default') { + this.btn.Minimize = new Button(x, y, w, h, 'Minimize', this.btnImg.Minimize); + this.btn.Maximize = new Button(x + w + p, y, w, h, 'Maximize', this.btnImg.Maximize); + if (!hideClose) { + this.btn.Close = new Button(x + (w + p) * 2, menuFontSize < 10 ? y + 1 : y, menuFontSize < 10 ? w - 1 : w, menuFontSize < 10 ? h - 1 : h, 'Close', this.btnImg.Close); + } + } + else { + this.btn.Minimize = new Button(x + w + p, y, w, h, 'Minimize', this.btnImg.Minimize); + if (!hideClose) { + this.btn[12] = new Button(x + (w + p) * 2, y, w, h, 'Close', this.btnImg.Close); + } + } + } + + // * LOWER BAR TRANSPORT BUTTONS * // + if (showTransportControls) { + const lowerBarFontSize = grSet[`lowerBarFontSize_${grSet.layout}`]; + const showPlaybackOrderBtn = grSet[`showPlaybackOrderBtn_${grSet.layout}`]; + const showReloadBtn = grSet[`showReloadBtn_${grSet.layout}`]; + const showAddTrackskBtn = grSet[`showAddTracksBtn_${grSet.layout}`]; + const showVolumeBtn = grSet[`showVolumeBtn_${grSet.layout}`]; + const transportBtnSize = grSet[`transportButtonSize_${grSet.layout}`]; + const transportBtnSpacing = grSet[`transportButtonSpacing_${grSet.layout}`]; + + let count = 4 + (showPlaybackOrderBtn ? 1 : 0) + (showReloadBtn ? 1 : 0) + (showVolumeBtn ? 1 : 0); + + const buttonSize = SCALE(transportBtnSize); + const y = wh - buttonSize - SCALE(grSet.layout !== 'default' ? 36 : 78) + SCALE(lowerBarFontSize); + const w = buttonSize; + const h = w; + const p = SCALE(transportBtnSpacing); // Space between buttons + const x = (ww - w * count - p * (count - 1)) / 2; + + const calcX = (index) => x + (w + p) * index; + + count = 0; + this.btn.stop = new Button(x, y, w, h, 'Stop', this.btnImg.Stop, grm.button.lowerTransportTooltip('stop')); + this.btn.prev = new Button(calcX(++count), y, w, h, 'Previous', this.btnImg.Previous, grm.button.lowerTransportTooltip('prev')); + this.btn.play = new Button(calcX(++count), y, w, h, 'PlayPause', !fb.IsPlaying || fb.IsPaused ? this.btnImg.Play : this.btnImg.Pause, grm.button.lowerTransportTooltip('play')); + this.btn.next = new Button(calcX(++count), y, w, h, 'Next', this.btnImg.Next, grm.button.lowerTransportTooltip('next')); + + if (showPlaybackOrderBtn) { + switch (plman.PlaybackOrder) { + case 0: + this.btn.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', this.btnImg.PlaybackDefault); + break; + case 1: + this.btn.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', this.btnImg.PlaybackRepeatPlaylist); + break; + case 2: + this.btn.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', this.btnImg.PlaybackRepeatTrack); + break; + case 3: case 4: case 5: case 6: + this.btn.playbackOrder = new Button(calcX(++count), y, w, h, 'PlaybackOrder', this.btnImg.PlaybackShuffle); + break; + } + } + if (showReloadBtn) { + this.btn.reload = new Button(calcX(++count), y, w, h, 'Reload', this.btnImg.Reload, grm.button.lowerTransportTooltip('reload')); + } + if (showAddTrackskBtn) { + this.btn.addTracks = new Button(calcX(++count), y, w, h, 'AddTracks', this.btnImg.AddTracks, grm.button.lowerTransportTooltip('addTracks')); + } + if (showVolumeBtn) { + this.btn.volume = new Button(calcX(++count), y, w, h, 'Volume', this.btnImg.ShowVolume); + grm.volBtn.setPosition(this.btn.volume.x, y, w); + } + } + } + + /** + * Loads country flags when defined in tags, displayed in the lower bar and Details. + */ + loadCountryFlags() { + this.flagImgs = []; + for (const country of GetMetaValues(grTF.artist_country)) { + const flagImage = this.loadFlagImage(country); + flagImage && this.flagImgs.push(flagImage); + } + } + + /** + * Loads flag images from the image directory based on the country name or ISO country code provided. + * @param {string} country - The country for which we want to load the flag image. + * @returns {GdiBitmap} The flag image object. + */ + loadFlagImage(country) { + const countryName = (ConvertIsoCountryCodeToFull(country) || country).trim().replace(/ /g, '-'); // In case we have a 2-digit country code + const path = `${$($Escape(grPath.flagsBase)) + (RES._4K ? '64\\' : '32\\') + countryName}.png`; + return gdi.Image(path); + } + // #endregion + + // * MAIN - PUBLIC METHODS - ALBUM ART * // + // #region MAIN - PUBLIC METHODS - ALBUM ART + /** + * Scales album art to a global size, handling potential errors. + * @throws Logs an error if the scaling operation fails. + */ + createScaledAlbumArt() { + if (this.albumArtScaled) this.albumArtScaled = null; + + try { + // * Avoid weird anti-aliased scaling along border of images, see: https://stackoverflow.com/questions/4772273/interpolationmode-highqualitybicubic-introducing-artefacts-on-edge-of-resized-im + this.albumArtScaled = this.albumArt.Resize(this.albumArtSize.w, this.albumArtSize.h, InterpolationMode.Bicubic); // Old method -> this.albumArtScaled = this.albumArt.Resize(this.albumArtSize.w, this.albumArtSize.h); + const sg = this.albumArtScaled.GetGraphics(); + const HQscaled = this.albumArt.Resize(this.albumArtSize.w, this.albumArtSize.h, InterpolationMode.HighQualityBicubic); + sg.DrawImage(HQscaled, 2, 2, this.albumArtScaled.Width - 4, this.albumArtScaled.Height - 4, 2, 2, this.albumArtScaled.Width - 4, this.albumArtScaled.Height - 4); + this.albumArtScaled.ReleaseGraphics(sg); + } catch (e) { + this.noArtwork = true; + this.albumArt = null; + this.noAlbumArtStub = true; + this.albumArtSize = new ImageSize(0, this.topMenuHeight, 0, 0); + console.log('\n\n'); + } + } + + /** + * Creates cropped album art within max dimensions. + * @param {object} albumArt - The original album art with Width and Height. + * @param {number} maxWidth - The max width for the art. + * @param {number} maxHeight - The max height for the art. + * @returns {object} The cropped image and its scale factor. + */ + createCroppedAlbumArt(albumArt, maxWidth, maxHeight) { + const widthScale = maxWidth / albumArt.Width; + const heightScale = maxHeight / albumArt.Height; + const scaledWidth = albumArt.Width * heightScale; + const scaledHeight = albumArt.Height * widthScale; + + // * Fill the height and crop the width + if (scaledWidth >= maxWidth) { + const cropWidth = (scaledWidth - maxWidth) / 2; + return { + image: CropImage(albumArt, cropWidth, 0), + scale: heightScale + }; + } + // * Fill the width and crop the height + else if (scaledHeight >= maxHeight) { + const cropHeight = scaledHeight - maxHeight; + return { + image: CropImage(albumArt, 0, cropHeight), + scale: widthScale + }; + } + // * If no cropping is needed, return the original image and scale + return { + image: albumArt, + scale: Math.min(widthScale, heightScale) + }; + } + + /** + * Displays the next artwork image when cycling through album artworks with a default 30 sec interval or when using album art context menu. + */ + displayNextImage() { + DebugLog(`Repainting in displayNextImage: ${this.albumArtIndex}`); + this.albumArtIndex = (this.albumArtIndex + 1) % this.albumArtList.length; + this.loadImageFromAlbumArtList(this.albumArtIndex); + if (grSet.theme === 'reborn' || grSet.theme === 'random' || grSet.styleBlackAndWhiteReborn || grSet.styleBlackReborn) { + this.newTrackFetchingArtwork = true; + grm.color.getThemeColors(this.albumArt); + this.initTheme(); + DebugLog('\n>>> initTheme -> displayNextImage <<<\n'); + } + this.lastLeftEdge = 0; + this.resizeArtwork(true); // Needed to readjust discArt shadow size if artwork size changes + RepaintWindow(); + this.albumArtTimeout = setTimeout(() => { + this.displayNextImage(); + }, grCfg.settings.artworkDisplayTime * 1000); + grm.button.initButtonState(); + } + /** + * Fetches new album art when a new album is being played or when cycling through album artworks. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + fetchAlbumArt(metadb) { + this.albumArtList = []; + + const fetchAlbumArtProfiler = this.showDebugTiming && fb.CreateProfiler('fetchAlbumArt'); + + const autoRandomPreset = + (!['off', 'track'].includes(grSet.presetAutoRandomMode) && grSet.presetSelectMode === 'harmonic' || + grSet.presetAutoRandomMode === 'dblclick' && grSet.presetSelectMode === 'theme') && !this.doubleClicked; + + if (this.isStreaming || this.isPlayingCD) { + this.discArt = this.disposeDiscArt(this.discArt); + this.discArtCover = this.disposeDiscArt(this.discArtCover); + this.albumArt = utils.GetAlbumArtV2(metadb); + grSet.showGridTitle_default = true; + grSet.showGridTitle_artwork = true; + if (this.albumArt) { + grm.color.getThemeColors(this.albumArt); + this.resizeArtwork(true); + } else { + this.noArtwork = true; + this.shadowImg = null; + } + this.initTheme(); + DebugLog('\n>>> initTheme -> fetchNewArtwork -> isStreaming || isPlayingCD <<<\n'); + } + else { + this.albumArtList = grCfg.imgPaths && grCfg.imgPaths.map(path => utils.Glob($(path), FileAttributes.Directory | FileAttributes.Hidden)).flat(); + const filteredFileTypes = grSet.filterDiscJpgsFromAlbumArt ? '(png|jpg)' : 'png'; + const pattern = new RegExp(`(cd|disc|vinyl|${grCfg.settings.discArtBasename})([0-9]*|[a-h]).${filteredFileTypes}`, 'i'); + const imageType = /(jpg|png)$/i; + // * Remove duplicates and cd/vinyl art and make sure all files are jpg or pngs + this.albumArtList = [...new Set(this.albumArtList)].filter(path => !pattern.test(path) && imageType.test(path)); + + // * Try loading album art from artwork image paths + if (this.albumArtList.length && !grSet.loadEmbeddedAlbumArtFirst) { + this.noArtwork = false; + this.noAlbumArtStub = false; + this.albumArtEmbedded = false; + if (this.albumArtList.length > 1 && grSet.cycleArt) { + this.albumArtTimeout = setTimeout(() => { + this.displayNextImage(); + }, grCfg.settings.artworkDisplayTime * 1000); + } + this.albumArtIndex = 0; + this.loadImageFromAlbumArtList(this.albumArtIndex); // Display first image + } + // * If not found, try embedded artwork from music file + else if (metadb && (this.albumArt = utils.GetAlbumArtV2(metadb))) { + this.discArtCover = grm.artCache.encache(utils.GetAlbumArtV2(metadb), this.albumArtList[metadb], 2); + this.noArtwork = false; + this.noAlbumArtStub = false; + if (autoRandomPreset) { // Prevent double initialization for theme presets to save performance, grMain.color.getThemeColors() and grMain.ui.initTheme() already handled in getRandomThemePreset() + this.setRandomThemePreset(); + } else { + this.initThemeTags(); + grm.color.getThemeColors(this.albumArt); + if (!this.loadingTheme) { + this.initTheme(); // * Prevent incorrect theme brightness at startup/reload when using embedded art + DebugLog('\n>>> initTheme -> fetchNewArtwork -> albumArtEmbedded <<<\n'); + } + } + if (grSet.panelWidthAuto) { + this.initPanelWidthAuto(); + } else { + this.resizeArtwork(true); + } + this.albumArtEmbedded = true; + } + // * No album art found, using noAlbumArtStub + else { + this.noArtwork = true; + this.noAlbumArtStub = true; + this.albumArt = null; + this.discArtCover = null; + this.initTheme(); + DebugLog('\n>>> initTheme -> fetchNewArtwork -> noAlbumArtStub <<<\n'); + if (grSet.panelWidthAuto) { + this.initPanelWidthAuto(); + } else { + this.resizeArtwork(true); + } + DebugLog('Repainting on_playback_new_track due to no cover image'); + RepaintWindow(); + } + } -/** - * Draws the debug theme overlay in the album art area when `Enable debug theme overlay` in Developer tools is active. - * @param {GdiGraphics} gr - */ -function drawDebugThemeOverlay(gr) { - if (!settings.showDebugThemeOverlay) return; - - const fullW = pref.layout === 'default' && pref.lyricsLayout === 'full' && pref.displayLyrics && noAlbumArtStub || pref.layout === 'artwork'; - const titleWidth = albumArtSize.w - SCALE(80); - const titleHeight = gr.CalcTextHeight(' ', ft.popup); - const lineSpacing = titleHeight * 1.5; - const logColor = RGB(255, 255, 255); - const x = albumArtSize.x + SCALE(pref.layout !== 'default' ? 20 : 40); - let y = albumArtSize.y; - - const createBlock = (obj) => Object.keys(obj).find(key => obj[key]) || ''; - - const tsBlock0 = createBlock({ - 'Nighttime,': pref.styleNighttime - }); - - const tsBlock1 = createBlock({ - 'Bevel,': pref.styleBevel - }); - - const tsBlock2 = createBlock({ - 'Blend,': pref.styleBlend, - 'Blend 2,': pref.styleBlend2, - 'Gradient,': pref.styleGradient, - 'Gradient 2,': pref.styleGradient2 - }); - - const tsBlock3 = createBlock({ - 'Alternative ': pref.styleAlternative, - 'Alternative 2': pref.styleAlternative2, - 'Black and white': pref.styleBlackAndWhite, - 'Black and white 2': pref.styleBlackAndWhite2, - 'Black and white reborn': pref.styleBlackAndWhiteReborn, - 'Black reborn': pref.styleBlackReborn, - 'Reborn white': pref.styleRebornWhite, - 'Reborn black': pref.styleRebornBlack, - 'Reborn fusion': pref.styleRebornFusion, - 'Reborn fusion 2': pref.styleRebornFusion2, - 'Reborn fusion accent': pref.styleRebornFusionAccent, - 'Random pastel': pref.styleRandomPastel, - 'Random dark': pref.styleRandomDark - }); - - const tsTopMenuButtons = pref.styleTopMenuButtons !== 'default' ? CapitalizeString(`${pref.styleTopMenuButtons}`) : ''; - const tsTransportButtons = pref.styleTransportButtons !== 'default' ? CapitalizeString(`${pref.styleTransportButtons}`) : ''; - const tsProgressBar1 = pref.styleProgressBarDesign === 'rounded' ? 'Rounded,' : ''; - const tsProgressBar2 = pref.styleProgressBar !== 'default' ? `Bg: ${CapitalizeString(`${pref.styleProgressBar},`)}` : ''; - const tsProgressBar3 = pref.styleProgressBarFill !== 'default' ? `Fill: ${CapitalizeString(`${pref.styleProgressBarFill}`)}` : ''; - const tsVolumeBar1 = pref.styleVolumeBarDesign === 'rounded' ? 'Rounded,' : ''; - const tsVolumeBar2 = pref.styleVolumeBar !== 'default' ? `Bg: ${CapitalizeString(`${pref.styleVolumeBar},`)}` : ''; - const tsVolumeBar3 = pref.styleVolumeBarFill !== 'default' ? `Fill: ${CapitalizeString(`${pref.styleVolumeBarFill}`)}` : ''; - - const propertiesLog = [ - { prop: selectedPrimaryColor, log: `Primary color: ${selectedPrimaryColor}` }, - { prop: selectedPrimaryColor2, log: `Primary 2 color: ${selectedPrimaryColor2}` }, - { prop: colBrightness, log: `Primary color brightness: ${colBrightness}` }, - { prop: colBrightness2, log: `Primary 2 color brightness: ${colBrightness2}` }, - { prop: imgBrightness, log: `Image brightness: ${imgBrightness}` }, - { prop: pref.styleBlend || pref.styleBlend2, log: `Image blur: ${blendedImgBlur}` }, - { prop: pref.styleBlend || pref.styleBlend2, log: `Image alpha: ${blendedImgAlpha}` }, - { prop: pref.preset, log: `Theme preset: ${pref.preset}` }, - { prop: pref.themeBrightness !== 'default', log: `Theme brightness: ${pref.themeBrightness}%` }, - { prop: tsBlock0 || tsBlock1 || tsBlock2 || tsBlock3, log: `Styles: ${tsBlock0} ${tsBlock1} ${tsBlock2} ${tsBlock3}` }, - { prop: tsTopMenuButtons, log: `Top menu button style: ${tsTopMenuButtons}` }, - { prop: tsTransportButtons, log: `Transport button style: ${tsTransportButtons}` }, - { prop: tsProgressBar1 || tsProgressBar2 || tsProgressBar3, log: tsProgressBar1 || tsProgressBar2 || tsProgressBar3 ? `Progressbar styles: ${tsProgressBar1} ${tsProgressBar2} ${tsProgressBar3}` : '' }, - { prop: tsVolumeBar1 || tsVolumeBar2 || tsVolumeBar3, log: `Volumebar styles: ${tsVolumeBar1} ${tsVolumeBar2} ${tsVolumeBar3}` } - ]; - - gr.SetSmoothingMode(SmoothingMode.None); - gr.FillSolidRect(fullW ? 0 : albumArtSize.x, fullW ? geo.topMenuHeight : albumArtSize.y, fullW ? ww : albumArtSize.w, fullW ? wh - geo.topMenuHeight - geo.lowerBarHeight : albumArtSize.h, RGBA(0, 0, 0, 180)); - gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); - - const drawString = (str) => { - y += lineSpacing; - gr.DrawString(str, ft.popup, logColor, x, y, titleWidth, titleHeight, StringFormat(0, 0, 4)); - }; - - for (const { prop, log } of propertiesLog) { - if (prop) drawString(log); - if (prop === imgBrightness) y += lineSpacing; + if (fetchAlbumArtProfiler) fetchAlbumArtProfiler.Print(); } -} + /** + * Fetches new album art/disc art when a new album is being played, disc art has changed or when cycling through album artworks. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + fetchNewArtwork(metadb) { + if (grSet.presetAutoRandomMode === 'album' || grSet.presetSelectMode === 'harmonic') this.initThemeSkip = true; + this.fetchAlbumArt(metadb); + this.fetchDiscArt(); + } -/** - * Draws the draw timing in the console when `Show extra draw timing` in Developer tools is active. - * @param {Date} drawTimingStart The start time of the operation. - */ -function drawDebugTiming(drawTimingStart) { - if (!drawTimingStart) return; - - const drawTimingEnd = new Date(); - const duration = drawTimingEnd - drawTimingStart; - const hours = String(drawTimingStart.getHours()).padStart(2, '0'); - const minutes = String(drawTimingStart.getMinutes()).padStart(2, '0'); - const seconds = String(drawTimingStart.getSeconds()).padStart(2, '0'); - const milliseconds = String(drawTimingStart.getMilliseconds()).padStart(3, '0'); - const time = `${hours}:${minutes}:${seconds}.${milliseconds}`; - const repaintRectCalls = repaintRectCount > 1 ? ` - ${repaintRectCount} repaintRect calls` : ''; - - console.log(`${time}: on_paint total: ${duration}ms${repaintRectCalls}`); -} + /** + * Loads an image from the this.albumArtList array. + * @param {number} index - The index of this.albumArtList signifying which image to load. + */ + loadImageFromAlbumArtList(index) { + const metadb = fb.GetNowPlaying(); + const artIndex = this.albumArtList[index]; + const tempAlbumArt = grm.artCache && grm.artCache.getImage(this.albumArtList[index]); + const tempDiscArtCover = grm.artCache && grm.artCache.getImage(this.albumArtList[index], 2); + + const autoRandomPreset = !this.doubleClicked && + (!['off', 'track'].includes(grSet.presetAutoRandomMode) && grSet.presetSelectMode === 'harmonic' + || + grSet.presetAutoRandomMode === 'dblclick' && grSet.presetSelectMode === 'theme'); + + const hasThemeTags = $('[%GR_THEME%]') || $('[%GR_STYLE%]') || $('[%GR_PRESET%]'); + + const _initTheme = (albumArt) => { + if (autoRandomPreset) { + this.setRandomThemePreset(); + return; + } + this.initThemeTags(); + grm.color.getThemeColors(albumArt); + if (!this.initThemeSkip && !hasThemeTags) { + this.initTheme(); + DebugLog('\n>>> initTheme -> loadImageFromAlbumArtList >>>\n'); + } + }; + if (tempAlbumArt) { + this.albumArt = tempAlbumArt; + this.discArtCover = tempDiscArtCover; + this.albumArtCopy = this.albumArt; -/** - * Draws red rectangles for debugging to show all painted areas in all panels when `Show draw areas` in Developer tools is active. - * @param {GdiGraphics} gr - */ -function drawDebugRectAreas(gr) { - if (!repaintRects.length) return; - try { - for (const rect of repaintRects) { - gr.DrawRect(rect.x, rect.y, rect.w, rect.h, SCALE(2), RGBA(255, 0, 0, 200)); - } - repaintRects = []; - } catch (e) {} -} + if (grSet.panelWidthAuto) { + this.initPanelWidthAuto(); + } + + if (index !== 0 && !this.newTrackFetchingArtwork) return; + this.newTrackFetchingArtwork = false; + _initTheme(this.albumArt); + } + else { + gdi.LoadImageAsyncV2(window.ID, artIndex).then(coverImage => { + this.albumArt = grm.artCache.encache(coverImage, artIndex); + this.discArtCover = grm.artCache.encache(coverImage, artIndex, 2); + + if (this.newTrackFetchingArtwork) { + if (!this.albumArt && fb.IsPlaying && metadb) { + this.albumArt = utils.GetAlbumArtV2(metadb); + if (this.albumArt) { + this.discArtCover = grm.artCache.encache(this.albumArt, artIndex, 2); + this.albumArtEmbedded = true; + } else { + this.noArtwork = true; + this.noAlbumArtStub = true; + this.albumArtEmbedded = false; + console.log('Error GetAlbumArtV2: Album art could not be properly parsed!'); + } + } + _initTheme(this.albumArt); + this.newTrackFetchingArtwork = false; + } + this.albumArtCopy = this.albumArt; -///////////////////////////// -// * MAIN USER INTERFACE * // -///////////////////////////// -/** - * Draws all main user interface parts in the correct order. - * @param {GdiGraphics} gr - */ -function drawMain(gr) { - const drawTimingStart = (timings.showDrawTiming || timings.showExtraDrawTiming) && new Date(); - - drawBackgrounds(gr); - - // * UIHacks aero glass shadow frame fix, needed for style Blend in Details - if (UIHacks.Aero.Effect === 2) gr.DrawLine(0, 0, ww, 0, 1, col.bg); - - drawDetailsBlend(gr); - drawPanels(gr); - drawAlbumArt(gr); - drawNoAlbumArt(gr) - drawHiResAudioLogo(gr); - drawPauseBtn(gr); - drawJumpSearch(gr); - drawDetailsMetadataGrid(gr); - drawDetailsBandLogo(gr); - drawDetailsLabelLogo(gr); - drawLyrics(gr); - drawStyles(gr); - drawThemeNotification(gr); - drawPanelShadows(gr); - drawTopMenuBar(gr); - drawLowerBar(gr); - drawCustomThemeMenu(gr); - drawMetadataGridMenu(gr); - drawStyledTooltips(gr); - drawStartupBackground(gr); - - // * UIHacks aero glass shadow frame fix - if (UIHacks.Aero.Effect === 2 && (!loadingThemeComplete && (pref.styleBlend || pref.styleBlend2)) || !pref.styleBlend && !pref.styleBlend2) { - gr.DrawLine(0, 0, ww, 0, 1, !loadingThemeComplete ? col.loadingThemeBg : col.uiHacksFrame); - if (pref.styleDefault) gr.DrawLine(ww, wh - 1, 0, wh - 1, 1, !loadingThemeComplete ? col.loadingThemeBg : col.uiHacksFrame); - else if (pref.styleGradient || pref.styleGradient2) { - gr.DrawLine(0, 0, ww, 0, 1, col.bg); - gr.FillGradRect(-0.5, 0, ww, 1, pref.styleGradient2 ? -200 : 0, pref.styleGradient2 ? 0 : col.styleGradient, pref.styleGradient2 ? col.styleGradient2 : 0, 0.5); - } - } - - drawDebugThemeOverlay(gr); - drawDebugTiming(drawTimingStart); - drawDebugRectAreas(gr); - repaintRectCount = 0; -} + if (grSet.panelWidthAuto) { + this.initPanelWidthAuto(); + } else { + this.resizeArtwork(true); + } + if (this.discArt) this.createDiscArtRotation(); + RepaintWindow(); + }); + } -/** - * Draws the main user interface. - * @param {GdiGraphics} gr - */ -function on_paint(gr) { - drawMain(gr); -} + if (!this.displayLibrarySplit()) this.resizeArtwork(false); + if (this.discArt) this.createDiscArtRotation(); + } + + /** + * Resizes loaded artwork to have better drawing performance and resets its position. + * Also resets the size and position of the pause button and lyrics. + * @param {boolean} resetDiscArtPosition - Whether the position of the disc art should be reset. + */ + resizeArtwork(resetDiscArtPosition) { + DebugLog('Resizing artwork'); + this.hasArtwork = false; + this.resizeAlbumArt(); + this.resizeDiscArt(resetDiscArtPosition); + this.resetPausePosition(); + grm.lyrics.resetLyricsPosition(); + } + + /** + * Resizes and resets the size and position of the album art. + * Accounts for different window states and user preferences to calculate + * the appropriate size and position of the album artwork. + */ + resizeAlbumArt() { + if (!this.albumArt || !this.albumArt.Width || !this.albumArt.Height) { + this.albumArtSize = new ImageSize(0, this.topMenuHeight, 0, 0); + return; + } + + // * Set album scale + const windowFullscreenOrMaximized = UIHacks.FullScreen || UIHacks.MainWindowState === WindowState.Maximized; + const aspectRatioInBounds = !grSet.albumArtAspectRatioLimit || (this.albumArt.Width < this.albumArt.Height * grSet.albumArtAspectRatioLimit) && (this.albumArt.Height < this.albumArt.Width * grSet.albumArtAspectRatioLimit); + const albumArtCropped = grSet.albumArtScale === 'cropped' && windowFullscreenOrMaximized && aspectRatioInBounds && (this.displayPlaylist || this.displayLibrary); + const albumArtStretched = grSet.albumArtScale === 'stretched' && windowFullscreenOrMaximized && aspectRatioInBounds && (this.displayPlaylist || this.displayLibrary); + const albumArtMaxWidth = this.ww * 0.5; + const albumArtMaxHeight = this.wh - this.topMenuHeight - this.lowerBarHeight; + const albumArtScaleFactor = this.displayPlaylist || this.displayLibrary ? 0.5 : 0.75; + const albumArtScaleDefault = Math.min(this.ww * albumArtScaleFactor / this.albumArt.Width, albumArtMaxHeight / this.albumArt.Height); + const albumArtScaleArtwork = Math.min(this.ww / this.albumArt.Width, albumArtMaxHeight / this.albumArt.Height); + let albumArtScale = grSet.layout === 'artwork' ? albumArtScaleArtwork : albumArtScaleDefault; + + // * Set album art width, height and proportions + if (albumArtCropped) { + const { image, scale } = this.createCroppedAlbumArt(this.albumArt, albumArtMaxWidth, albumArtMaxHeight); + this.albumArtCopy = image; + albumArtScale = scale; + this.albumArtSize.w = Math.floor(this.albumArtCopy.Width * albumArtScale); + this.albumArtSize.h = Math.floor(this.albumArtCopy.Height * albumArtScale); + } else if (albumArtStretched) { + this.albumArtCopy = null; + this.albumArtSize.w = albumArtMaxWidth; + this.albumArtSize.h = albumArtMaxHeight; + } else { // Restore original proportional album art image + this.albumArtCopy = null; + this.albumArtSize.w = Math.floor(this.albumArt.Width * albumArtScale); + this.albumArtSize.h = Math.floor(this.albumArt.Height * albumArtScale); + } + + // * Set xCenter position + let xCenter = this.ww * 0.5; + this.albumArtOffCenter = false; + if (this.displayPlaylist || this.displayLibrary) { + xCenter = grSet.layout === 'artwork' ? 0 : this.ww * 0.25; + } else if (albumArtScale === this.ww * 0.75 / this.albumArt.Width) { + xCenter = Math.round(this.ww * 0.66 - SCALE(40)); // xCenter += this.ww * 0.1; + this.albumArtOffCenter = true; + } + + // * Set album art x-coordinate + switch (grSet.layout) { + case 'default': // In a non-proportional player size, 'pref.albumArtAlign' sets album art alignment in Default layout + if (this.displayPlaylist || this.displayLibrary) { + switch (grSet.albumArtAlign) { + case 'left': + this.albumArtSize.x = Math.round(Math.min(0, this.ww * 0.5 - this.albumArtSize.w)); + break; + case 'leftMargin': + this.albumArtSize.x = Math.round(Math.min(this.ww / this.wh > 1.8 ? SCALE(40) : 0, this.ww * 0.5 - this.albumArtSize.w)); + break; + case 'center': + this.albumArtSize.x = Math.round(Math.min(xCenter - 0.5 * this.albumArtSize.w, this.ww * 0.5 - this.albumArtSize.w)); + break; + default: + this.albumArtSize.x = Math.round(this.ww * 0.5 - this.albumArtSize.w); + break; + } + } else { + this.albumArtSize.x = Math.round(xCenter - 0.5 * this.albumArtSize.w); + } + break; + + case 'artwork': // And is always centered in Artwork layout + this.albumArtSize.x = Math.round(!this.displayPlaylist || this.displayLyrics ? this.ww * 0.5 - this.albumArtSize.w * 0.5 : this.ww); + break; + } + + // * Set album art y-coordinate + const restrictedWidth = albumArtScale !== (this.wh - this.topMenuHeight - this.lowerBarHeight) / this.albumArt.Height; + const centerY = Math.floor(((this.wh - this.lowerBarHeight + this.topMenuHeight) / 2) - this.albumArtSize.h / 2); + this.albumArtSize.y = restrictedWidth ? Math.min(centerY, SCALE(150) + 10) : this.topMenuHeight; + + this.createScaledAlbumArt(); + this.hasArtwork = true; + } + + /** + * Resets the size and position of the pause button. + */ + resetPausePosition() { + const noAlbumArtSize = this.wh - this.topMenuHeight - this.lowerBarHeight; + const windowFullscreenOrMaximized = UIHacks.FullScreen || UIHacks.MainWindowState === WindowState.Maximized; + + const albumArtPauseBtnX = windowFullscreenOrMaximized ? this.ww * 0.25 : this.albumArtSize.x + this.albumArtSize.w * 0.5; + const albumArtPauseBtnY = this.albumArtSize.y + this.albumArtSize.h * 0.5; + const discArtPauseBtnX = this.discArtSize.x + this.discArtSize.w * 0.5; + const discArtPauseBtnY = this.discArtSize.y + this.discArtSize.h * 0.5; + + const noAlbumArtPauseBtnX = + !grSet.panelWidthAuto && grSet.layout !== 'artwork' && !this.noAlbumArtStub && (this.displayPlaylist || this.displayLibrary) || + grSet.layout === 'artwork' || this.displayDetails || grSet.lyricsLayout === 'full' && this.displayLyrics ? this.ww * 0.5 : + grSet.panelWidthAuto ? + grSet.albumArtAlign === 'left' ? noAlbumArtSize * 0.5 : + grSet.albumArtAlign === 'leftMargin' ? this.ww / this.wh > 1.8 ? noAlbumArtSize * 0.5 + SCALE(40) : 0 : + grSet.albumArtAlign === 'center' ? Math.floor(this.ww * 0.5 - noAlbumArtSize * 0.5 - (this.ww * 0.25 - noAlbumArtSize * 0.5)) : + this.ww * 0.5 - noAlbumArtSize * 0.5 : + this.ww * 0.25; + + const noAlbumArtPauseBtnY = this.wh * 0.5 - this.topMenuHeight; + + if (this.albumArt) grm.pseBtn.setCoords(albumArtPauseBtnX, albumArtPauseBtnY); + else if (this.discArt) grm.pseBtn.setCoords(discArtPauseBtnX, discArtPauseBtnY); + else if (this.noAlbumArtStub) grm.pseBtn.setCoords(noAlbumArtPauseBtnX, noAlbumArtPauseBtnY); + } + // #endregion + + // * DETAILS - PUBLIC METHODS - DISC ART * // + // #region DETAILS - PUBLIC METHODS - DISC ART + /** + * Creates and masks an image to the disc art. + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} x - The X-coordinate of the disc area. + * @param {number} y - The Y-coordinate of the disc area. + * @param {number} w - The width of the mask. + * @param {number} h - The height of the mask. + * @param {number} srcX - The X-coordinate of the source image. + * @param {number} srcY - The Y-coordinate of the source image. + * @param {number} srcW - The width of the source image. + * @param {number} srcH - The height of the source image. + * @param {number} [angle] - The angle of the mask in degrees. Default 0. + * @param {number} [alpha] - The alpha of the mask. Values 0-255. + * @returns {GdiGraphics} The rounded masked image. + */ + createDiscArtAlbumArtMask(gr, x, y, w, h, srcX, srcY, srcW, srcH, angle, alpha) { + // * First draw album art in the background + gr.DrawImage(this.albumArtScaled, x, y, w, h, 0, 0, w, h, 0, alpha); + + // * Mask + const maskImg = gdi.CreateImage(w, h); + let g = maskImg.GetGraphics(); + g.FillEllipse(this.discArtSize.x - this.albumArtSize.x + this.discArtShadow - SCALE(4), this.discArtSize.y - this.albumArtSize.y + SCALE(2), + this.discArtSize.w - this.discArtShadow + SCALE(4), this.discArtSize.h - this.discArtShadow + SCALE(2), 0xffffffff); + maskImg.ReleaseGraphics(g); + + // * Album art + const albumArtImg = gdi.CreateImage(w, h); + g = albumArtImg.GetGraphics(); + g.DrawImage(this.albumArtScaled, 0, 0, w, h, 0, 0, this.albumArtScaled.Width, this.albumArtScaled.Height); + albumArtImg.ReleaseGraphics(g); + + const mask = maskImg.Resize(w, h); + albumArtImg.ApplyMask(mask); + + return gr.DrawImage(albumArtImg, x, y, w, h, 0, 0, w, h, 0, 255); + } + + /** + * Creates the album cover mask for the disc art stub. + * @param {GdiBitmap} img - The image to apply the mask to. + * @param {number} w - The width of the mask. + * @param {number} h - The height of the mask. + */ + createDiscArtCoverMask(img, w, h) { + const mask = GDI(this.discArtSize.w, this.discArtSize.h, true, g => { + const lw = SCALE(25); + const innerRingSize = Math.round(this.discArtSize.h * 0.666 + lw * 0.5); + const innerCenterX = Math.round(this.discArtSize.w * 0.5); + const innerCenterY = Math.round(this.discArtSize.h * 0.5); + const innerRadiusX = Math.round(this.discArtSize.w * 0.5 - innerRingSize * 0.5); + const innerRadiusY = Math.round(this.discArtSize.h * 0.5 - innerRingSize * 0.5); + + g.SetSmoothingMode(SmoothingMode.AntiAlias); + g.FillSolidRect(0, 0, this.discArtSize.w, this.discArtSize.h, RGB(255, 255, 255)); + g.FillEllipse(lw * 0.5, lw * 0.5, this.discArtSize.w - lw, this.discArtSize.h - lw, RGB(0, 0, 0)); // Outer ring + g.FillEllipse(innerCenterX - innerRadiusX, innerCenterY - innerRadiusY, innerRadiusX * 2, innerRadiusY * 2, RGB(255, 255, 255)); // Inner ring + }); + + img.ApplyMask(mask.Resize(w, h)); + } + + /** + * Creates the disc art rotation animation with RotateImg(). + * @returns {object} The rotated disc art image. + */ + createDiscArtRotation() { + // Drawing discArt rotated is slow, so first draw it rotated into the discArtRotation image, and then draw discArtRotation image unrotated in on_paint. + if (grSet.displayDiscArt && (this.discArt && this.discArtSize.w > 0)) { + let tracknum = parseInt(fb.TitleFormat(`$num($if(${grTF.vinyl_tracknum},$sub($mul(${grTF.vinyl_tracknum},2),1),$if2(%tracknumber%,1)),1)`).Eval()) - 1; + if (!grSet.rotateDiscArt || Number.isNaN(tracknum)) tracknum = 0; // Avoid NaN issues when changing tracks rapidly + + this.discArtRotation = RotateImg(this.discArt, this.discArtSize.w, this.discArtSize.h, tracknum * grSet.rotationAmt); + if (['cdAlbumCover', 'vinylAlbumCover'].includes(grSet.discArtStub) && this.discArtCover && (!grSet.noDiscArtStub || grSet.showDiscArtStub)) { + this.createDiscArtCoverMask(this.discArtCover, this.discArtCover.Width, this.discArtCover.Height); + this.discArtRotationCover = RotateImg(this.discArtCover, this.discArtSize.w, this.discArtSize.h, tracknum * grSet.rotationAmt); + } + } + + // TODO: Once spinning art is done, scrap this and the rotation amount crap and just use indexes into the discArtArray when needed. + // ? IDEA: Smooth rotation to new position? + return this.discArtRotation; + } + + /** + * Creates the drop shadow for disc art. + */ + createDiscArtShadow() { + const discArtShadowProfiler = this.showDebugTiming && fb.CreateProfiler('createDiscArtShadow'); + const discArtMargin = SCALE(2); + + if (this.displayDetails && ((this.albumArt && this.albumArtSize.w > 0) || (this.discArt && grSet.displayDiscArt && this.discArtSize.w > 0))) { + this.shadowImg = this.discArt && grSet.displayDiscArt ? + gdi.CreateImage(this.discArtSize.x + this.discArtSize.w + 2 * this.discArtShadow, this.discArtSize.h + discArtMargin + 2 * this.discArtShadow) : + gdi.CreateImage(this.albumArtSize.x + this.albumArtSize.w + 2 * this.discArtShadow, this.albumArtSize.h + 2 * this.discArtShadow); + if (grSet.layout === 'default' && this.shadowImg) { + const shimg = this.shadowImg.GetGraphics(); + if (this.discArt && grSet.displayDiscArt) { + const offset = this.discArtSize.w * 0.40; // Don't change this value + const xVal = this.discArtSize.x; + const shadowOffset = this.discArtShadow * 2; + + shimg.DrawEllipse(xVal + shadowOffset, shadowOffset + discArtMargin, this.discArtSize.w - shadowOffset, this.discArtSize.w - shadowOffset, this.discArtShadow * 2, grCol.discArtShadow); // outer shadow + shimg.DrawEllipse(xVal + this.discArtShadow + offset, offset + this.discArtShadow + discArtMargin, this.discArtSize.w - offset * 2, this.discArtSize.h - offset * 2, 60, grCol.discArtShadow); // inner shadow + } + this.shadowImg.ReleaseGraphics(shimg); + this.shadowImg.StackBlur(this.discArtShadow); + } + } + + if (discArtShadowProfiler) discArtShadowProfiler.Print(); + } + + /** + * Disposes the disc art image when changing or deactivating disc art. + * @param {GdiBitmap} discArtImg - The loaded disc art image. + */ + disposeDiscArt(discArtImg) { + this.discArtSize = new ImageSize(0, 0, 0, 0); + discArtImg = null; + } + + /** + * Fetches new disc art when a new album is being played. + */ + fetchDiscArt() { + const fetchDiscArtProfiler = this.showDebugTiming && fb.CreateProfiler('fetchDiscArt'); + const getDiscArtImagePaths = grPath.discArtImagePaths(); + const getDiscArtStubPaths = grPath.discArtStubPaths(); + let discArtPath; + let tempDiscArt; + + if (grSet.displayDiscArt && !this.isStreaming) { // We must attempt to load CD/vinyl art first so that the shadow is drawn correctly + if (grSet.noDiscArtStub || grSet.showDiscArtStub) { + // * Search for disc art + for (const path of getDiscArtImagePaths) { + if (IsFile(path)) { + this.discArtFound = true; + discArtPath = path; + break; + } + } + } + + // * No disc art found, display custom disc art stubs + if (!discArtPath && (!grSet.noDiscArtStub || grSet.showDiscArtStub)) { + this.discArtFound = false; + discArtPath = Object.prototype.hasOwnProperty.call(getDiscArtStubPaths, grSet.discArtStub) ? getDiscArtStubPaths[grSet.discArtStub] : grPath.discArtCustomStub; + } + + // * Load disc art + if (this.albumArtFromCache) { + tempDiscArt = grm.artCache && grm.artCache.getImage(discArtPath); + } + if (tempDiscArt) { + this.disposeDiscArt(this.discArt); + this.discArt = tempDiscArt; + this.resizeArtwork(true); + this.createDiscArtRotation(); + if (grSet.spinDiscArt) { + this.discArtArray = []; // Clear last image + this.setDiscArtRotationTimer(); + } + } + else { + gdi.LoadImageAsyncV2(window.ID, discArtPath).then(discArtImg => { + this.disposeDiscArt(this.discArt); // Delay disposal so we don't get flashing + this.discArt = grm.artCache.encache(discArtImg, discArtPath); + this.resizeArtwork(true); + this.createDiscArtRotation(); + if (grSet.spinDiscArt) { + this.discArtArray = []; // Clear last image + this.setDiscArtRotationTimer(); + } + this.lastLeftEdge = 0; // Recalc label location + RepaintWindow(); + }); + } + } + + if (fetchDiscArtProfiler) fetchDiscArtProfiler.Print(); + } + + /** + * Resizes and resets the size and position of the disc art. + * @param {boolean} resetDiscArtPosition - Whether the position of the disc art should be reset. + */ + resizeDiscArt(resetDiscArtPosition) { + if (!this.discArt) { + this.discArtSize = new ImageSize(0, 0, 0, 0); + return; + } + + const discArtSizeCorr = SCALE(4); + const discArtMargin = SCALE(2); + const discArtMarginRight = SCALE(36); + const discArtMaxHeight = this.wh - this.topMenuHeight - this.lowerBarHeight; + const discScaleFactor = this.displayPlaylist || this.displayLibrary ? 0.5 : 0.75; + const discScale = Math.min(this.ww * discScaleFactor / this.discArt.Width, (discArtMaxHeight - SCALE(16)) / this.discArt.Height); + + if (this.hasArtwork) { + if (resetDiscArtPosition) { + this.discArtSize.x = + this.ww - (this.albumArtSize.x + this.albumArtSize.w) < this.albumArtSize.h * grSet.discArtDisplayAmount ? Math.floor(this.ww - this.albumArtSize.h - discArtMarginRight) : + grSet.discArtDisplayAmount === 1 ? Math.floor(this.ww - this.albumArtSize.h - discArtMarginRight) : + grSet.discArtDisplayAmount === 0.5 ? Math.floor(Math.min(this.ww - this.albumArtSize.h - discArtMarginRight, + this.albumArtSize.x + this.albumArtSize.w - (this.albumArtSize.h - 4) * (1 - grSet.discArtDisplayAmount) - (grSet.discArtDisplayAmount === 1 || grSet.discArtDisplayAmount === 0.5 ? 0 : discArtMarginRight))) : + Math.floor(this.albumArtSize.x + this.albumArtSize.w - (this.albumArtSize.h - discArtSizeCorr) * (1 - grSet.discArtDisplayAmount) - discArtMarginRight); + + this.discArtSize.y = this.albumArtSize.y + discArtMargin; + this.discArtSize.w = this.albumArtSize.h - discArtSizeCorr; // Disc art must be square so use the height of album art for width of discArt + this.discArtSize.h = this.discArtSize.w; + } else { // When disc art moves because folder images are different sizes we want to push it outwards, but not move it back in so it jumps around less + this.discArtSize.x = Math.max(this.discArtSize.x, Math.floor(Math.min(this.ww - this.albumArtSize.h - discArtMarginRight, + this.albumArtSize.x + this.albumArtSize.w - (this.albumArtSize.h - 4) * (1 - grSet.discArtDisplayAmount) - (grSet.discArtDisplayAmount === 1 || grSet.discArtDisplayAmount === 0.5 ? 0 : discArtMarginRight)))); + + this.discArtSize.y = this.discArtSize.y > 0 ? Math.min(this.discArtSize.y, this.albumArtSize.y + discArtMargin) : this.albumArtSize.y + discArtMargin; + this.discArtSize.w = Math.max(this.discArtSize.w, this.albumArtSize.h - discArtSizeCorr); + this.discArtSize.h = this.discArtSize.w; + if (this.discArtSize.x + this.discArtSize.w > this.ww) { + this.discArtSize.x = this.ww - this.discArtSize.w - discArtMarginRight; + } + } + } + else { // * No album art so we need to calc size of disc + let xCenter = this.ww * 0.5; + this.albumArtOffCenter = false; + if (this.displayPlaylist || this.displayLibrary) { + xCenter = this.ww * 0.25; + } else if (discScale === this.ww * 0.75 / this.discArt.Width) { + xCenter = Math.round(this.ww * 0.66 - SCALE(40)); + this.albumArtOffCenter = true; + } + + // Need to -4 from height and add 2 to y to avoid skipping discArt drawing - not sure this is needed + this.discArtSize.w = Math.floor(this.discArt.Width * discScale) - discArtSizeCorr; + this.discArtSize.h = this.discArtSize.w; + this.discArtSize.x = Math.floor(xCenter - this.discArtSize.w * 0.5); + + // * Set disc art y-coordinate + const restrictedWidth = discScale !== (discArtMaxHeight - SCALE(16)) / this.discArt.Height; + const centerY = this.topMenuHeight + Math.floor(((discArtMaxHeight - SCALE(16)) / 2) - this.discArtSize.h / 2); + this.discArtSize.y = restrictedWidth ? Math.min(centerY, 160) : this.topMenuHeight + discArtMargin; + + this.hasArtwork = true; + } + + if ((this.hasArtwork || this.noAlbumArtStub) && (this.discArt && this.displayDetails && grSet.displayDiscArt && grSet.layout !== 'compact')) { + this.createDiscArtShadow(); + } + } + + /** + * Sets the disc art timer with different set interval values for rotating the disc art. + */ + setDiscArtRotationTimer() { + clearInterval(this.discArtRotationTimer); + if (grSet.layout === 'default' && this.discArt && fb.IsPlaying && !fb.IsPaused && grSet.displayDiscArt && grSet.spinDiscArt && this.displayDetails) { + console.log(`creating ${grSet.spinDiscArtImageCount} rotated disc images, shown every ${grSet.spinDiscArtRedrawInterval}ms`); + this.discArtRotationTimer = setInterval(() => { + this.discArtRotationIndex++; + this.discArtRotationIndex %= grSet.spinDiscArtImageCount; + this.discArtRotationIndexCover++; + this.discArtRotationIndexCover %= grSet.spinDiscArtImageCount; + + if (!this.discArtArray[this.discArtRotationIndex] && this.discArt && this.discArtSize.w) { + DebugLog(`creating discArtImg: ${this.discArtRotationIndex} (${this.discArtSize.w}x${this.discArtSize.h}) with rotation: ${360 / grSet.spinDiscArtImageCount * this.discArtRotationIndex} degrees`); + this.discArtArray[this.discArtRotationIndex] = RotateImg(this.discArt, this.discArtSize.w, this.discArtSize.h, 360 / grSet.spinDiscArtImageCount * this.discArtRotationIndex); + if (['cdAlbumCover', 'vinylAlbumCover'].includes(grSet.discArtStub) && this.discArtCover && (!grSet.noDiscArtStub || grSet.showDiscArtStub)) { + this.discArtArrayCover[this.discArtRotationIndexCover] = RotateImg(this.discArtCover, this.discArtSize.w, this.discArtSize.h, 360 / grSet.spinDiscArtImageCount * this.discArtRotationIndexCover); + } + } + + // The first line of discArtImg that will be drawn + const discArtLeftEdge = grSet.detailsAlbumArtOpacity !== 255 || grSet.detailsAlbumArtDiscAreaOpacity !== 255 || grSet.discArtOnTop ? this.discArtSize.x : this.albumArtSize.x + this.albumArtSize.w - 1; + window.RepaintRect(discArtLeftEdge, this.discArtSize.y, this.discArtSize.w - (discArtLeftEdge - this.discArtSize.x), this.discArtSize.h, !grSet.discArtOnTop && !this.displayLyrics); + }, grSet.spinDiscArtRedrawInterval); + } + } + // #endregion + + // * DETAILS - PUBLIC METHODS - BAND & LABEL LOGO * // + // #region DETAILS - PUBLIC METHODS - BAND & LABEL LOGO + /** + * Checks if a band logo exists at various paths. + * @param {string} bandStr - The name of the band. + * @returns {string} The path of the band logo if it exists. + */ + checkBandLogo(bandStr) { + // See if artist logo exists at various paths + const testBandLogoPath = (imgDir, name) => { + if (name) { + const logoPath = `${imgDir}${name}.png`; + if (IsFile(logoPath)) { + console.log(`Found band logo: ${logoPath}`); + return logoPath; + } + } + return false; + }; + + return testBandLogoPath(grPath.artistlogos, bandStr) || // Try 800x310 white + testBandLogoPath(grPath.artistlogosColor, bandStr); // Try 800x310 color + } + + /** + * Gets the band logo and its inverted version based on the current playing album artist in Details. + */ + getBandLogo() { + this.bandLogo = null; + this.bandLogoInverted = null; + let path; + let tryArtistList = [ + ...GetMetaValues('%album artist%').map(artist => ReplaceFileChars(artist)), + ...GetMetaValues('%album artist%').map(artist => ReplaceFileChars(artist).replace(/^[Tt]he /, '')), + ReplaceFileChars($('[%track artist%]')), + ...GetMetaValues('%artist%').map(artist => ReplaceFileChars(artist)), + ...GetMetaValues('%artist%').map(artist => ReplaceFileChars(artist).replace(/^[Tt]he /, '')) + ]; + + tryArtistList = [...new Set(tryArtistList)]; + tryArtistList.some(artistString => { + path = this.checkBandLogo(artistString); + return path; + }); + + if (!path) return; + + this.bandLogo = grm.artCache.getImage(path); + if (!this.bandLogo) { + const logo = gdi.Image(path); + if (logo) { + this.bandLogo = grm.artCache.encache(logo, path); + this.bandLogoInverted = grm.artCache.encache(logo.InvertColours(), `${path}-inv`); + } + } + this.bandLogoInverted = grm.artCache.getImage(`${path}-inv`); + if (!this.bandLogoInverted && this.bandLogo) { + this.bandLogoInverted = grm.artCache.encache(this.bandLogo.InvertColours(), `${path}-inv`); + } + } + + /** + * Gets label logos based on current playing album artist in Details. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + getLabelLogo(metadb) { + const labelTags = ['label', 'publisher', 'discogs_label']; + let labelStrings = []; + this.recordLabels = []; // Will free memory from earlier loaded record label images + this.recordLabelsInverted = []; + + for (const label of labelTags) { + labelStrings.push(...GetMetaValues(label, metadb)); + } + labelStrings = [...new Set(labelStrings)]; + + for (const labelString of labelStrings) { + const addLabel = this.loadLabelLogo(labelString); + if (addLabel != null) { + this.recordLabels.push(addLabel); + try { + this.recordLabelsInverted.push(addLabel.InvertColours()); + } catch (e) {} + } + } + } + + /** + * Loads the label logo in Details. + * @param {string} publisherString - The name of a record label or publisher. + * @returns {GdiBitmap} The record label logo as a gdi image object. + */ + loadLabelLogo(publisherString) { + const d = new Date(); + const lastSrchYear = d.getFullYear(); + let dir = grPath.labelsBase; + let recordLabel = null; + let labelStr = ReplaceFileChars(publisherString); + + if (labelStr) { + // * First check for record label folder + if (IsFolder(dir + labelStr) || + IsFolder(dir + (labelStr = + labelStr.replace(/ Records$/, '') + .replace(/ Recordings$/, '') + .replace(/ Music$/, '') + .replace(/\.$/, '') + .replace(/[\u2010\u2013\u2014]/g, '-')))) { // Hyphen, endash, emdash + let year = parseInt($('$year(%date%)')); + for (; year <= lastSrchYear; year++) { + const yearFolder = `${dir + labelStr}\\${year}`; + if (IsFolder(yearFolder)) { + console.log(`Found folder for ${labelStr} for year ${year}.`); + dir += `${labelStr}\\${year}\\`; + break; + } + } + if (year > lastSrchYear) { + dir += `${labelStr}\\`; // We didn't find a year folder so use the "default" logo in the root + console.log(`Found folder for ${labelStr} and using latest logo.`); + } + } + // * Actually load the label from either the directory we found above, or the base record label folder + labelStr = ReplaceFileChars(publisherString); // We need to start over with the original string when searching for the file, just to be safe + let label = `${dir + labelStr}.png`; + + if (IsFile(label)) { + recordLabel = gdi.Image(label); + console.log('Found Record label:', label, !recordLabel ? '' : ''); + } + else { + labelStr = + labelStr.replace(/ Records$/, '') + .replace(/ Recordings$/, '') + .replace(/ Music$/, '') + .replace(/[\u2010\u2013\u2014]/g, '-'); // Hyphen, endash, emdash + + label = `${dir + labelStr}.png`; + if (IsFile(label)) return gdi.Image(label); + label = `${dir + labelStr} Records.png`; + if (IsFile(label)) return gdi.Image(label); + } + } + return recordLabel; + } + // #endregion + + // * DETAILS - PUBLIC METHODS - METADATA GRID * // + // #region DETAILS - PUBLIC METHODS - METADATA GRID + /** + * Calculates date ratios based on various time-related properties of a music track, displayed on the timeline in Details. + * @param {boolean} dontUpdateLastPlayed - Whether the last played date should be updated or not. + * @param {string} currentLastPlayed - The current value of the last played time. + */ + calcDateRatios(dontUpdateLastPlayed = false, currentLastPlayed) { + const newDate = new Date(); + const timezoneOffset = UpdateTimezoneOffset(); + + let ratio; + let lfmPlayedTimesJsonLast = ''; + let playedTimesJsonLast = ''; + let playedTimesRatios = []; + let lfmPlayedTimes = []; + let playedTimes = []; + + let added = ToTime($('$if2(%added_enhanced%,%added%)'), timezoneOffset); + let lastPlayed = ToTime($('$if2(%last_played_enhanced%,%last_played%)'), timezoneOffset); + const firstPlayed = ToTime($('$if2(%first_played_enhanced%,%first_played%)'), timezoneOffset); + const today = DateToYMD(newDate); + + if (dontUpdateLastPlayed && $Date(lastPlayed) === today) { + lastPlayed = ToTime(currentLastPlayed, timezoneOffset); + } + + if (Component.EnhancedPlaycount) { + const playedTimesJson = $('[%played_times_js%]', fb.GetNowPlaying()); + const lastfmJson = $('[%lastfm_played_times_js%]', fb.GetNowPlaying()); + const log = ''; // ! Don't need this crap to flood the console // playedTimesJson === playedTimesJsonLast && lastfmJson === lfmPlayedTimesJsonLast ? false : grCfg.settings.showDebugLog; + lfmPlayedTimesJsonLast = lastfmJson; + playedTimesJsonLast = playedTimesJson; + lfmPlayedTimes = ParseJson(lastfmJson, 'lastfm: ', log); + playedTimes = ParseJson(playedTimesJson, 'foobar: ', log); + } + else { + playedTimes.push(firstPlayed); + playedTimes.push(lastPlayed); + } + + if (firstPlayed) { + if (!added) { + added = firstPlayed; + } + const age = CalcAge(added); + + this.timelineFirstPlayedRatio = CalcAgeRatio(firstPlayed, age); + this.timelineLastPlayedRatio = CalcAgeRatio(lastPlayed, age); + if (this.timelineLastPlayedRatio < this.timelineFirstPlayedRatio) { + // Due to daylight savings time, if there's a single play before the time changed lastPlayed could be < firstPlayed + this.timelineLastPlayedRatio = this.timelineFirstPlayedRatio; + } + + if (playedTimes.length) { + for (let i = 0; i < playedTimes.length; i++) { + ratio = CalcAgeRatio(playedTimes[i], age); + playedTimesRatios.push(ratio); + } + } else { + playedTimesRatios = [this.timelineFirstPlayedRatio, this.timelineLastPlayedRatio]; + playedTimes = [firstPlayed, lastPlayed]; + } + + let j = 0; + const tempPlayedTimesRatios = playedTimesRatios.slice(); + tempPlayedTimesRatios.push(1.0001); // Pick up every last.fm time after lastPlayed fb knows about + for (let i = 0; i < tempPlayedTimesRatios.length; i++) { + while (j < lfmPlayedTimes.length && (ratio = CalcAgeRatio(lfmPlayedTimes[j], age)) < tempPlayedTimesRatios[i]) { + playedTimesRatios.push(ratio); + playedTimes.push(lfmPlayedTimes[j]); + j++; + } + if (ratio === tempPlayedTimesRatios[i]) { // Skip one instance + // console.log('skipped -->', ratio); + j++; + } + } + playedTimesRatios.sort(); + playedTimes.sort(); + + this.timelineFirstPlayedRatio = playedTimesRatios[0]; + this.timelineLastPlayedRatio = playedTimesRatios[Math.max(0, playedTimesRatios.length - (dontUpdateLastPlayed ? 2 : 1))]; + } + else { + this.timelineFirstPlayedRatio = 0.33; + this.timelineLastPlayedRatio = 0.66; + } + grm.timeline.setPlayTimes(this.timelineFirstPlayedRatio, this.timelineLastPlayedRatio, playedTimesRatios, playedTimes); + } + + /** + * Loads the codec logo of the now playing track, displayed in the metadata grid in Details. + */ + loadCodecLogo() { + const codec = $('$lower($if2(%codec%,$ext(%path%)))'); + const format = $('$lower($ext(%path%))', fb.GetNowPlaying()); + const lightBg = new Color(grCol.detailsText).brightness < 140; + const bw = lightBg ? 'black' : 'white'; + + const codecFormat = { + 'aac': 'aac', 'aac acm codec': 'aac', + 'ac3': 'ac3', 'atsc a/52': 'ac3', 'e-ac3': 'ac3', + 'aiff': 'pcm-aiff', + 'alac': 'alac', + 'alaw': 'alaw', 'ccitt a-law': 'alaw', + 'amr': 'amr', + 'ape': 'ape', 'monkey\'s audio': 'ape', + 'caf': 'caf', + 'dsd': 'dsd', + 'dst': 'dsd-sacd', + 'dts': 'dts', 'dca (dts coherent acoustics)': 'dts', + 'dxd': 'dxd', + 'flac': 'flac', + 'gsm': 'gsm', 'gsm 6.10': 'gsm', + 'imaadpcm': 'imaadpcm', 'ima adpcm': 'imaadpcm', + 'la': 'la', + 'mid': 'mid', + 'mlp': 'mlp', + 'mod': 'mod', + 'mp2': 'mp2', + 'mp3': 'mp3', 'mpeg layer-3': 'mp3', + 'mpc': 'musepack', 'musepack': 'musepack', + 'msadpcm': 'msadpcm', 'microsoft adpcm': 'msadpcm', + 'ofr': 'ofr', 'optimfrog': 'ofr', + 'ogg': 'ogg', 'vorbis': 'ogg', + 'opus': 'opus', + 'pcm': format === 'aiff' ? 'pcm-aiff' : ['w64', 'wav'].includes(format) ? 'pcm-wav' : 'pcm', + 'qoa': 'qoa', + 'shn': 'shn', 'shorten': 'shn', + 'spx': 'spx', 'speex': 'spx', + 'tak': 'tak', + 'tta': 'tta', 'true audio': 'tta', + 'ulaw': 'ulaw', 'ccitt u-law': 'ulaw', + 'usac': 'usac', + 'wav': 'pcm-wav', + 'w64': 'pcm-wav', + 'wma': 'wma', + 'wv': 'wavpack', 'wavpack': 'wavpack' + }; + + const codecName = codecFormat[codec] || codecFormat[format]; + const codecLogoPath = (codecName) => `${grPath.images}codec\\${codecName}-${bw}.png`; + + if (codecName) { + this.codecLogo = gdi.Image(codecLogoPath(codecName)); + } + // Handle special cases + if (codec.startsWith('dsd')) { + this.codecLogo = gdi.Image(codecLogoPath('dsd')); + } else if (codec.startsWith('dxd')) { + this.codecLogo = gdi.Image(codecLogoPath('dxd')); + } else if (codec.startsWith('dst')) { + this.codecLogo = gdi.Image(codecLogoPath('dst')); + } + } + + /** + * Loads the channel logo of the now playing track, displayed in the metadata grid in Details. + */ + loadChannelLogo() { + const channels = $('%channels%'); + const type = + (grSet.layout === 'default' && grSet.showGridChannelLogo_default === 'textlogo' || + grSet.layout === 'artwork' && grSet.showGridChannelLogo_artwork === 'textlogo') ? '_text' : ''; + + const lightBg = new Color(grCol.detailsText).brightness < 140; + const bw = lightBg ? 'black' : 'white'; + + const channelFormat = { + 'mono': '10_mono', + 'stereo': '20_stereo', + '3ch': '30_center', + '4ch': '40_quad', + '5ch': '50_surround', + '6ch': '51_surround', + '7ch': '61_surround', + '8ch': '71_surround', + '10ch': '91_surround', + '12ch': '111_surround' + }; + + const channelName = channelFormat[channels]; + const channelLogoPath = (channelName) => `${grPath.images}channels\\${channelName}${type}-${bw}.png`; + if (channelName) this.channelLogo = gdi.Image(channelLogoPath(channelName)); + } + + /** + * Loads the release country flags, displayed in the metadata grid in Details. + */ + loadReleaseCountryFlag() { + this.releaseFlagImg = this.loadFlagImage($(grTF.releaseCountry)); + } + + /** + * Updates the metadata grid in Details, reuses last value for last played unless provided one. + * @param {string} currentLastPlayed - The current value of the "Last Played" metadata field. + * @param {string} currentPlayingPlaylist - The current active playlist that is being played from. + * @returns {Array|null} The updated metadata grid, which is an array of objects with properties `label`, `val` and `age`. + */ + updateMetadataGrid(currentLastPlayed, currentPlayingPlaylist) { + if (!grCfg.metadataGrid) return null; + + currentLastPlayed = (grStr && grStr.grid ? grStr.grid.find(value => value.label === 'Last Played') || {} : {}).val; + grStr.grid = []; + + for (const key of grCfg.metadataGrid) { + let val = $(key.val); + if (val && key.label) { + if (key.age) { + val = $(`$date(${val})`); // Never show time + const age = CalcAgeDateString(val); + if (age) val += ` (${age})`; + } + grStr.grid.push({ + age: key.age, + label: key.label, + val + }); + } + } + if (typeof currentLastPlayed !== 'undefined') { + const lp = grStr.grid.find(value => value.label === 'Last Played'); + if (lp) { + lp.val = $Date(currentLastPlayed); + if (CalcAgeDateString(lp.val)) { + lp.val += ` (${CalcAgeDateString(lp.val)})`; + } + } + } + if (typeof currentPlayingPlaylist !== 'undefined') { + const pl = grStr.grid.find(value => value.label === 'Playing List'); + if (pl) { + pl.val = currentPlayingPlaylist; + } + } + return grStr.grid; + } + // #endregion + + // * PLAYLIST - PUBLIC METHODS - INITIALIZATION * // + // #region PLAYLIST PUBLIC METHODS - INITIALIZATION + /** + * Clears current used color of header and row nowplaying bg to prevent flashing from old used primary color. + */ + clearPlaylistNowPlayingBg() { + if (['white', 'black', 'reborn', 'random'].includes(grSet.theme)) { + pl.col.header_nowplaying_bg = ''; + pl.col.row_nowplaying_bg = ''; + } + } + + /** + * Initializes the Playlist. + */ + initPlaylist() { + pl.call = new PlaylistCallbacks(); + pl.playlist.initialize(); + } + + /** + * Updates the Playlist when content has changed, e.g when adding/removing items or changing the active playlist. + */ + updatePlaylist() { + Debounce((playlistIndex) => { + this.traceCall && console.log('initPlaylistDebounced'); + pl.call.on_playlist_items_added(playlistIndex); + }, 100, { + leading: false, + trailing: true + })(plman.ActivePlaylist); + } + // #endregion + + // * PLAYLIST - PUBLIC METHODS - CONTROLS * // + // #region PLAYLIST PUBLIC METHODS - CONTROLS + /** + * Sorts the Playlist by sort patterns defined in the config file. + */ + setPlaylistSortOrder() { + const sortOrder = { + default: grCfg.settings.playlistSortDefault, + artistDate_asc: grCfg.settings.playlistSortArtistDate_asc, + artistDate_dsc: grCfg.settings.playlistSortArtistDate_dsc, + albumTitle: grCfg.settings.playlistSortAlbumTitle, + albumRating_asc: grCfg.settings.playlistSortAlbumRating_asc, + albumRating_dsc: grCfg.settings.playlistSortAlbumRating_dsc, + albumPlaycount_asc: grCfg.settings.playlistSortAlbumPlaycount_asc, + albumPlaycount_dsc: grCfg.settings.playlistSortAlbumPlaycount_dsc, + trackTitle: grCfg.settings.playlistSortTrackTitle, + trackNumber: grCfg.settings.playlistSortTrackNumber, + trackRating_asc: grCfg.settings.playlistSortTrackRating_asc, + trackRating_dsc: grCfg.settings.playlistSortTrackRating_dsc, + trackPlaycount_asc: grCfg.settings.playlistSortTrackPlaycount_asc, + trackPlaycount_dsc: grCfg.settings.playlistSortTrackPlaycount_dsc, + year_asc: grCfg.settings.playlistSortYear_asc, + year_dsc: grCfg.settings.playlistSortYear_dsc, + genre_asc: grCfg.settings.playlistSortGenre, + genre_dsc: grCfg.settings.playlistSortGenre, + label_asc: grCfg.settings.playlistSortLabel, + label_dsc: grCfg.settings.playlistSortLabel, + country_asc: grCfg.settings.playlistSortCountry, + country_dsc: grCfg.settings.playlistSortCountry, + filePath: grCfg.settings.playlistSortFilePath, + custom: grCfg.settings.playlistSortCustom + }; + + if (['genre_dsc', 'label_dsc', 'country_dsc'].includes(grSet.playlistSortOrder)) { + plman.SortByFormatV2(plman.ActivePlaylist, sortOrder[grSet.playlistSortOrder], -1); + } else { + plman.SortByFormat(plman.ActivePlaylist, sortOrder[grSet.playlistSortOrder] || ''); + } + } + // #endregion + + // * LIBRARY - PUBLIC METHODS - INITIALIZATION * // + // #region LIBRARY - PUBLIC METHODS - INITIALIZATION + /** + * Initializes the Library. + */ + initLibraryPanel() { + if (lib.initialized) return; + lib.ui = new LibUserInterface(); + lib.panel = new LibPanel(); + lib.sbar = new LibScrollbar(); + lib.vk = new LibVkeys(); + lib.lib = new LibLibrary(); + lib.pop = new LibPopulate(); + lib.search = new LibSearch(); + lib.find = new LibFind(); + lib.but = new LibButtons(); + lib.popUpBox = new LibPopUpBox(); + lib.men = new LibMenuItems(); + lib.timer = new LibTimers(); + lib.call = new LibCallbacks(); + lib.initialized = true; + } + + /** + * Initializes active Library layout presets. + */ + initLibraryLayout() { + const libraryLayoutSplitPresets = + grSet.libraryLayoutSplitPreset || grSet.libraryLayoutSplitPreset2 || grSet.libraryLayoutSplitPreset3 || grSet.libraryLayoutSplitPreset4; + + const setLibraryView = () => { + lib.lib.logTree(); + lib.pop.clearTree(); + lib.ui.getFont(); // * Reset font size when pref.libraryLayoutSplitPreset4 was used + RepaintWindowRectAreas(); + if (grSet.libraryLayout !== 'split' && (!grSet.libraryLayoutFullPreset || !libraryLayoutSplitPresets)) { + libSet.albumArtShow = grSet.savedAlbumArtShow; + libSet.albumArtLabelType = grSet.savedAlbumArtLabelType; + } + lib.panel.imgView = grSet.libraryLayout === 'normal' && grSet.libraryLayoutFullPreset ? libSet.albumArtShow = false : libSet.albumArtShow; + lib.men.loadView(false, !lib.panel.imgView ? (libSet.artTreeSameView ? libSet.viewBy : libSet.treeViewBy) : (libSet.artTreeSameView ? libSet.viewBy : libSet.albumArtViewBy), lib.pop.sel_items[0]); + } + + // * Full layout preset + if (grSet.libraryLayout === 'full' && grSet.libraryLayoutFullPreset) { + grSet.libraryDesign = 'reborn'; + grSet.libraryThumbnailSize = grSet.libraryThumbnailSizeSaved; + if (grSet.playerSize_HD_small && (grSet.libraryThumbnailSize === 'auto' || libSet.thumbNailSize === 'auto')) { + libSet.thumbNailSize = 1; + } + libSet.albumArtLabelType = 1; + lib.panel.imgView = libSet.albumArtShow = true; + } + // * Split layout presets + else if (grSet.libraryLayout === 'split' && libraryLayoutSplitPresets) { + if (grSet.layout !== 'default') return; + + if (!plSet.show_header) this.updatePlaylist(); + + if (grSet.playlistLayout === 'full') { + grSet.playlistLayout = 'normal'; + } + + if (grSet.libraryLayoutSplitPreset) { + grSet.libraryDesign = 'reborn'; + grSet.libraryThumbnailSize = 'playlist'; + lib.panel.imgView = libSet.albumArtShow = false; + libSet.albumArtLabelType = 1; + plSet.show_header = true; + if (this.displayPlaylist && this.displayLibrary) { + plSet.auto_collapse = true; + pl.playlist.header_auto_collapse(); + } + else { + plSet.auto_collapse = false; + pl.playlist.header_expand(); + } + } + else if (grSet.libraryLayoutSplitPreset2) { + grSet.libraryDesign = 'reborn'; + grSet.libraryThumbnailSize = 'playlist'; + lib.panel.imgView = libSet.albumArtShow = false; + libSet.albumArtLabelType = 1; + plSet.auto_collapse = false; + plSet.show_header = this.displayPlaylist && !this.displayLibrary && grSet.libraryLayout === 'split'; + this.updatePlaylist(); + } + else if (grSet.libraryLayoutSplitPreset3) { + grSet.libraryDesign = 'reborn'; + grSet.libraryThumbnailSize = 'playlist'; + lib.panel.imgView = libSet.albumArtShow = true; + libSet.albumArtLabelType = 1; + plSet.show_header = true; + if (this.displayPlaylist && this.displayLibrary) { + plSet.auto_collapse = true; + pl.playlist.header_auto_collapse(); + } + else { + plSet.auto_collapse = false; + pl.playlist.header_expand(); + } + } + else if (grSet.libraryLayoutSplitPreset4) { + grSet.libraryDesign = 'artistLabelsRight'; + grSet.libraryThumbnailSize = 'playlist'; + lib.panel.imgView = libSet.albumArtShow = true; + libSet.albumArtLabelType = 2; + plSet.show_header = true; + if (this.displayPlaylist && this.displayLibrary) { + plSet.auto_collapse = true; + pl.playlist.header_auto_collapse(); + } + else { + plSet.auto_collapse = false; + pl.playlist.header_expand(); + } + } + pl.call.on_size(this.ww, this.wh); + } + + if (!lib.initialized) return; + setLibraryView(); + this.setLibrarySize(); + grm.theme.initLibraryColors(); + grm.style.initStyleColors(); + grm.theme.themeColorAdjustments(); + window.Repaint(); + } + + /** + * Sets the Library design. + */ + setLibraryDesign() { + const libraryDesign = { + traditional: 0, + modern: 1, + ultraModern: 2, + clean: 3, + facet: 4, + coversLabelsRight: 5, + coversLabelsBottom: 6, + coversLabelsBlend: 7, + artistLabelsRight: 8, + flowMode: 11, + reborn: 12 + }; + + const quickSetupValue = libraryDesign[grSet.libraryDesign]; + lib.panel.set('quickSetup', quickSetupValue); + + if (grSet.libraryDesign === 'flowMode') { + grSet.libraryLayout = 'full'; + } + } + + /** + * Sets the Library size and position. + */ + setLibrarySize() { + if (!lib.initialized) return; + + const noAlbumArtSize = this.wh - this.topMenuHeight - this.lowerBarHeight; + + const x = + grSet.layout === 'artwork' || grSet.libraryLayout !== 'normal' ? 0 : + grSet.panelWidthAuto ? this.displayLibrarySplit() || !fb.IsPlaying ? 0 : this.noAlbumArtStub ? noAlbumArtSize : this.albumArtSize.x + this.albumArtSize.w : + this.ww * 0.5; + + const y = this.topMenuHeight; + + const libraryWidth = + grSet.layout === 'artwork' || grSet.libraryLayout === 'full' ? this.ww : + grSet.panelWidthAuto ? this.displayLibrarySplit() ? noAlbumArtSize : !fb.IsPlaying ? this.ww : this.ww - (this.noAlbumArtStub ? noAlbumArtSize : this.albumArtSize.x + this.albumArtSize.w) : + this.ww * 0.5; + + const libraryHeight = Math.max(0, this.wh - this.lowerBarHeight - y); + + lib.call.on_size(x, y, libraryWidth, libraryHeight); + } + // #endregion + + // * LIBRARY - PUBLIC METHODS - CONTROLS * // + // #region LIBRARY - PUBLIC METHODS - CONTROLS + /** + * Drags and drops items from Library to Playlist in split layout. + */ + librarySplitDragDrop() { + const handleList = lib.pop.getHandleList('newItems'); + lib.pop.sortIfNeeded(handleList); + fb.DoDragDrop(0, handleList, handleList.Count ? 1 | 4 : 0); + + if (plman.IsPlaylistLocked(plman.ActivePlaylist)) return; // Do nothing, it's locked or an auto-playlist + + plman.ClearPlaylistSelection(plman.ActivePlaylist); + + const dropIndex = pl.playlist.selection_handler.last_hover_row.idx; + + setTimeout(() => { + plman.RemovePlaylistSelection(plman.ActivePlaylist); + plman.InsertPlaylistItems(plman.ActivePlaylist, dropIndex, handleList); + plman.SetPlaylistFocusItem(plman.ActivePlaylist, dropIndex); + }, 1); + } + // #endregion + + // * LIBRARY - PUBLIC METHODS - ALBUM ART * // + // #region LIBRARY - PUBLIC METHODS - ALBUM ART + /** + * Dynamically resizes Library album cover thumbnails based on the player size. + */ + autoThumbnailSize() { + if (grSet.libraryThumbnailSize !== 'auto') return; + + const noStd = ['coversLabelsRight', 'artistLabelsRight'].includes(grSet.libraryDesign) || libSet.albumArtLabelType === 2; + const fullW = grSet.libraryLayout === 'full' && grSet.layout === 'default'; + + if (!RES._4K && !RES._QHD) { + if (grSet.layout === 'default' && this.ww < 1600 && this.wh < 960 || grSet.layout === 'artwork' && this.ww < 700 && this.wh < 860) { + libSet.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 0 : // Thumbnail size 'Small' or 'Mini' + grSet.layout === 'artwork' ? 1 : 2; // Thumbnail size 'Small' or 'Regular' + libSet.verticalAlbumArtPad = 2; + } + if (grSet.layout === 'default' && this.ww >= 1600 && this.wh >= 960 || grSet.layout === 'artwork' && this.ww >= 700 && this.wh >= 860) { + libSet.thumbNailSize = noStd && fullW ? 2 : noStd && !fullW ? 1 : // Thumbnail size 'Small' + fullW ? 3 : 3; // Thumbnail size 'Medium' + libSet.verticalAlbumArtPad = 2; + } + if (grSet.layout === 'default' && this.ww >= 1802 && this.wh >= 1061 || grSet.layout === 'artwork' && this.ww >= 901 && this.wh >= 1062) { + libSet.thumbNailSize = noStd && !fullW ? 2 : noStd && fullW ? 3 : // Thumbnail size 'Small' or 'Regular' + fullW ? this.ww === 1802 && this.wh === 1061 ? 5 : 4 : 3; // Thumbnail size 'XL' or 'Large' or 'Medium' + libSet.verticalAlbumArtPad = 2; + } + } + else if (RES._QHD) { + if (grSet.layout === 'default' && this.ww < 1802 && this.wh < 1061 || grSet.layout === 'artwork' && this.ww < 901 && this.wh < 1061) { + libSet.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 0 : // Thumbnail size 'Small' or 'Mini' + grSet.layout === 'artwork' ? 1 : 2; // Thumbnail size 'Small' or 'Regular' + libSet.verticalAlbumArtPad = 2; + } + if (grSet.layout === 'default' && this.ww >= 1802 && this.wh >= 1061 || grSet.layout === 'artwork' && this.ww >= 901 && this.wh >= 1061) { + libSet.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 1 : // Thumbnail size 'Small' + fullW ? 4 : 2; // Thumbnail size 'Medium' or 'Regular' + libSet.verticalAlbumArtPad = 3; + } + if (grSet.layout === 'default' && this.ww >= 2280 && this.wh >= 1300 || grSet.layout === 'artwork' && this.ww >= 1140 && this.wh >= 1300) { + libSet.thumbNailSize = noStd && !fullW ? 2 : noStd && fullW ? 3 : // Thumbnail size 'Small' or 'Regular' + fullW ? 5 : 3; // Thumbnail size 'Large' or 'Medium' + libSet.verticalAlbumArtPad = fullW ? 2 : 3; + } + } + else if (RES._4K) { + if (grSet.layout === 'default' && this.ww < 2800 && this.wh < 1720 || grSet.layout === 'artwork' && this.ww < 1400 && this.wh < 1720) { + libSet.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 0 : // Thumbnail size 'Small' or 'Mini' + fullW ? 2 : 1; // Thumbnail size 'Small' + libSet.verticalAlbumArtPad = 2; + } + if (grSet.layout === 'default' && this.ww >= 2800 && this.wh >= 1720 || grSet.layout === 'artwork' && this.ww >= 1400 && this.wh >= 1720) { + libSet.thumbNailSize = noStd && fullW ? 1 : noStd && !fullW ? 1 : // Thumbnail size 'Small' + fullW ? 3 : 1; // Thumbnail size 'Regular' or 'Small' + libSet.verticalAlbumArtPad = 3; + } + if (grSet.layout === 'default' && this.ww >= 3400 && this.wh >= 2020 || grSet.layout === 'artwork' && this.ww >= 1400 && this.wh >= 1720) { + libSet.thumbNailSize = noStd && !fullW ? 1 : noStd && fullW ? 3 : // Thumbnail size 'Small' or 'Regular' + fullW ? this.ww === 3400 && this.wh === 2020 ? 4 : 4 : 2; // Thumbnail size 'Medium' or 'Regular' + libSet.verticalAlbumArtPad = 2; + } + } + } + // #endregion + + // * BIOGRAPHY - PUBLIC METHODS - INITIALIZATION * // + // #region BIOGRAPHY - PUBLIC METHODS - INITIALIZATION + /** + * Initializes the Biography. + */ + initBiographyPanel() { + if (bio.initialized) return; + bio.ui = new BioUserInterface(); + bio.vk = new BioVkeys(); + bio.panel = new BioPanel(); + bio.name = new BioNames(); + bio.alb_scrollbar = new BioScrollbar(); + bio.art_scrollbar = new BioScrollbar(); + bio.art_scroller = new BioScrollbar(); + bio.cov_scroller = new BioScrollbar(); + bio.but = new BioButtons(); + bio.popUpBox = new BioPopUpBox(); + bio.txt = new BioText(); + bio.tag = new BioTagger(); + bio.resize = new BioResizeHandler(); + bio.lib = new BioLibrary(); + bio.img = new BioImages(); + bio.seeker = new BioSeeker(); + bio.filmStrip = new BioFilmStrip(); + bio.timer = new BioTimers(); + bio.men = new BioMenuItems(); + bio.server = new BioServer(); + bio.infobox = new BioInfobox(); + bio.lyrics = new BioLyrics(); + bio.call = new BioCallbacks(); + bio.initialized = true; + } + + /** + * Initializes active Biography layout presets. + */ + initBiographyLayout() { + if (grSet.biographyLayoutFullPreset) { + bioSet.style = grSet.biographyLayoutFullPreset && grSet.layout === 'default' && grSet.biographyLayout === 'full' ? 3 : 0; + bioSet.showFilmStrip = false; + bioSet.filmStripPos = 3; + } + RepaintWindowRectAreas(); + this.setBiographySize(); + } -////////////////////////////////// -// ! CALL MAIN INITIALIZATION ! // -////////////////////////////////// -if (pref.systemFirstLaunch) { - systemFirstLaunch(); -} else { - initMain(); + /** + * Sets the Biography display layout. + */ + setBiographyDisplay() { + switch (grSet.biographyDisplay) { + case 'Image+text': + bioSet.img_only = false; + bioSet.text_only = false; + break; + case 'Image': + bioSet.img_only = true; + bioSet.text_only = false; + break; + case 'Text': + bioSet.img_only = false; + bioSet.text_only = true; + break; + } + } + + /** + * Sets the Biography size and position. + */ + setBiographySize() { + if (!bio.initialized) return; + + const x = 0; + const y = this.topMenuHeight; + + const biographyWidth = + grSet.layout === 'artwork' || grSet.biographyLayout === 'full' ? this.ww : + grSet.panelWidthAuto ? (!this.albumArt && !this.noAlbumArtStub || !fb.IsPlaying) ? this.ww : this.albumArtSize.x + this.albumArtSize.w : + this.ww * 0.5; + + const biographyHeight = Math.max(0, this.wh - this.lowerBarHeight - y); + + bio.call.on_size(x, y, biographyWidth, biographyHeight); + } + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-menu-context.js b/profile/georgia-reborn/scripts/Base/gr-menu-context.js new file mode 100644 index 00000000..c86903c5 --- /dev/null +++ b/profile/georgia-reborn/scripts/Base/gr-menu-context.js @@ -0,0 +1,1823 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Context Menu Control * // +// * Author: TT * // +// * Org. Author: TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +///////////////////////////// +// * CONTEXT BASE OBJECT * // +///////////////////////////// +/** + * A class that represents the base object for creating context menus. + */ +class ContextBaseObject { + /** + * Creates the `ContextBaseObject` instance. + * @param {string} text_arg - The text value that will be assigned to the `text` property of the object. + */ + constructor(text_arg) { + /** @protected @constant {string} */ + this.text = text_arg; + /** @protected @type {?number} */ + this.idx = undefined; + } + + // * PROTECTED METHODS * // + // #region PROTECTED METHODS + /** + * Initializes the menu with a given parent menu context. This should set up any necessary + * state or sub-menus that are required for the menu to function properly when it is shown. + * This method must be implemented by subclasses to handle their specific initialization logic. + * @abstract + * @param {ContextMenu} parent_menu - The parent of the menu being initialized. + * @protected + */ + initMenu(parent_menu) { + throw new LogicError('initMenu not implemented'); + } + + /** + * Initializes menu items starting at the given index. It's used to populate the menu + * with options or commands starting at a specific point in the menu structure. The method + * should return the index after the last initialized item, allowing for sequential menu + * initialization. This method must be implemented by subclasses to handle their specific + * menu item initialization logic. + * @abstract + * @param {number} start_idx - The index at which the menu should start initializing items. + * @returns {number} The index after the last initialized menu item (end_idx). + * @protected + */ + initMenuIndex(start_idx) { + throw new LogicError('initMenuIndex not implemented'); + } + + /** + * Executes the menu option corresponding to the given index. This method is responsible + * for handling the action that should be taken when a user selects a menu item. Depending + * on the menu option, this may involve running a command, displaying a submenu, or other + * actions. This method must be implemented by subclasses to handle the execution logic + * for their specific menu options. + * @abstract + * @param {number} idx - The index of the menu option that needs to be executed. + * @returns {boolean} True if the execution was successful, false otherwise. + * @protected + */ + executeMenu(idx) { + throw new LogicError('executeMenu not implemented'); + } + // #endregion +} + + +////////////////////// +// * CONTEXT MENU * // +////////////////////// +/** + * A class that provides methods for adding items to the context menu and handling user interactions. + * @augments {ContextBaseObject} + */ +class ContextMenu extends ContextBaseObject { + /** + * Creates the `ContextMenu` instance. + * Initializes properties and creates a context menu. + * @param {string} text_arg - The text value for the constructor. + * @param {object} [optional_args] - Additional parameters that can be passed to the constructor. + * @param {boolean} [optional_args.is_grayed_out] - The item will be grayed out. + * @param {boolean} [optional_args.is_checked] - The item will be checked. + */ + constructor(text_arg, optional_args) { + super(text_arg); + + /** @private @constant {boolean} */ + this.is_grayed_out = !!(optional_args && optional_args.is_grayed_out); + /** @protected @type {array} */ + this.menu_items = []; + + this.cm = window.CreatePopupMenu(); + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Initializes a menu by appending menu items to it and setting their properties based on the state of the parent menu. + * @param {ContextMenu} parent_menu - The menu to which the current menu will be appended. + * @protected + */ + initMenu(parent_menu) { + for (const item of this.menu_items) { + item.initMenu(this); + } + + this.cm.AppendTo(parent_menu.cm, this.is_grayed_out ? MF_GRAYED : MF_STRING, this.text); + } + + /** + * Initializes the menu index for each menu item recursively, starting from a given index. + * @param {number} start_idx - The index at which the menu should start initializing. + * @returns {number} The end_idx. + * @protected + */ + initMenuIndex(start_idx) { + let cur_idx = start_idx; + + this.idx = cur_idx++; + for (const item of this.menu_items) { + if (!item.initMenuIndex) { + continue; + } + cur_idx = item.initMenuIndex(cur_idx); + } + + return cur_idx; + } + + /** + * Adds an item to the "menu_items" array. + * @param {ContextBaseObject} item - The item to be append to the "menu_items" array. + * @throws {InvalidTypeError} - If the provided item is not an instance of ContextBaseObject. + */ + append(item) { + if (!(item instanceof ContextBaseObject)) { + throw new InvalidTypeError('context_item', typeof item, 'instanceof ContextBaseObject'); + } + + this.menu_items.push(item); + } + + /** + * Appends a new ContextItem object to the current context. + * @param {string} text_arg - The text content of the item to be append. + * @param {Function} callback_fn_arg - A function that will be called when the appended item is clicked or activated. + * @param {object} [optional_args] - Additional parameters that can be passed to the function. + * @param {boolean} [optional_args.is_grayed_out] - The item will be grayed out. + * @param {boolean} [optional_args.is_checked] - The item will be checked. + * @param {boolean} [optional_args.is_radio_checked] - The item will be radio checked. + */ + appendItem(text_arg, callback_fn_arg, optional_args) { + this.append(new ContextItem(text_arg, callback_fn_arg, optional_args)); + } + + /** + * Disposes of each item in the menu_items array and sets the menu_items property to null. + */ + dispose() { + this.cm = null; + + const items = this.menu_items; + for (let i = 0; i < items.length; ++i) { + if (items[i].dispose) { + items[i].dispose(); + } + items[i] = null; + } + + this.menu_items = null; + } + + /** + * Checks if the menu_items array is empty. + * @returns {boolean} True or false. + */ + empty() { + return IsEmpty(this.menu_items); + } + + /** + * Executes a menu item based on the given index. + * @param {number} idx - The index of the menu item that needs to be executed. + * @returns {boolean} The result of calling the `executeMenu` method on the menu item that matches the given index (`idx`). + * @protected + */ + executeMenu(idx) { + for (let i = 0; i < this.menu_items.length; ++i) { + const items = this.menu_items; + const item = items[i]; + const next_item = items[i + 1]; + + if (idx === item.idx || (idx > item.idx && (!next_item || idx < next_item.idx))) { + return item.executeMenu(idx); + } + } + return false; + } + + /** + * Checks a specific item in a menu and sets it to a radio checked state. + * @param {number} start_idx - The starting index of the menu_items array where the radioCheck should begin checking. + * @param {number} check_idx - The index of the menu item that you want to perform a radio check on. + * @throws {ArgumentError} If the 'start_idx' is out of bounds. + * @throws {ArgumentError} If the 'check_idx' is out of bounds or if it points to a ContextSeparator. + */ + radioCheck(start_idx, check_idx) { + const item = this.menu_items[start_idx + check_idx]; + if (!item) { + throw new ArgumentError('check_idx', check_idx, 'Value is out of bounds'); + } + + if (start_idx >= this.menu_items.length) { + throw new ArgumentError('start_idx', start_idx, 'Value is out of bounds'); + } + + if (item instanceof ContextSeparator) { + throw new ArgumentError('check_idx', check_idx, 'Index points to MenuSeparator'); + } + + item.radioCheck(true); + } + + /** + * Appends a menu separator to the current context menu. + */ + separator() { + this.append(new ContextSeparator()); + } + // #endregion +} + + +////////////// +// * ITEM * // +////////////// +/** + * A class that handles the menu items in a context menu. + * @augments {ContextBaseObject} + */ +class ContextItem extends ContextBaseObject { + /** + * Creates the `ContextItem` instance. + * Initializes properties and creates a menu item. + * @param {string} text_arg - The text value for the constructor. + * @param {Function} callback_fn_arg - A callback function that will be assigned to the`callback_fn` property. + * @param {object} [optional_args] - Additional parameters that can be passed to the constructor. + * @param {boolean} [optional_args.is_grayed_out] - The item will be grayed out. + * @param {boolean} [optional_args.is_checked] - The item will be checked. + * @param {boolean} [optional_args.is_radio_checked] - The ratio item will be checked. + */ + constructor(text_arg, callback_fn_arg, optional_args) { + super(text_arg); + + /** @private @constant {Function} */ + this.callback_fn = callback_fn_arg; + /** @private @constant {boolean} */ + this.is_grayed_out = !!(optional_args && optional_args.is_grayed_out); + /** @private @constant {boolean} */ + this.is_checked = !!(optional_args && optional_args.is_checked); + /** @private @constant {boolean} */ + this.is_radio_checked = !!(optional_args && optional_args.is_radio_checked); + } + + // * PROTECTED METHODS * // + // #region PROTECTED METHODS + /** + * Initializes a menu item by appending it to a parent menu and setting its properties such as grayed out, checked, or radio checked. + * @param {ContextMenu} parent_menu - The menu object that the current menu item belongs to. + * @protected + */ + initMenu(parent_menu) { + parent_menu.cm.AppendMenuItem(this.is_grayed_out ? MF_GRAYED : MF_STRING, this.idx, this.text); + if (this.is_checked) { + parent_menu.cm.CheckMenuItem(this.idx, true); + } + else if (this.is_radio_checked) { + parent_menu.cm.CheckMenuRadioItem(this.idx, this.idx, this.idx); + } + } + + /** + * Initializes a menu index and returns the incremented value. + * @param {number} start_idx - The initial value for the menu index. + * @returns {number} The end_idx. + * @protected + */ + initMenuIndex(start_idx) { + this.idx = start_idx; + return this.idx + 1; + } + + /** + * Executes a menu item's callback function if the provided index matches the stored index. + * @param {number} idx - The index of the menu item that needs to be executed. + * @returns {boolean} True or false. + * @protected + */ + executeMenu(idx) { + if (this.idx !== idx) { + return false; + } + + this.callback_fn(); + return true; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Sets the state of the check mark ✓ in menu items. + * @param {boolean} is_checked_arg - Whether something is checked or not. + */ + check(is_checked_arg) { + this.is_checked = is_checked_arg; + } + + /** + * Sets the state of the radio mark 🔘 in menu items. + * @param {boolean} is_checked_arg - Whether something is checked or not. + */ + radioCheck(is_checked_arg) { + this.is_radio_checked = is_checked_arg; + } + // #endregion +} + + +/////////////////// +// * SEPARATOR * // +/////////////////// +/** + * A class that handles the separators in a context menu. + * @augments {ContextBaseObject} + */ +class ContextSeparator extends ContextBaseObject { + /** + * Creates the `ContextSeparator` instance. + */ + constructor() { + super(''); + } + + // * PROTECTED METHODS * // + // #region PROTECTED METHODS + /** + * Initializes a menu by appending a separator to the parent menu. + * @param {ContextMenu} parent_menu - The menu to which the new menu item will be added as a child. + * @protected + */ + initMenu(parent_menu) { + parent_menu.cm.AppendMenuSeparator(); + } + + /** + * Initializes a menu index and returns the incremented value. + * @param {number} start_idx - The initial value for the menu index. + * @returns {number} The end_idx. + * @protected + */ + initMenuIndex(start_idx) { + this.idx = start_idx; + return this.idx + 1; + } + + /** + * Execute menu returns false. + * @param {number} idx - The index of the menu item to execute. + * @returns {boolean} False if the menu item was executed successfully, true otherwise. + * @protected + */ + executeMenu(idx) { + return false; + } + // #endregion +} + + +///////////////////// +// * FOOBAR MENU * // +///////////////////// +/** + * A class that handles the foobar2000 context menu. + * @augments {ContextBaseObject} + */ +class ContextFoobarMenu extends ContextBaseObject { + /** + * Creates the `ContextFoobarMenu` instance. + * @param {FbMetadbHandleList} metadb_handles_arg - An array of media database handles. + */ + constructor(metadb_handles_arg) { + super(''); + + /** @private @type {IContextMenuManager} */ + this.cm = fb.CreateContextMenuManager(); + /** @private @type {FbMetadbHandleList} */ + this.metadb_handles = metadb_handles_arg; + } + + // * PROTECTED METHODS * // + // #region PROTECTED METHODS + /** + * Initializes a menu by initializing the context and building the menu items. + * @param {ContextMenu} parent_menu - The parent menu to which the new menu will be added as a child. + * @protected + */ + initMenu(parent_menu) { + this.cm.InitContext(this.metadb_handles); + this.cm.BuildMenu(parent_menu.cm, this.idx); + } + + /** + * Initializes a menu index and returns the index plus 5000. + * @param {number} start_idx - The initial value for the menu index. + * @returns {number} The end_idx. + * @protected + */ + initMenuIndex(start_idx) { + this.idx = start_idx; + return this.idx + 5000; + } + + /** + * Disposes the foobar menu. + * @protected + */ + dispose() { + this.cm = null; + } + + /** + * Executes a menu item based on its index. + * @param {number} idx - The index of the menu that needs to be executed. + * @returns {boolean} The result of executing the command with the specified id. + * @protected + */ + executeMenu(idx) { + return this.cm.ExecuteByID(idx - this.idx); + } + // #endregion +} + + +/////////////////// +// * MAIN MENU * // +/////////////////// +/** + * A class that manages the execution of the main context menu. + * @augments {ContextMenu} + */ +class ContextMainMenu extends ContextMenu { + /** + * Creates the `ContextMainMenu` instance. + */ + constructor() { + super(''); + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Executes a menu by initializing it, displaying it and executing the selected menu item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True if some item was clicked. + */ + execute(x, y) { + // Initialize menu + let cur_idx = 1; + for (const item of this.menu_items) { + if (!item.initMenuIndex) { + continue; + } + cur_idx = item.initMenuIndex(cur_idx); + } + + for (const item of this.menu_items) { + item.initMenu(this); + } + + // Execute menu + const idx = this.cm.TrackPopupMenu(x, y); + if (!idx) { + return false; + } + + return this.executeMenu(idx); + } + // #endregion +} + + +/////////////////////////// +// * ALL CONTEXT MENUS * // +/////////////////////////// +/** + * A class that holds the collection of all available context menus in the theme. + */ +class ContextMenus { + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Contains some basic and SMP related options. + * Displayed when shift right clicking in playlist panel or playlist manager. + * @param {ContextMenu} cm - The context menu object. + */ + contextMenuDefault(cm) { + if (!cm) { + return; + } + + if (!cm.empty()) { + cm.separator(); + } + + cm.appendItem('Console', () => { + fb.ShowConsole(); + }); + + cm.appendItem('Restart', () => { + fb.RunMainMenuCommand('File/Restart'); + }); + + cm.appendItem('Preferences...', () => { + fb.RunMainMenuCommand('File/Preferences'); + }); + + cm.separator(); + + cm.appendItem('Configure panel...', () => { + window.ShowConfigure(); + }); + + cm.appendItem('Panel properties...', () => { + window.ShowProperties(); + }); + } + + /** + * Contains top bar related options from top menu "Options" for quick access. + * Displayed when right clicking on the top bar. + * @param {ContextMenu} cm - The context menu object. + */ + contextMenuTopBar(cm) { + const updateButtons = () => { + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }; + + const topMenuDisplayMenu = new ContextMenu('Display'); + + // * DISPLAY - SHOW TOP MENU BUTTONS - DEFAULT * // + const topMenuDisplayMenuDefault = new ContextMenu('Default'); + topMenuDisplayMenuDefault.appendItem('Details', () => { + grSet.showPanelDetails_default = !grSet.showPanelDetails_default; + updateButtons(); + }, { is_checked: grSet.showPanelDetails_default }); + topMenuDisplayMenuDefault.appendItem('Library', () => { + grSet.showPanelLibrary_default = !grSet.showPanelLibrary_default; + updateButtons(); + }, { is_checked: grSet.showPanelLibrary_default }); + topMenuDisplayMenuDefault.appendItem('Biography', () => { + grSet.showPanelBiography_default = !grSet.showPanelBiography_default; + updateButtons(); + }, { is_checked: grSet.showPanelBiography_default }); + topMenuDisplayMenuDefault.appendItem('Lyrics', () => { + grSet.showPanelLyrics_default = !grSet.showPanelLyrics_default; + updateButtons(); + }, { is_checked: grSet.showPanelLyrics_default }); + topMenuDisplayMenuDefault.appendItem('Rating', () => { + grSet.showPanelRating_default = !grSet.showPanelRating_default; + updateButtons(); + }, { is_checked: grSet.showPanelRating_default }); + topMenuDisplayMenu.append(topMenuDisplayMenuDefault); + + // * DISPLAY - SHOW TOP MENU BUTTONS - ARTWORK * // + const topMenuDisplayMenuArtwork = new ContextMenu('Artwork'); + topMenuDisplayMenuArtwork.appendItem('Details', () => { + grSet.showPanelDetails_artwork = !grSet.showPanelDetails_artwork; + updateButtons(); + }, { is_checked: grSet.showPanelDetails_artwork }); + topMenuDisplayMenuArtwork.appendItem('Library', () => { + grSet.showPanelLibrary_artwork = !grSet.showPanelLibrary_artwork; + updateButtons(); + }, { is_checked: grSet.showPanelLibrary_artwork }); + topMenuDisplayMenuArtwork.appendItem('Biography', () => { + grSet.showPanelBiography_artwork = !grSet.showPanelBiography_artwork; + updateButtons(); + }, { is_checked: grSet.showPanelBiography_artwork }); + topMenuDisplayMenuArtwork.appendItem('Lyrics', () => { + grSet.showPanelLyrics_artwork = !grSet.showPanelLyrics_artwork; + updateButtons(); + }, { is_checked: grSet.showPanelLyrics_artwork }); + topMenuDisplayMenuArtwork.appendItem('Rating', () => { + grSet.showPanelRating_artwork = !grSet.showPanelRating_artwork; + updateButtons(); + }, { is_checked: grSet.showPanelRating_artwork }); + topMenuDisplayMenu.append(topMenuDisplayMenuArtwork); + topMenuDisplayMenu.separator(); + + // * DISPLAY - ALIGN TOP MENU BUTTONS * // + const topMenuDisplayAlign = [['Align left', 'left'], ['Align center', 'center']]; + for (const align of topMenuDisplayAlign) { + topMenuDisplayMenu.appendItem(align[0], ((align) => { + grSet.topMenuAlignment = align; + updateButtons(); + }).bind(null, align[1]), { is_radio_checked: align[1] === grSet.topMenuAlignment }); + } + topMenuDisplayMenu.separator(); + topMenuDisplayMenu.appendItem('Compact top menu', () => { + grSet.topMenuCompact = !grSet.topMenuCompact; + if (!grSet.topMenuCompact) { + grm.button.topMenu(false); + } + }, { is_checked: grSet.topMenuCompact }); + + cm.append(topMenuDisplayMenu); + + // * STYLE - TOP MENU BUTTONS * // + const topMenuStyleMenu = new ContextMenu('Style'); + const topMenuButtonStyle = [ + ['Default', 'default'], + ['Filled', 'filled'], + ['Bevel', 'bevel'], + ['Inner', 'inner'], + ['Emboss', 'emboss'], + ['Minimal', 'minimal'] + ]; + for (const style of topMenuButtonStyle) { + topMenuStyleMenu.appendItem(style[0], ((style) => { + grSet.styleTopMenuButtons = style; + if (!grSet.themeSandbox) grSet.savedStyleTopMenuButtons = grSet.styleTopMenuButtons = style; else grSet.styleTopMenuButtons = style; + grm.ui.updateStyle(); + }).bind(null, style[1]), { is_radio_checked: style[1] === grSet.styleTopMenuButtons }); + } + cm.append(topMenuStyleMenu); + } + + /** + * Contains some options not find in top menu "Options" and append panel related top menu "Options" for quick access. + * Displayed when right clicking on the big album art on the left side. + * @param {ContextMenu} cm - The context menu object. + */ + contextMenuAlbumCover(cm) { + if (!grSet.showTransportControls_default) { + cm.appendItem('Stop', () => { + fb.Stop(); + }); + cm.appendItem('Previous', () => { + fb.Prev(); + }); + cm.appendItem(fb.IsPlaying ? 'Pause' : 'Play', () => { + fb.PlayOrPause(); + }); + cm.appendItem('Next', () => { + fb.Next(); + }); + cm.separator(); + + const playbackOrderMenu = new ContextMenu('Playback order'); + const playbackOrderModes = ['default', 'repeatPlaylist', 'repeatTrack', 'shuffle']; + for (const playbackOrder of playbackOrderModes) { + playbackOrderMenu.appendItem(playbackOrder, () => { + switch (playbackOrder) { + case 'default': + grSet.playbackOrder = 'default'; + fb.RunMainMenuCommand('Playback/Order/Default'); + break; + case 'repeatPlaylist': + grSet.playbackOrder = 'repeatPlaylist'; + fb.RunMainMenuCommand('Playback/Order/Repeat (playlist)'); + break; + case 'repeatTrack': + grSet.playbackOrder = 'repeatTrack'; + fb.RunMainMenuCommand('Playback/Order/Repeat (track)'); + break; + case 'shuffle': + grSet.playbackOrder = 'shuffle'; + fb.RunMainMenuCommand('Playback/Order/Shuffle (tracks)'); + break; + } + }, { is_radio_checked: playbackOrder === grSet.playbackOrder }); + } + cm.append(playbackOrderMenu); + cm.separator(); + } + + // * Top menu options - Playlist, Details, Library, Lyrics - context menu + const showPlaylist = grSet.layout === 'artwork' ? grm.ui.displayPlaylistArtwork && !grm.ui.displayLyrics : grm.ui.displayPlaylist && !grm.ui.displayLyrics; + const showDetails = grm.ui.displayDetails; + const showArtworkLayoutAlbumArt = grSet.layout === 'artwork' && !grm.ui.displayPlaylist && !grm.ui.displayPlaylistArtwork && !grm.ui.displayLibrary && !grm.ui.displayBiography && !grm.ui.displayLyrics; + + if (!showArtworkLayoutAlbumArt) { + cm.appendItem(showPlaylist ? 'Playlist options menu' : showDetails ? 'Details options menu' : grm.ui.displayLibrary ? 'Library options menu' : 'Lyrics options menu', () => { + if (showPlaylist) { + grm.topMenu.topMenuOptions(grm.ui.displayBiography ? grm.ui.state.mouse_x * 2 : grm.ui.state.mouse_x, grm.ui.state.mouse_y, true, true); + } + else if (showDetails) { + grm.topMenu.topMenuOptions(grm.ui.state.mouse_x, grm.ui.state.mouse_y, true, false, true); + } + else if (grm.ui.displayLibrary) { + grm.topMenu.topMenuOptions(grm.ui.state.mouse_x, grm.ui.state.mouse_y, true, false, false, true); + } + else if (grm.ui.displayLyrics) { + grm.topMenu.topMenuOptions(grm.ui.state.mouse_x, grm.ui.state.mouse_y, true, false, false, false, false, true); + } + }); + } + + if (grSet.theme === 'random') { + cm.appendItem('Generate new color', () => { + grm.ui.getRandomThemeColorContextMenu = true; + grm.ui.initTheme(); + setTimeout(() => { grm.ui.getRandomThemeColorContextMenu = false }, 200); + }); + cm.separator(); + } + + if (grSet.layout === 'default' && grSet.theme.startsWith('custom')) { + cm.separator(); + cm.appendItem('Edit custom theme', () => { + grm.ui.displayCustomThemeMenu = true; + if (showPlaylist) { + grm.ui.displayPanel('playlist'); + grm.cthMenu.initCustomThemeMenu('pl_bg'); + } + else if (showDetails) { + grm.ui.displayPanel('details'); + grm.cthMenu.initCustomThemeMenu(false, 'main_bg'); + } + else if (grm.ui.displayLibrary) { + grm.ui.displayPanel('library'); + grm.cthMenu.initCustomThemeMenu(false, false, 'lib_bg'); + } + else if (grm.ui.displayLyrics) { + grm.ui.displayPanel('lyrics'); + grm.cthMenu.initCustomThemeMenu(false, 'main_text'); + } + window.Repaint(); + }); + } + + if (showDetails) { + cm.appendItem('Edit metadata grid', () => { + if (grSet.layout === 'default') { + grm.ui.displayMetadataGridMenu = !grm.ui.displayMetadataGridMenu; + if (!grm.ui.displayDetails) { + grm.ui.displayDetails = true; + grm.ui.displayPlaylist = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + } + grm.gridMenu.initMetadataGridMenu(1); + RepaintWindow(); + } else { + fb.ShowPopupMessage(`Metadata grid can only be live edited in default layout:\nOptions > Layout > Default\n\nYou could manually edit your config file while reloading to take effect:\n${grCfg.configPath}\n`, 'Metadata grid live editing'); + } + }); + cm.separator(); + } + + if (grSet.layout === 'default') { + if (grm.ui.displayPlaylist && !grm.ui.displayBiography && !grm.ui.displayLyrics) { + cm.appendItem(grm.ui.displayPlaylist && grSet.playlistLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { + grSet.playlistLayout = grSet.playlistLayout === 'normal' ? 'full' : 'normal'; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + RepaintWindowRectAreas(); + plSet.auto_collapse = false; + pl.playlist.header_expand(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.jSearch.on_size(); + grm.button.initButtonState(); + }); + cm.separator(); + } + else if (grm.ui.displayLibrary) { + cm.separator(); + cm.appendItem(grm.ui.displayLibrary && grSet.libraryLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { + grSet.libraryLayout = grSet.libraryLayout === 'normal' ? 'full' : 'normal'; + grm.ui.displayPlaylist = grSet.libraryLayout === 'split'; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + grm.ui.initLibraryLayout(); + grm.button.initButtonState(); + }); + if (grSet.libraryLayout === 'normal') { + cm.appendItem(grm.ui.displayLibrary && grSet.libraryLayout === 'normal' ? 'Change layout to split' : 'Change layout to normal', () => { + grSet.libraryLayout = grSet.libraryLayout === 'normal' ? 'split' : 'normal'; + grm.ui.displayPlaylist = grSet.libraryLayout === 'split'; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + grm.ui.initLibraryLayout(); + grm.button.initButtonState(); + }); + cm.separator(); + } + } + else if (grm.ui.displayLyrics) { + cm.appendItem(grm.ui.displayLyrics && grSet.lyricsLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { + grSet.lyricsLayout = grSet.lyricsLayout === 'normal' ? 'full' : 'normal'; + grm.ui.displayPlaylist = !grm.ui.displayPlaylist; + grm.ui.lyricsLayoutFullWidth = grSet.lyricsLayout === 'full'; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + RepaintWindowRectAreas(); + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + }); + cm.separator(); + } + } + + cm.appendItem(grSet.layout !== 'artwork' && (grm.ui.displayPlaylist || grm.ui.displayLyrics && grSet.lyricsLayout === 'full') ? 'Details' : 'Playlist', () => { + if (grSet.layout !== 'artwork') { + grm.ui.btn.details.onClick(); + if (grm.ui.displayPlaylist && !grSet.lyricsPanelState) grm.ui.displayLyrics = false; + } + else if (grSet.layout === 'artwork') { + grm.ui.btn.playlistArtworkLayout.onClick(); + if (grm.ui.displayPlaylistArtwork) grm.ui.displayLyrics = false; + } + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + window.Repaint(); + }); + cm.separator(); + + cm.appendItem(grm.ui.displayLyrics ? 'Hide lyrics' : 'Display lyrics', () => { + if (grSet.layout === 'artwork' && grm.ui.displayPlaylist) grm.ui.btn.details.onClick(); + grm.ui.displayLyrics = !grm.ui.displayLyrics; + if (!grm.ui.displayLyrics && grSet.lyricsLayout === 'full' || grm.ui.noAlbumArtStub) { + grm.ui.displayPlaylist = true; + } + if (grm.ui.displayLyrics && grSet.lyricsLayout === 'full') { + grm.ui.displayPlaylist = false; + grm.ui.displayDetails = false; + } + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.lyrics.initLyrics(); + on_playback_seek(); + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + window.Repaint(); + }); + + if (grm.ui.albumArtList.length > 1) { + const loadImage = () => { + setTimeout(() => { + grm.ui.loadImageFromAlbumArtList(grm.ui.albumArtIndex); + if (grSet.theme === 'reborn' || grSet.theme === 'random' || grSet.styleBlackAndWhiteReborn || grSet.styleBlackReborn) { + grm.ui.newTrackFetchingArtwork = true; + grm.color.getThemeColors(grm.ui.albumArt); + grm.ui.initTheme(); + DebugLog('\n>>> initTheme -> Album cover context menu -> Display next/previous artwork <<<\n'); + } + window.Repaint(); + }, !grm.ui.activeMenu); + } + if (grm.ui.albumArtIndex !== grm.ui.albumArtList.length - 1) { + cm.appendItem(fb.IsPlaying ? 'Display next artwork' : '', () => { + grm.ui.albumArtIndex = (grm.ui.albumArtIndex + 1) % grm.ui.albumArtList.length; + loadImage(); + }); + } + if (grm.ui.albumArtIndex !== 0) { + cm.appendItem(fb.IsPlaying ? 'Display previous artwork' : '', () => { + grm.ui.albumArtIndex = (grm.ui.albumArtIndex - 1) % grm.ui.albumArtList.length; + loadImage(); + }); + } + } + + cm.separator(); + + const query = $('$if3(%album artist%, %artist, %composer%)', fb.GetNowPlaying()).replace(/ /g, '%20'); + cm.appendItem('Get disc art', () => { + RunCmd(`https://fanart.tv/?s=${query}§=2`); + }); + + const discArtMenu = new ContextMenu('Disc art placeholder'); + discArtMenu.appendItem('Show placeholder if no disc art found', () => { + grSet.showDiscArtStub = !grSet.showDiscArtStub; + grSet.noDiscArtStub = false; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }, { is_checked: grSet.showDiscArtStub }); + discArtMenu.separator(); + discArtMenu.appendItem('No placeholder', () => { + grSet.noDiscArtStub = !grSet.noDiscArtStub; + grSet.showDiscArtStub = false; + grm.ui.discArt = grm.ui.disposeDiscArt(grm.ui.discArt); + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArray = []; + grm.ui.discArtArrayCover = []; + if (!grSet.noDiscArtStub) grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }, { is_checked: grSet.noDiscArtStub }); + discArtMenu.separator(); + const displayCdArtMenu = [ + ['CD - Album cover', 'cdAlbumCover'], + ['CD - White', 'cdWhite'], + ['CD - Black', 'cdBlack'], + ['CD - Blank', 'cdBlank'], + ['CD - Transparent', 'cdTrans'] + ]; + for (const cdArt of displayCdArtMenu) { + discArtMenu.appendItem(cdArt[0], ((cdArt) => { + grSet.discArtStub = cdArt; + grSet.noDiscArtStub = false; + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArrayCover = []; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }).bind(null, cdArt[1]), { is_radio_checked: cdArt[1] === grSet.discArtStub }); + } + discArtMenu.separator(); + const displayVinylArtMenu = [ + ['Vinyl - Album cover', 'vinylAlbumCover'], + ['Vinyl - White', 'vinylWhite'], + ['Vinyl - Void', 'vinylVoid'], + ['Vinyl - Cold fusion', 'vinylColdFusion'], + ['Vinyl - Ring of fire', 'vinylRingOfFire'], + ['Vinyl - Maple', 'vinylMaple'], + ['Vinyl - Black', 'vinylBlack'], + ['Vinyl - Black hole', 'vinylBlackHole'], + ['Vinyl - Ebony', 'vinylEbony'], + ['Vinyl - Transparent', 'vinylTrans'] + ]; + for (const vinylArt of displayVinylArtMenu) { + discArtMenu.appendItem(vinylArt[0], ((vinylArt) => { + grSet.discArtStub = vinylArt; + grSet.noDiscArtStub = false; + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArrayCover = []; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }).bind(null, vinylArt[1]), { is_radio_checked: vinylArt[1] === grSet.discArtStub }); + } + cm.append(discArtMenu); + cm.separator(); + + cm.appendItem('Open containing folder', () => { + fb.RunContextCommand('Open Containing Folder'); + }); + + cm.appendItem('Properties', () => { + fb.RunContextCommand('Properties'); + }); + + cm.separator(); + + cm.appendItem('Reload theme', () => { + window.Reload(); + }); + } + + /** + * Contains all seekbar ( progress bar, peakmeter bar and waveform bar ) top menu "Options" for quick access. + * Displayed when right clicking on the lower bar. + * @param {ContextMenu} cm - The context menu object. + */ + contextMenuLowerBar(cm) { + const updateButtons = () => { + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }; + + const updateSeekbar = () => { + grm.ui.initMetrics(); + grm.ui.resizeArtwork(true); + RepaintWindow(); + }; + + // * TRANSPORT BUTTON SIZE * // + const transportSizeMenu = new ContextMenu('Transport button size'); + const transportSizeMenuDefault = new ContextMenu('Default'); + const transportSizeDefault = [['28px', 28], ['30px', 30], ['32px (default)', 32], ['34px', 34], ['36px', 36], ['38px', 38], ['40px', 40], ['42px', 42]]; + for (const size of transportSizeDefault) { + transportSizeMenuDefault.appendItem(size[0], ((size) => { + grSet.transportButtonSize_default = size; + if (size === -1) { + grSet.transportButtonSize_default -= 2; + } else if (size === 999) { + grSet.transportButtonSize_default += 2; + } else { + grSet.transportButtonSize_default = size; + } + grm.ui.createFonts(); + grm.ui.resizeArtwork(true); + updateButtons(); + }).bind(null, size[1]), { is_radio_checked: size[1] === grSet.transportButtonSize_default }); + } + transportSizeMenu.append(transportSizeMenuDefault); + + const transportSizeMenuArtwork = new ContextMenu('Artwork'); + const transportSizeArtwork = [['28px', 28], ['30px', 30], ['32px (default)', 32], ['34px', 34], ['36px', 36], ['38px', 38], ['40px', 40], ['42px', 42]]; + for (const size of transportSizeArtwork) { + transportSizeMenuArtwork.appendItem(size[0], ((size) => { + grSet.transportButtonSize_artwork = size; + if (size === -1) { + grSet.transportButtonSize_artwork -= 2; + } else if (size === 999) { + grSet.transportButtonSize_artwork += 2; + } else { + grSet.transportButtonSize_artwork = size; + } + grm.ui.createFonts(); + grm.ui.resizeArtwork(true); + updateButtons(); + }).bind(null, size[1]), { is_radio_checked: size[1] === grSet.transportButtonSize_artwork }); + } + transportSizeMenu.append(transportSizeMenuArtwork); + + const transportSizeMenuCompact = new ContextMenu('Compact'); + const transportSizeCompact = [['28px', 28], ['30px', 30], ['32px (default)', 32], ['34px', 34], ['36px', 36], ['38px', 38], ['40px', 40], ['42px', 42]]; + for (const size of transportSizeCompact) { + transportSizeMenuCompact.appendItem(size[0], ((size) => { + grSet.transportButtonSize_compact = size; + if (size === -1) { + grSet.transportButtonSize_compact -= 2; + } else if (size === 999) { + grSet.transportButtonSize_compact += 2; + } else { + grSet.transportButtonSize_compact = size; + } + grm.ui.createFonts(); + grm.ui.resizeArtwork(true); + updateButtons(); + }).bind(null, size[1]), { is_radio_checked: size[1] === grSet.transportButtonSize_compact }); + } + transportSizeMenu.append(transportSizeMenuCompact); + cm.append(transportSizeMenu); + + // * TRANSPORT BUTTON SPACING * // + const transportSpacingMenu = new ContextMenu('Transport button spacing'); + const transportSpacingMenuDefault = new ContextMenu('Default'); + const transportSpacingDefault = [['-2', -1], ['3px', 3], ['5px (default)', 5], ['7px', 7], ['10px', 10], ['15px', 15], ['+2', 999]]; + for (const spacing of transportSpacingDefault) { + transportSpacingMenuDefault.appendItem(spacing[0], ((spacing) => { + grSet.transportButtonSpacing_default = spacing; + if (spacing === -1) { + grSet.transportButtonSpacing_default -= 2; + } else if (spacing === 999) { + grSet.transportButtonSpacing_default += 2; + } else { + grSet.transportButtonSpacing_default = spacing; + } + grm.ui.updateStyle(); + }).bind(null, spacing[1]), { is_radio_checked: spacing[1] === grSet.transportButtonSpacing_default }); + } + transportSpacingMenu.append(transportSpacingMenuDefault); + + const transportSpacingMenuArtwork = new ContextMenu('Artwork'); + const transportSpacingArtwork = [['-2', -1], ['3px', 3], ['5px (default)', 5], ['7px', 7], ['10px', 10], ['15px', 15], ['+2', 999]]; + for (const spacing of transportSpacingArtwork) { + transportSpacingMenuArtwork.appendItem(spacing[0], ((spacing) => { + grSet.transportButtonSpacing_artwork = spacing; + if (spacing === -1) { + grSet.transportButtonSpacing_artwork -= 2; + } else if (spacing === 999) { + grSet.transportButtonSpacing_artwork += 2; + } else { + grSet.transportButtonSpacing_artwork = spacing; + } + grm.ui.updateStyle(); + }).bind(null, spacing[1]), { is_radio_checked: spacing[1] === grSet.transportButtonSpacing_artwork }); + } + transportSpacingMenu.append(transportSpacingMenuArtwork); + + const transportSpacingMenuCompact = new ContextMenu('Compact'); + const transportSpacingCompact = [['-2', -1], ['3px', 3], ['5px (default)', 5], ['7px', 7], ['10px', 10], ['15px', 15], ['+2', 999]]; + for (const spacing of transportSpacingCompact) { + transportSpacingMenuCompact.appendItem(spacing[0], ((spacing) => { + grSet.transportButtonSpacing_compact = spacing; + if (spacing === -1) { + grSet.transportButtonSpacing_compact -= 2; + } else if (spacing === 999) { + grSet.transportButtonSpacing_compact += 2; + } else { + grSet.transportButtonSpacing_compact = spacing; + } + grm.ui.updateStyle(); + }).bind(null, spacing[1]), { is_radio_checked: spacing[1] === grSet.transportButtonSpacing_compact }); + } + transportSpacingMenu.append(transportSpacingMenuCompact); + cm.append(transportSpacingMenu); + + cm.separator(); + const transportButtonDisplayMenu = new ContextMenu('Display'); + + // * SHOW TRANSPORT CONTROLS * // + const transportControlsMenu = new ContextMenu('Show transport controls'); + transportControlsMenu.appendItem('Default', () => { + grSet.showTransportControls_default = !grSet.showTransportControls_default; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showTransportControls_default }); + transportControlsMenu.appendItem('Artwork', () => { + grSet.showTransportControls_artwork = !grSet.showTransportControls_artwork; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showTransportControls_artwork }); + transportControlsMenu.appendItem('Compact', () => { + grSet.showTransportControls_compact = !grSet.showTransportControls_compact; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showTransportControls_compact }); + transportButtonDisplayMenu.append(transportControlsMenu); + + // * SHOW PLAYBACK ORDER BUTTON * // + const playbackOrderBtnMenu = new ContextMenu('Show playback order button'); + playbackOrderBtnMenu.appendItem('Default', () => { + grSet.showPlaybackOrderBtn_default = !grSet.showPlaybackOrderBtn_default; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showPlaybackOrderBtn_default }); + playbackOrderBtnMenu.appendItem('Artwork', () => { + grSet.showPlaybackOrderBtn_artwork = !grSet.showPlaybackOrderBtn_artwork; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showPlaybackOrderBtn_artwork }); + playbackOrderBtnMenu.appendItem('Compact', () => { + grSet.showPlaybackOrderBtn_compact = !grSet.showPlaybackOrderBtn_compact; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showPlaybackOrderBtn_compact }); + transportButtonDisplayMenu.append(playbackOrderBtnMenu); + + // * SHOW RELOAD BUTTON * // + const reloadBtnMenu = new ContextMenu('Show reload button'); + reloadBtnMenu.appendItem('Default', () => { + grSet.showReloadBtn_default = !grSet.showReloadBtn_default; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showReloadBtn_default }); + reloadBtnMenu.appendItem('Artwork', () => { + grSet.showReloadBtn_artwork = !grSet.showReloadBtn_artwork; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showReloadBtn_artwork }); + reloadBtnMenu.appendItem('Compact', () => { + grSet.showReloadBtn_compact = !grSet.showReloadBtn_compact; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showReloadBtn_compact }); + transportButtonDisplayMenu.append(reloadBtnMenu); + + // * SHOW ADD TRACKS BUTTON * // + const addTrackBtnMenu = new ContextMenu('Show add tracks button'); + addTrackBtnMenu.appendItem('Default', () => { + grSet.showAddTracksBtn_default = !grSet.showAddTracksBtn_default; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showAddTracksBtn_default }); + addTrackBtnMenu.appendItem('Artwork', () => { + grSet.showAddTracksBtn_artwork = !grSet.showAddTracksBtn_artwork; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showAddTracksBtn_artwork }); + addTrackBtnMenu.appendItem('Compact', () => { + grSet.showAddTrackdBtn_compact = !grSet.showAddTrackdBtn_compact; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showAddTrackdBtn_compact }); + transportButtonDisplayMenu.append(addTrackBtnMenu); + + // * SHOW VOLUME BUTTON * // + const volumeBtnMenu = new ContextMenu('Show volume button'); + volumeBtnMenu.appendItem('Default', () => { + grSet.showVolumeBtn_default = !grSet.showVolumeBtn_default; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showVolumeBtn_default }); + volumeBtnMenu.appendItem('Artwork', () => { + grSet.showVolumeBtn_artwork = !grSet.showVolumeBtn_artwork; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showVolumeBtn_artwork }); + volumeBtnMenu.appendItem('Compact', () => { + grSet.showVolumeBtn_compact = !grSet.showVolumeBtn_compact; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.showVolumeBtn_compact }); + volumeBtnMenu.separator(); + volumeBtnMenu.appendItem('Auto-hide bar', () => { + grSet.autoHideVolumeBar = !grSet.autoHideVolumeBar; + grm.ui.resizeArtwork(true); + updateButtons(); + }, { is_checked: grSet.autoHideVolumeBar }); + transportButtonDisplayMenu.append(volumeBtnMenu); + transportButtonDisplayMenu.separator(); + + // * SHOW PLAYBACK TIME IN LOWER BAR * // + const playbackTimeMenu = new ContextMenu('Show playback time'); + playbackTimeMenu.appendItem('Default', () => { + grSet.showPlaybackTime_default = !grSet.showPlaybackTime_default; + updateButtons(); + }, { is_checked: grSet.showPlaybackTime_default }); + playbackTimeMenu.appendItem('Artwork', () => { + grSet.showPlaybackTime_artwork = !grSet.showPlaybackTime_artwork; + updateButtons(); + }, { is_checked: grSet.showPlaybackTime_artwork }); + playbackTimeMenu.appendItem('Compact', () => { + grSet.showPlaybackTime_compact = !grSet.showPlaybackTime_compact; + updateButtons(); + }, { is_checked: grSet.showPlaybackTime_compact }); + transportButtonDisplayMenu.append(playbackTimeMenu); + + // * SHOW ARTIST IN LOWER BAR * // + const showArtistMenu = new ContextMenu('Show artist'); + showArtistMenu.appendItem('Default', () => { + grSet.showLowerBarArtist_default = !grSet.showLowerBarArtist_default; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarArtist_default }); + showArtistMenu.appendItem('Artwork', () => { + grSet.showLowerBarArtist_artwork = !grSet.showLowerBarArtist_artwork; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarArtist_artwork }); + showArtistMenu.appendItem('Compact', () => { + grSet.showLowerBarArtist_compact = !grSet.showLowerBarArtist_compact; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarArtist_compact }); + transportButtonDisplayMenu.append(showArtistMenu); + + // * SHOW TRACK NUMBER IN LOWER BAR * // + const showTrackNumberMenu = new ContextMenu('Show track number'); + showTrackNumberMenu.appendItem('Default', () => { + grSet.showLowerBarTrackNum_default = !grSet.showLowerBarTrackNum_default; + on_metadb_changed(); + RepaintWindow(); + }, { is_checked: grSet.showLowerBarTrackNum_default }); + showTrackNumberMenu.appendItem('Artwork', () => { + grSet.showLowerBarTrackNum_artwork = !grSet.showLowerBarTrackNum_artwork; + on_metadb_changed(); + RepaintWindow(); + }, { is_checked: grSet.showLowerBarTrackNum_artwork }); + showTrackNumberMenu.appendItem('Compact', () => { + grSet.showLowerBarTrackNum_compact = !grSet.showLowerBarTrackNum_compact; + on_metadb_changed(); + RepaintWindow(); + }, { is_checked: grSet.showLowerBarTrackNum_compact }); + transportButtonDisplayMenu.append(showTrackNumberMenu); + + // * SHOW SONG TITLE IN LOWER BAR * // + const showTitleMenu = new ContextMenu('Show song title'); + showTitleMenu.appendItem('Default', () => { + grSet.showLowerBarTitle_default = !grSet.showLowerBarTitle_default; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarTitle_default }); + showTitleMenu.appendItem('Artwork', () => { + grSet.showLowerBarTitle_artwork = !grSet.showLowerBarTitle_artwork; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarTitle_artwork }); + showTitleMenu.appendItem('Compact', () => { + grSet.showLowerBarTitle_compact = !grSet.showLowerBarTitle_compact; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarTitle_compact }); + transportButtonDisplayMenu.append(showTitleMenu); + + // * SHOW COMPOSER IN LOWER BAR * // + const showComposerMenu = new ContextMenu('Show composer'); + showComposerMenu.appendItem('Default', () => { + grSet.showLowerBarComposer_default = !grSet.showLowerBarComposer_default; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarComposer_default }); + showComposerMenu.appendItem('Artwork', () => { + grSet.showLowerBarComposer_artwork = !grSet.showLowerBarComposer_artwork; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarComposer_artwork }); + showComposerMenu.appendItem('Compact', () => { + grSet.showLowerBarComposer_compact = !grSet.showLowerBarComposer_compact; + RepaintWindow(); + }, { is_checked: grSet.showLowerBarComposer_compact }); + transportButtonDisplayMenu.append(showComposerMenu); + + // * SHOW ARTIST COUNTRY FLAGS IN LOWER BAR * // + const showArtistFlagsMenu = new ContextMenu('Show artist country flags'); + showArtistFlagsMenu.appendItem('Default', () => { + grSet.showLowerBarArtistFlags_default = !grSet.showLowerBarArtistFlags_default; + grm.ui.loadCountryFlags(); + RepaintWindow(); + }, { is_checked: grSet.showLowerBarArtistFlags_default }); + showArtistFlagsMenu.appendItem('Artwork', () => { + grSet.showLowerBarArtistFlags_artwork = !grSet.showLowerBarArtistFlags_artwork; + grm.ui.loadCountryFlags(); + RepaintWindow(); + }, { is_checked: grSet.showLowerBarArtistFlags_artwork }); + showArtistFlagsMenu.appendItem('Compact', () => { + grSet.showLowerBarArtistFlags_compact = !grSet.showLowerBarArtistFlags_compact; + grm.ui.loadCountryFlags(); + RepaintWindow(); + }, { is_checked: grSet.showLowerBarArtistFlags_compact }); + transportButtonDisplayMenu.append(showArtistFlagsMenu); + + // * SHOW SOFTWARE VERSION IN LOWER BAR * // + const showSoftwareVersionMenu = new ContextMenu('Show software version'); + showSoftwareVersionMenu.appendItem('Default', () => { + grSet.showLowerBarVersion_default = !grSet.showLowerBarVersion_default; + grm.ui.initMain(); + }, { is_checked: grSet.showLowerBarVersion_default }); + showSoftwareVersionMenu.appendItem('Artwork', () => { + grSet.showLowerBarVersion_artwork = !grSet.showLowerBarVersion_artwork; + grm.ui.initMain(); + }, { is_checked: grSet.showLowerBarVersion_artwork }); + showSoftwareVersionMenu.appendItem('Compact', () => { + grSet.showLowerBarVersion_compact = !grSet.showLowerBarVersion_compact; + grm.ui.initMain(); + }, { is_checked: grSet.showLowerBarVersion_compact }); + transportButtonDisplayMenu.append(showSoftwareVersionMenu); + transportButtonDisplayMenu.separator(); + + // * SHOW PROGRESS BAR * // + const progressBarMenu = new ContextMenu('Show progress bar'); + progressBarMenu.appendItem('Default', () => { + grSet.showProgressBar_default = !grSet.showProgressBar_default; + updateSeekbar(); + }, { is_checked: grSet.showProgressBar_default }); + progressBarMenu.appendItem('Artwork', () => { + grSet.showProgressBar_artwork = !grSet.showProgressBar_artwork; + updateSeekbar(); + }, { is_checked: grSet.showProgressBar_artwork }); + progressBarMenu.appendItem('Compact', () => { + grSet.showProgressBar_compact = !grSet.showProgressBar_compact; + updateSeekbar(); + }, { is_checked: grSet.showProgressBar_compact }); + transportButtonDisplayMenu.append(progressBarMenu); + + // * SHOW PEAKMETER BAR * // + const peakmeterBarMenu = new ContextMenu('Show peakmeter bar'); + peakmeterBarMenu.appendItem('Default', () => { + grSet.showPeakmeterBar_default = !grSet.showPeakmeterBar_default; + updateSeekbar(); + }, { is_checked: grSet.showPeakmeterBar_default }); + peakmeterBarMenu.appendItem('Artwork', () => { + grSet.showPeakmeterBar_artwork = !grSet.showPeakmeterBar_artwork; + updateSeekbar(); + }, { is_checked: grSet.showPeakmeterBar_artwork }); + peakmeterBarMenu.appendItem('Compact', () => { + grSet.showPeakmeterBar_compact = !grSet.showPeakmeterBar_compact; + updateSeekbar(); + }, { is_checked: grSet.showPeakmeterBar_compact }); + transportButtonDisplayMenu.append(peakmeterBarMenu); + + // * SHOW WAVEFORM BAR * // + const waveformBarMenu = new ContextMenu('Show waveform bar'); + waveformBarMenu.appendItem('Default', () => { + grSet.showWaveformBar_default = !grSet.showWaveformBar_default; + updateSeekbar(); + }, { is_checked: grSet.showWaveformBar_default }); + waveformBarMenu.appendItem('Artwork', () => { + grSet.showWaveformBar_artwork = !grSet.showWaveformBar_artwork; + updateSeekbar(); + }, { is_checked: grSet.showWaveformBar_artwork }); + waveformBarMenu.appendItem('Compact', () => { + grSet.showWaveformBar_compact = !grSet.showWaveformBar_compact; + updateSeekbar(); + }, { is_checked: grSet.showWaveformBar_compact }); + transportButtonDisplayMenu.append(waveformBarMenu); + + cm.append(transportButtonDisplayMenu); + cm.separator(); + + // * BUTTON CONTROLS * // + const buttonControlsMenu = new ContextMenu('Controls'); + buttonControlsMenu.appendItem('Add tracks playlist', () => { grm.inputBox.addTracksPlaylist(); }); + buttonControlsMenu.appendItem('Switch to playlist when adding songs', () => { + grSet.addTracksPlaylistSwitch = !grSet.addTracksPlaylistSwitch; + }, { is_checked: grSet.addTracksPlaylistSwitch }); + cm.append(buttonControlsMenu); + cm.separator(); + + // * STYLES - TRANSPORT BUTTONS * // + const transportButtonStyleMenu = new ContextMenu('Style buttons'); + const transportButtonStyles = [ + ['Default', 'default'], + ['Bevel', 'bevel'], + ['Inner', 'inner'], + ['Emboss', 'emboss'], + ['Minimal', 'minimal'] + ]; + for (const style of transportButtonStyles) { + transportButtonStyleMenu.appendItem(style[0], ((style) => { + grSet.styleTransportButtons = style; + if (!grSet.themeSandbox) grSet.savedStyleTransportButtons = grSet.styleTransportButtons = style; else grSet.styleTransportButtons = style; + grm.ui.updateStyle(); + }).bind(null, style[1]), { is_radio_checked: style[1] === grSet.styleTransportButtons }); + } + cm.append(transportButtonStyleMenu); + + // * STYLES - VOLUME BAR * // + const transportVolumeBarStyleMenu = new ContextMenu('Style volume bar'); + const transportVolumeBarStylesDesignMenu = new ContextMenu('Design'); + const transportVolumeBarStylesDesign = [['Default', 'default'], ['Rounded', 'rounded']]; + for (const design of transportVolumeBarStylesDesign) { + transportVolumeBarStylesDesignMenu.appendItem(design[0], ((design) => { + grSet.styleVolumeBarDesign = design; + if (!grSet.themeSandbox) grSet.savedStyleVolumeBarDesign = grSet.styleVolumeBarDesign = design; else grSet.styleVolumeBarDesign = design; + grm.ui.updateStyle(); + }).bind(null, design[1]), { is_radio_checked: design[1] === grSet.styleVolumeBarDesign }); + } + transportVolumeBarStyleMenu.append(transportVolumeBarStylesDesignMenu); + + const transportVolumeBarStylesBgMenu = new ContextMenu('Background'); + const transportVolumeBarStylesBg = [['Default', 'default'], ['Bevel', 'bevel'], ['Inner', 'inner']]; + for (const style of transportVolumeBarStylesBg) { + transportVolumeBarStylesBgMenu.appendItem(style[0], ((style) => { + grSet.styleVolumeBar = style; + if (!grSet.themeSandbox) grSet.savedStyleVolumeBar = grSet.styleVolumeBar = style; else grSet.styleVolumeBar = style; + grm.ui.updateStyle(); + }).bind(null, style[1]), { is_radio_checked: style[1] === grSet.styleVolumeBar }); + } + transportVolumeBarStyleMenu.append(transportVolumeBarStylesBgMenu); + + const transportVolumeBarStylesFillMenu = new ContextMenu('Fill'); + const transportVolumeBarStylesFill = [['Default', 'default'], ['Bevel', 'bevel'], ['Inner', 'inner']]; + for (const style of transportVolumeBarStylesFill) { + transportVolumeBarStylesFillMenu.appendItem(style[0], ((style) => { + grSet.styleVolumeBarFill = style; + if (!grSet.themeSandbox) grSet.savedStyleVolumeBarFill = grSet.styleVolumeBarFill = style; else grSet.styleVolumeBarFill = style; + grm.ui.updateStyle(); + }).bind(null, style[1]), { is_radio_checked: style[1] === grSet.styleVolumeBarFill }); + } + transportVolumeBarStyleMenu.append(transportVolumeBarStylesFillMenu); + cm.append(transportVolumeBarStyleMenu); + } + + /** + * Contains all seekbar ( progress bar, peakmeter bar and waveform bar ) top menu "Options" for quick access. + * Displayed when right clicking on the lower bar. + * @param {ContextMenu} cm - The context menu object. + */ + contextMenuSeekbar(cm) { + const seekbar = [['Progress bar', 'progressbar'], ['Peakmeter bar', 'peakmeterbar'], ['Waveform bar', 'waveformbar']]; + for (const type of seekbar) { + cm.appendItem(type[0], () => { + grSet.seekbar = type[1]; + grm.ui.initMetrics(); + grm.ui.setProgressBarRefresh(); + if (grSet.seekbar === 'waveformbar') grm.waveBar.updateBar(); + RepaintWindow(); + }, { is_radio_checked: type[1] === grSet.seekbar }); + } + + // * PROGRESS BAR * // + if (grSet.seekbar === 'progressbar') { + cm.separator(); + const progressBarStyleMenu = new ContextMenu('Style'); + const progressBarStyle = [['Default', 'default'], ['Rounded', 'rounded'], ['Lines', 'lines'], ['Blocks', 'blocks'], ['Dots', 'dots'], ['Thin', 'thin']]; + for (const sec of progressBarStyle) { + progressBarStyleMenu.appendItem(sec[0], () => { + grSet.styleProgressBarDesign = sec[1]; + grm.ui.initMetrics(); + RepaintWindow(); + }, { is_radio_checked: sec[1] === grSet.styleProgressBarDesign }); + } + cm.append(progressBarStyleMenu); + + const progressBarSeekSpeedMenu = new ContextMenu('Mouse wheel seek speed'); + const progressBarSeekSpeed = [[' 1 sec', 1], [' 2 sec', 2], [' 3 sec', 3], [' 4 sec', 4], [' 5 sec (default)', 5], [' 6 sec', 6], [' 7 sec', 7], [' 8 sec', 8], [' 9 sec', 9], ['10 sec', 10]]; + for (const sec of progressBarSeekSpeed) { + progressBarSeekSpeedMenu.appendItem(sec[0], () => { + grSet.progressBarWheelSeekSpeed = sec[1]; + }, { is_radio_checked: sec[1] === grSet.progressBarWheelSeekSpeed }); + } + cm.append(progressBarSeekSpeedMenu); + + const progressBarRefreshMenu = new ContextMenu('Refresh rate'); + const progressBarRefresh = [['1000 ms (very slow CPU)', 1000], [' 500 ms', 500], [' 333 ms', 333], [' Variable (default)', 'variable'], [' 250 ms', 250], [' 200 ms', 200], [' 150 ms', 150], [' 100 ms', 100], [' 60 ms', 60], [' 30 ms (very fast CPU)', 30]]; + for (const rate of progressBarRefresh) { + progressBarRefreshMenu.appendItem(rate[0], () => { + grSet.progressBarRefreshRate = rate[1]; + grm.ui.setProgressBarRefresh(); + }, { is_radio_checked: rate[1] === grSet.progressBarRefreshRate }); + } + cm.append(progressBarRefreshMenu); + } + // * PEAKMETER BAR * // + else if (grSet.seekbar === 'peakmeterbar') { + cm.separator(); + const peakmeterBarDesignMenu = new ContextMenu('Style'); + const peakmeterBarDesign = [['Horizontal', 'horizontal'], ['Horizontal center', 'horizontal_center'], ['Vertical', 'vertical']]; + for (const design of peakmeterBarDesign) { + peakmeterBarDesignMenu.appendItem(design[0], () => { + grSet.peakmeterBarDesign = design[1]; + grm.peakBar.on_size(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }, { is_radio_checked: design[1] === grSet.peakmeterBarDesign }); + } + cm.append(peakmeterBarDesignMenu); + + if (grSet.peakmeterBarDesign === 'vertical') { + const peakmeterBarVertSizeMenu = new ContextMenu('Size'); + const peakmeterBarVertSize = [[' 0 px', 0], [' 2 px', 2], [' 4 px', 4], [' 6 px', 6], [' 8 px', 8], ['10 px', 10], [grSet.layout !== 'default' ? '12 px (default)' : '12 px', 12], ['14 px', 14], ['16 px', 16], ['18 px', 18], [grSet.layout !== 'default' ? '20 px' : '20 px (default)', 20], ['25 px', 25], ['30 px', 30], ['35 px', 35], ['40 px', 40], ['Minimum', 'min']]; + for (const size of peakmeterBarVertSize) { + peakmeterBarVertSizeMenu.appendItem(size[0], () => { + grSet.peakmeterBarVertSize = size[1]; + grm.peakBar = new PeakmeterBar(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }, { is_radio_checked: size[1] === grSet.peakmeterBarVertSize }); + } + cm.append(peakmeterBarVertSizeMenu); + + const peakmeterBarVertDbRangeMenu = new ContextMenu('Decibel range'); + const peakmeterBarVertDbRange = [['2 to -20 db (default)', 220], ['2 to -15 db', 215], ['2 to -10 db', 210], ['3 to -20 db', 320], ['3 to -15 db', 315], ['3 to -10 db', 310], ['5 to -20 db', 520], ['5 to -15 db', 515], ['5 to -10 db', 510]]; + for (const range of peakmeterBarVertDbRange) { + peakmeterBarVertDbRangeMenu.appendItem(range[0], () => { + grSet.peakmeterBarVertDbRange = range[1]; + grm.peakBar = new PeakmeterBar(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }, { is_radio_checked: range[1] === grSet.peakmeterBarVertDbRange }); + } + cm.append(peakmeterBarVertDbRangeMenu); + } + + const peakmeterBarDisplayMenu = new ContextMenu('Display'); + if (grSet.peakmeterBarDesign === 'horizontal' || grSet.peakmeterBarDesign === 'horizontal_center') { + peakmeterBarDisplayMenu.appendItem('Show over bars', () => { + grSet.peakmeterBarOverBars = !grSet.peakmeterBarOverBars; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarOverBars }); + peakmeterBarDisplayMenu.separator(); + peakmeterBarDisplayMenu.appendItem('Show outer bars', () => { + grSet.peakmeterBarOuterBars = !grSet.peakmeterBarOuterBars; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarOuterBars }); + peakmeterBarDisplayMenu.appendItem('Show outer peaks', () => { + grSet.peakmeterBarOuterPeaks = !grSet.peakmeterBarOuterPeaks; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarOuterPeaks }); + peakmeterBarDisplayMenu.separator(); + peakmeterBarDisplayMenu.appendItem('Show main bars', () => { + grSet.peakmeterBarMainBars = !grSet.peakmeterBarMainBars; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarMainBars }); + peakmeterBarDisplayMenu.appendItem('Show main peaks', () => { + grSet.peakmeterBarMainPeaks = !grSet.peakmeterBarMainPeaks; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarMainPeaks }); + peakmeterBarDisplayMenu.separator(); + peakmeterBarDisplayMenu.appendItem('Show middle bars', () => { + grSet.peakmeterBarMiddleBars = !grSet.peakmeterBarMiddleBars; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarMiddleBars }); + } + + peakmeterBarDisplayMenu.appendItem('Show progress bar', () => { + grSet.peakmeterBarProgBar = !grSet.peakmeterBarProgBar; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarProgBar }); + + if (grSet.peakmeterBarDesign === 'horizontal' || grSet.peakmeterBarDesign === 'horizontal_center') { + peakmeterBarDisplayMenu.separator(); + peakmeterBarDisplayMenu.appendItem('Show gaps', () => { + grSet.peakmeterBarGaps = !grSet.peakmeterBarGaps; + grm.peakBar.on_size(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarGaps }); + peakmeterBarDisplayMenu.appendItem('Show grid', () => { + grSet.peakmeterBarGrid = !grSet.peakmeterBarGrid; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarGrid }); + } + + if (grSet.peakmeterBarDesign === 'vertical') { + peakmeterBarDisplayMenu.appendItem('Show peaks', () => { + grSet.peakmeterBarVertPeaks = !grSet.peakmeterBarVertPeaks; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarVertPeaks }); + peakmeterBarDisplayMenu.appendItem('Show baseline', () => { + grSet.peakmeterBarVertBaseline = !grSet.peakmeterBarVertBaseline; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarVertBaseline }); + } + + peakmeterBarDisplayMenu.appendItem(grSet.layout !== 'default' ? 'Show info (only available in Default layout)' : 'Show info', () => { + grSet.peakmeterBarInfo = !grSet.peakmeterBarInfo; + RepaintWindow(); + }, { is_checked: grSet.peakmeterBarInfo }); + + cm.append(peakmeterBarDisplayMenu); + + const peakmeterBarRefreshMenu = new ContextMenu('Refresh rate'); + const peakmeterBarRefresh = [['200 ms (very slow CPU)', 200], ['150 ms', 150], ['120 ms', 120], ['100 ms', 100], [' 80 ms (default)', 80], [' 60 ms', 60], [' 30 ms (very fast CPU)', 30]]; + for (const rate of peakmeterBarRefresh) { + peakmeterBarRefreshMenu.appendItem(rate[0], () => { + grSet.peakmeterBarRefreshRate = rate[1]; + grm.ui.setProgressBarRefresh(); + }, { is_radio_checked: rate[1] === grSet.peakmeterBarRefreshRate }); + } + cm.append(peakmeterBarRefreshMenu); + } + // * WAVEFORM BAR * // + else if (grSet.seekbar === 'waveformbar') { + cm.separator(); + const waveformBarAnalysisMenu = new ContextMenu('Analysis'); + const waveformBarAnalysis = [['RMS level', 'rms_level'], ['Peak level', 'peak_level'], ['RMS peak', 'rms_peak']]; + for (const type of waveformBarAnalysis) { + waveformBarAnalysisMenu.appendItem(type[0] + (grSet.waveformBarMode === 'ffprobe' ? '' : '\t (ffprobe only)'), () => { + grSet.waveformBarAnalysis = type[1]; + grm.waveBar.updateConfig({ preset: { analysisMode: type[1] } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, { + is_grayed_out: grSet.waveformBarMode !== 'ffprobe', + is_radio_checked: type[1] === grSet.waveformBarAnalysis + } + ); + } + waveformBarAnalysisMenu.separator(); + waveformBarAnalysisMenu.appendItem('Delete analysis files', () => { + const msg = 'Do you want to delete all waveform bar cache?\n\nThis will permanently delete analyzed files.\n\nContinue?\n\n\n'; + + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) DeleteWaveformBarCache(); + }); + }); + waveformBarAnalysisMenu.separator(); + waveformBarAnalysisMenu.appendItem('Auto-delete analysis files', () => { + grSet.waveformBarAutoDelete = !grSet.waveformBarAutoDelete; + grm.waveBar.updateConfig({ analysis: { autoDelete: grSet.waveformBarAutoDelete } }); + }, { is_grayed_out: grSet.waveformBarMode === 'visualizer' }); + cm.append(waveformBarAnalysisMenu); + + const waveformBarModeMenu = new ContextMenu('Mode'); + const waveformBarMode = [['FFprobe', 'ffprobe'], ['Audiowaveform', 'audiowaveform'], ['Visualizer', 'visualizer']]; + if (!IsFile(grm.waveBar.binaries.ffprobe)) { + waveformBarModeMenu.appendItem('Download FFprobe', () => { + grm.waveBar.getFFprobe(); + }); + waveformBarModeMenu.separator(); + } + for (const mode of waveformBarMode) { + const found = IsFile(grm.waveBar.binaries[mode[1]]); + waveformBarModeMenu.appendItem(mode[0] + (found ? '' : '\t(not found)'), () => { + grSet.waveformBarMode = mode[1]; + grm.waveBar.updateConfig({ analysis: { binaryMode: grSet.waveformBarMode } }); + grm.waveBar.updateBar(true); + RepaintWindow(); + }, { + is_grayed_out: !found, + is_radio_checked: mode[1] === grSet.waveformBarMode + } + ); + } + cm.append(waveformBarModeMenu); + + const waveformBarDesignMenu = new ContextMenu('Style'); + const waveformBarDesign = [['Waveform', 'waveform'], ['Bars', 'bars'], ['Dots', 'dots'], ['Halfbars', 'halfbars']]; + for (const design of waveformBarDesign) { + waveformBarDesignMenu.appendItem(design[0], () => { + grSet.waveformBarDesign = design[1]; + grm.waveBar.updateConfig({ preset: { barDesign: design[1] } }); + }, { is_radio_checked: design[1] === grSet.waveformBarDesign }); + } + cm.append(waveformBarDesignMenu); + + const waveformBarSizeMenu = new ContextMenu('Size'); + const waveformBarSizeWaveMenu = new ContextMenu('Waveform'); + const waveformBarSizeWave = [['1', 1], ['2', 2], ['3 (Default)', 3], ['4', 4], ['5', 5]]; + for (const size of waveformBarSizeWave) { + waveformBarSizeWaveMenu.appendItem(size[0], () => { + grSet.waveformBarSizeWave = size[1]; + grm.waveBar.updateConfig({ ui: { sizeWave: size[1] } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, { is_radio_checked: size[1] === grSet.waveformBarSizeWave }); + } + waveformBarSizeMenu.append(waveformBarSizeWaveMenu); + const waveformBarSizeBarsMenu = new ContextMenu('Bars'); + const waveformBarSizeBars = [['1 (Default)', 1], ['2', 2], ['3', 3], ['4', 4], ['5', 5]]; + for (const size of waveformBarSizeBars) { + waveformBarSizeBarsMenu.appendItem(size[0], () => { + grSet.waveformBarSizeBars = size[1]; + grm.waveBar.updateConfig({ ui: { sizeBars: size[1] } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, { is_radio_checked: size[1] === grSet.waveformBarSizeBars }); + } + waveformBarSizeMenu.append(waveformBarSizeBarsMenu); + const waveformBarSizeDotsMenu = new ContextMenu('Dots'); + const waveformBarSizeDots = [['1', 1], ['2 (Default)', 2], ['3', 3], ['4', 4], ['5', 5]]; + for (const size of waveformBarSizeDots) { + waveformBarSizeDotsMenu.appendItem(size[0], () => { + grSet.waveformBarSizeDots = size[1]; + grm.waveBar.updateConfig({ ui: { sizeDots: size[1] } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, { is_radio_checked: size[1] === grSet.waveformBarSizeDots }); + } + waveformBarSizeMenu.append(waveformBarSizeDotsMenu); + const waveformBarSizeHalfMenu = new ContextMenu('Halfbars'); + const waveformBarSizeHalf = [['1', 1], ['2', 2], ['3', 3], ['4 (Default)', 4], ['5', 5]]; + for (const size of waveformBarSizeHalf) { + waveformBarSizeHalfMenu.appendItem(size[0], () => { + grSet.waveformBarSizeHalf = size[1]; + grm.waveBar.updateConfig({ ui: { sizeHalf: size[1] } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, { is_radio_checked: size[1] === grSet.waveformBarSizeHalf }); + } + waveformBarSizeMenu.append(waveformBarSizeHalfMenu); + waveformBarSizeMenu.separator(); + waveformBarSizeMenu.appendItem('Normalize width', () => { + grSet.waveformBarSizeNormalize = !grSet.waveformBarSizeNormalize; + grm.waveBar.updateConfig({ ui: { sizeNormalizeWidth: grSet.waveformBarSizeNormalize } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, { is_checked: grSet.waveformBarSizeNormalize }); + cm.append(waveformBarSizeMenu); + + const waveformBarDisplayMenu = new ContextMenu('Display'); + const waveformBarDisplay = [['Full', 'full'], ['Partial', 'partial']]; + for (const paint of waveformBarDisplay) { + waveformBarDisplayMenu.appendItem(paint[0], () => { + grSet.waveformBarPaint = paint[1]; + grm.waveBar.updateConfig({ preset: { paintMode: paint[1] } }); + }, { is_radio_checked: paint[1] === grSet.waveformBarPaint }); + } + waveformBarDisplayMenu.separator(); + + waveformBarDisplayMenu.appendItem(`Prepaint${grSet.waveformBarPaint === 'full' ? '\t(partial only)' : ''}`, () => { + grSet.waveformBarPrepaint = !grSet.waveformBarPrepaint; + grm.waveBar.updateConfig({ preset: { prepaint: grSet.waveformBarPrepaint } }); + }, { + is_grayed_out: grSet.waveformBarPaint === 'full', + is_checked: grSet.waveformBarPrepaint + } + ); + + const waveformBarPrepaintMenuDisabled = grSet.waveformBarPaint === 'full' || grSet.waveformBarMode === 'visualizer' || !grSet.waveformBarPrepaint; + const waveformBarPrepaintMenu = new ContextMenu('Prepaint front', { is_grayed_out: waveformBarPrepaintMenuDisabled }); + const waveformBarPrepaint = [[' 2 secs', 2], [' 5 secs', 5], ['10 secs', 10], [' Full', Infinity]]; + for (const time of waveformBarPrepaint) { + waveformBarPrepaintMenu.appendItem(time[0], () => { + grSet.waveformBarPrepaintFront = time[1]; + grm.waveBar.updateConfig({ preset: { prepaintFront: time[1] } }); + }, { + is_grayed_out: waveformBarPrepaintMenuDisabled, + is_radio_checked: time[1] === grSet.waveformBarPrepaintFront + } + ); + } + waveformBarDisplayMenu.append(waveformBarPrepaintMenu); + waveformBarDisplayMenu.separator(); + + waveformBarDisplayMenu.appendItem('Animate', () => { + grSet.waveformBarAnimate = !grSet.waveformBarAnimate; + grm.waveBar.updateConfig({ preset: { animate: grSet.waveformBarAnimate } }); + }, { is_checked: grSet.waveformBarAnimate }); + + waveformBarDisplayMenu.appendItem(`Use BPM${grSet.waveformBarPaint === 'full' && grSet.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`, () => { + grSet.waveformBarBPM = !grSet.waveformBarBPM; + if (grSet.waveformBarBPM) grSet.waveformBarRefreshRateVar = true; + grm.waveBar.updateConfig({ + preset: { useBPM: grSet.waveformBarBPM }, + ui: { refreshRateVar: grSet.waveformBarRefreshRateVar } + }); + }, { + is_grayed_out: !(grSet.waveformBarPaint === 'partial' && grSet.waveformBarPrepaint || grSet.waveformBarMode === 'visualizer'), + is_checked: grSet.waveformBarBPM + } + ); + + waveformBarDisplayMenu.appendItem('Invert halfbars', () => { + grSet.waveformBarInvertHalfbars = !grSet.waveformBarInvertHalfbars; + grm.waveBar.updateConfig({ preset: { invertHalfbars: grSet.waveformBarInvertHalfbars } }); + }, { is_checked: grSet.waveformBarInvertHalfbars }); + waveformBarDisplayMenu.separator(); + + waveformBarDisplayMenu.appendItem('Show indicator', () => { + grSet.waveformBarIndicator = !grSet.waveformBarIndicator; + grm.waveBar.updateConfig({ preset: { indicator: grSet.waveformBarIndicator } }); + }, { is_checked: grSet.waveformBarIndicator }); + cm.append(waveformBarDisplayMenu); + + const waveformBarRefreshMenuDisabled = !(grSet.waveformBarPaint === 'partial' && grSet.waveformBarPrepaint || grSet.waveformBarMode === 'visualizer'); + const waveformBarRefreshMenu = new ContextMenu(`Refresh rate${grSet.waveformBarPaint === 'full' && grSet.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`, { is_grayed_out: waveformBarRefreshMenuDisabled }); + const waveformBarRefresh = [['1000 ms (very slow CPU)', 1000], [' 500 ms', 500], [' 200 ms', 200], [' 100 ms (default)', 100], [' 80 ms', 80], [' 60 ms', 60], [' 30 ms (very fast CPU)', 30]]; + for (const rate of waveformBarRefresh) { + waveformBarRefreshMenu.appendItem(rate[0], () => { + grSet.waveformBarRefreshRate = rate[1]; + grm.waveBar.updateConfig({ ui: { refreshRate: rate[1] } }); + }, { + is_grayed_out: waveformBarRefreshMenuDisabled, + is_radio_checked: rate[1] === grSet.waveformBarRefreshRate + } + ); + } + waveformBarRefreshMenu.separator(); + waveformBarRefreshMenu.appendItem(' Variable refresh rate', () => { + grSet.waveformBarRefreshRateVar = !grSet.waveformBarRefreshRateVar; + grm.waveBar.updateConfig({ ui: { refreshRateVar: grSet.waveformBarRefreshRateVar } }); + }, { is_checked: grSet.waveformBarRefreshRateVar }); + cm.append(waveformBarRefreshMenu); + } + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Base/gr-menu-custom.js b/profile/georgia-reborn/scripts/Base/gr-menu-custom.js index 8b8aaa10..b0981e15 100644 --- a/profile/georgia-reborn/scripts/Base/gr-menu-custom.js +++ b/profile/georgia-reborn/scripts/Base/gr-menu-custom.js @@ -1,71 +1,100 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Custom Menu * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Custom Menu * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////////////////// -// * CUSTOM MENU VARIABLES * // -/////////////////////////////// -/** @type {number} The virtual key code for the "Copy" command. */ -const VK_COPY = 0x03; - -/** @type {number} The virtual key code for the "Cut" command. */ -const VK_CUT = 0x18; - -/** @type {number} The virtual key code for the "Paste" command. */ -const VK_PASTE = 0x16; - -/** @type {number} The virtual key code for the "Select All" command. */ -const VK_SELECT_ALL = 0x01; - -/** @type {GdiBitmap} The GDI image object for getGraphics. */ -let getImage; - -/** @type {GdiGraphics} The GDI graphics object to calculate text widths. */ -let getGraphics; - - ////////////////////////////// // * CUSTOM MENU CONTROLS * // ////////////////////////////// /** - * Base control for mouse and keyboard events of the custom menu. + * A class that creates the base control for handling mouse and keyboard events for the custom menu. */ -class BaseControl { +class CustomMenu { /** - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} label The menu names. + * Creates the `CustomMenu` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} label - The menu names. */ constructor(x, y, label) { - /** @protected */ this.x = x; - /** @protected */ this.y = y; - /** @protected */ this.label = label; + /** @protected @type {number} */ + this.x = x; + /** @protected @type {number} */ + this.y = y; + /** @protected @type {string} */ + this.label = label; + + /** @protected @type {object} */ this.state = {}; + /** @private @constant @type {number} */ this.doubleClickTime = 300; + /** @private @type {number} */ this.lastClickTime = null; + /** @protected @type {boolean} */ this.focus = false; + /** @protected @type {boolean} */ this.disabled = false; + /** @protected @type {boolean} */ this._hovered = false; - this.font = ft.popup; - this.popupFontSize = pref[`popupFontSize_${pref.layout}`]; + /** @public @type {object} The hovered control object used in custom theme menu. */ + this.hoveredControl = null; + /** @public @type {boolean} The mouse button pressed state, used in custom theme menu. */ + this.mouseIsDown = false; + + /** @protected @type {string} */ + this.font = grFont.popup; + /** @protected @type {string} */ + this.popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + /** @protected @type {boolean} */ this.closeBtn = this.label === '\u2715'; - /** @protected */ this.g = undefined; - if (!getImage) { - getImage = gdi.CreateImage(1, 1); - getGraphics = getImage.GetGraphics(); - } - this.g = getGraphics; + + const gdiService = GdiService.getInstance(); + /** @protected @type {GdiGraphics} */ + this.g = gdiService.getGraphics(); + } + + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS + /** + * Gets the active control. + * @returns {CustomMenu | null} The active control instance or null. + */ + static get activeControl() { + return CustomMenu._activeControl; + } + + /** + * Sets the active control. + * @param {CustomMenu} control - An instance of CustomMenu to set as active. + */ + static set activeControl(control) { + CustomMenu._activeControl = control; + } + + /** + * Gets the control list. + * @returns {CustomMenu | null} The control list instance or null. + */ + static get controlList() { + return CustomMenu._controlList; + } + + /** + * Sets the control list. + * @param {CustomMenu} list - An instance of CustomMenu to set as active. + */ + static set controlList(list) { + CustomMenu._controlList = list; } /** @@ -79,19 +108,20 @@ class BaseControl { /** * Sets the menu button hover state. * If you need to repaint on hovered value changing do override this method in child class. - * @param {boolean} value True or false. + * @param {boolean} value - True or false. */ set hovered(value) { this._hovered = value; } + // #endregion - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Calculates the width of a given text using a specified font and optionally rounds the result. - * @param {string} text The text for which the width will be calculated. - * @param {GdiFont} font The font to use for calculating the width. - * @param {boolean=} [round=false] The rounded value if set. + * @param {string} text - The text for which the width will be calculated. + * @param {GdiFont} font - The font to use for calculating the width. + * @param {boolean} [round] - The rounded value if set. * @returns {number} The text width number of the used font. */ calcTextWidth(text, font, round) { @@ -101,9 +131,9 @@ class BaseControl { /** * Calculates the height of a given text using a specified font and optionally rounds the result. - * @param {string} text The text for which the height will be calculated. - * @param {GdiFont} font The font to use for calculating the height. - * @param {boolean=} [round=false] The rounded value if set. + * @param {string} text - The text for which the height will be calculated. + * @param {GdiFont} font - The font to use for calculating the height. + * @param {boolean} [round] - The rounded value if set. * @returns {number} The text height number of the used font. */ calcTextHeight(text, font, round) { @@ -122,103 +152,105 @@ class BaseControl { * Releases the graphics object associated with the getImage method. */ destructor() { - getImage.ReleaseGraphics(this.g); + const gdiService = GdiService.getInstance(); + gdiService.releaseGraphics(this.g); } + // #endregion + // * VIRTUAL METHODS * // + // #region VIRTUAL METHODS /** * A placeholder that handles mouse down events. * These aren't really virtually, just stubbed so that not every child needs to create one if it wants to ignore these events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @virtual + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ mouseDown(x, y) {} /** * A placeholder that handles letter keystrokes events. * These aren't really virtually, just stubbed so that not every child needs to create one if it wants to ignore these events. - * @param {number} code The character code. - * @virtual + * @param {number} code - The character code. */ onChar(code) {} /** * A placeholder that handles keyboard events. * These aren't really virtually, just stubbed so that not every child needs to create one if it wants to ignore these events. - * @param {number} vkey The virtual key code. - * @virtual + * @param {number} vkey - The virtual key code. */ onKey(vkey) {} + // #endregion // * CALLBACKS * // - + // #region CALLBACKS /** * Handles character input events for an active control. - * @param {number} code The character code. + * @param {number} code - The character code. */ on_char(code) { - if (activeControl) { - activeControl.onChar(code); + if (CustomMenu.activeControl) { + CustomMenu.activeControl.onChar(code); } } /** * Handles key down events and calls the onKey method. - * @param {number} vkey The virtual key code. + * @param {number} vkey - The virtual key code. */ on_key_down(vkey) { - if (activeControl) { - activeControl.onKey(vkey); + if (CustomMenu.activeControl) { + CustomMenu.activeControl.onKey(vkey); } } /** * Handles left mouse button down events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ on_mouse_lbtn_down(x, y) { - mouseDown = true; + this.mouseIsDown = true; } /** * Handles left mouse button up and double click events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} m The mouse mask. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} m - The mouse mask. */ on_mouse_lbtn_up(x, y, m) { if (Date.now() - this.lastClickTime > this.doubleClickTime) { this.lastClickTime = Date.now(); - mouseDown = false; + this.mouseIsDown = false; let found = false; - if (activeControl && activeControl instanceof DropDownMenu && activeControl.isSelectUp && activeControl.mouseInThis(x, y)) { - activeControl.clicked(x, y); + if (CustomMenu.activeControl && CustomMenu.activeControl instanceof CustomMenuDropDown && CustomMenu.activeControl.isSelectUp && CustomMenu.activeControl.mouseInThis(x, y)) { + CustomMenu.activeControl.clicked(x, y); found = true; } - for (let i = controlList.length - 1; i >= 0 && !found; i--) { // Reverse order for better z-index handling - if (controlList[i].mouseInThis(x, y)) { - if (activeControl && activeControl !== controlList[i]) activeControl.clearFocus(); - activeControl = controlList[i]; - activeControl.clicked(x, y); + for (let i = CustomMenu.controlList.length - 1; i >= 0 && !found; i--) { // Reverse order for better z-index handling + if (CustomMenu.controlList[i].mouseInThis(x, y)) { + if (CustomMenu.activeControl && CustomMenu.activeControl !== CustomMenu.controlList[i]) CustomMenu.activeControl.clearFocus(); + CustomMenu.activeControl = CustomMenu.controlList[i]; + CustomMenu.activeControl.clicked(x, y); found = true; } } - if (!found && activeControl) { - activeControl.clearFocus(); - activeControl = undefined; + if (!found && CustomMenu.activeControl) { + CustomMenu.activeControl.clearFocus(); + CustomMenu.activeControl = undefined; } } else { this.lastClickTime = Date.now(); - activeControl && activeControl.doubleClicked(x, y); + CustomMenu.activeControl && CustomMenu.activeControl.doubleClicked(x, y); } } /** * Handles mouse movement events and updates the hovered control based on the mouse cursor's current position. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ on_mouse_move(x, y) { if (x === this.state.mouse_x && y === this.state.mouse_y) return; @@ -228,27 +260,28 @@ class BaseControl { let found = false; const setHovered = (control) => { - if (hoveredControl && hoveredControl !== control) hoveredControl.hovered = false; // Clear last hovered control - hoveredControl = control; - hoveredControl.hovered = true; + if (this.hoveredControl && this.hoveredControl !== control) this.hoveredControl.hovered = false; // Clear last hovered control + this.hoveredControl = control; + this.hoveredControl.hovered = true; found = true; }; - if (activeControl && activeControl instanceof DropDownMenu && activeControl.isSelectUp && activeControl.mouseInThis(x, y)) { // handles z-index stuff in a janky way - setHovered(activeControl); + if (CustomMenu.activeControl && CustomMenu.activeControl instanceof CustomMenuDropDown && + CustomMenu.activeControl.isSelectUp && CustomMenu.activeControl.mouseInThis(x, y)) { // handles z-index stuff in a janky way + setHovered(CustomMenu.activeControl); } - for (let i = controlList.length - 1; i >= 0; i--) { // Traverse list in reverse order to better handle z-index issues - if (controlList[i].mouseInThis(x, y)) { - setHovered(controlList[i]); - if (mouseDown) { - controlList[i].mouseDown(x, y); + for (let i = CustomMenu.controlList.length - 1; i >= 0; i--) { // Traverse list in reverse order to better handle z-index issues + if (CustomMenu.controlList[i].mouseInThis(x, y)) { + setHovered(CustomMenu.controlList[i]); + if (this.mouseIsDown) { + CustomMenu.controlList[i].mouseDown(x, y); } break; } } - if (!found && hoveredControl) { - hoveredControl.hovered = false; - hoveredControl = null; + if (!found && this.hoveredControl) { + this.hoveredControl.hovered = false; + this.hoveredControl = null; } } @@ -257,49 +290,70 @@ class BaseControl { */ on_size() { setTimeout(() => { - if (displayCustomThemeMenu) reinitCustomThemeMenu(); - else if (displayMetadataGridMenu) initMetadataGridMenu(); + if (grm.ui.displayCustomThemeMenu) grm.cthMenu.reinitCustomThemeMenu(); + else if (grm.ui.displayMetadataGridMenu) grm.gridMenu.initMetadataGridMenu(); window.Repaint(); }, 0); } + // #endregion } -////////////////////////////// -// * CUSTOM MENU DROPDOWN * // -////////////////////////////// +/////////////////////////////// +// * CUSTOM MENU DROP DOWN * // +/////////////////////////////// /** - * Drop down top navigation of the custom menu. - * @extends {BaseControl} + * A class that creates a dropdown menu for the custom menu. + * @augments {CustomMenu} */ -class DropDownMenu extends BaseControl { +class CustomMenuDropDown extends CustomMenu { /** - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} label The main drop down menu item. - * @param {string[]} labelArray The submenu items of the main. - * @param {number=} [activeIndex=-1] The index and state of the drop down main item. + * Creates the `CustomMenuDropDown` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} label - The main drop down menu item. + * @param {string[]} labelArray - The submenu items of the main. + * @param {number} [activeIndex] - The index and state of the drop down main item. */ constructor(x, y, label, labelArray, activeIndex) { super(x, y, label); - /** @private */ this.labelArray = labelArray; + + /** @private @type {string[]} */ + this.labelArray = labelArray; + /** @private @type {number} */ this.activeIndex = activeIndex || -1; - /** @private @const */ this.padding = SCALE(5); // The padding between top and label and option and bottom line + /** @private @constant @type {number} The padding between top and label and option and bottom line. */ + this.padding = SCALE(5); + /** @private @type {number} */ this.labelW = this.calcTextWidth(label, this.font) + this.padding * 4; + /** @private @type {number} */ this.optionW = this.calcTextWidth(LongestString(this.labelArray), this.font) + this.padding * 4; + /** @private @type {number} */ this.w = Math.max(this.labelW, this.optionW); - /** @private @const */ this.labelHeight = Math.ceil(this.calcTextHeight('Ag', this.font)); - /** @private @const */ this.optionHeight = Math.ceil(this.calcTextHeight('Ag', this.font)); - this.h = this.labelHeight + this.padding * 2; // This.h = this.labelHeight + this.padding + this.optionHeight + this.padding; - /** @private @const */ this.borderPadding = SCALE(5); - /** @private */ this.selectUp = false; - /** @private */ this.selectUpHeight = (this.optionHeight + this.padding * 2) * this.labelArray.length; // Height of select up option - /** @private */ this.selectUpHoveredOption = -1; // When selectUp is visible, which item is hovered - /** @private {number} */ this.selectUpActiveIndex = -1; // This is the index that is active when the selectUp is toggled + /** @private @constant @type {number} */ + this.labelHeight = Math.ceil(this.calcTextHeight('Ag', this.font)); + /** @private @constant @type {number} */ + this.optionHeight = Math.ceil(this.calcTextHeight('Ag', this.font)); + /** @private @type {number} */ + this.h = this.labelHeight + this.padding * 2; + + /** @private @constant */ + this.borderPadding = SCALE(5); + /** @private @type {boolean} */ + this.selectUp = false; + /** @private @type {number} The height of select up option. */ + this.selectUpHeight = (this.optionHeight + this.padding * 2) * this.labelArray.length; + /** @private @type {number} When selectUp is visible, which item is hovered */ + this.selectUpHoveredOption = -1; + /** @private @type {number} This is the index that is active when the selectUp is toggled. */ + this.selectUpActiveIndex = -1; + /** @private @type {number} */ this.selectedColor = RGB(0, 0, 0); } + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS /** * Gets the drop down menu item hover state. * @returns {boolean} True or false. @@ -310,7 +364,7 @@ class DropDownMenu extends BaseControl { /** * Sets the drop down menu item hover state. - * @param {boolean} value + * @param {boolean} value - The hover state to set. */ set hovered(value) { this._hovered = value; @@ -327,52 +381,143 @@ class DropDownMenu extends BaseControl { /** * Sets the drop down menu item selection state. - * @param {boolean} value + * @param {boolean} value - The selection state to set. */ set isSelectUp(value) { this.selectUp = value; } + // #endregion - // * METHODS * // + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Displays and handles the color page of the panel in the custom menu. + * @param {string} label - The current label of the menu item. + * @param {string} labelArray - The panel color page to display. + * @param {number} activeIndex - The index of the currently selected item in the labelArray. + * @private + */ + _buttonHandler(label, labelArray, activeIndex) { + const customThemeMenu = { + Main_Pre: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_pre'); }, + Main_Bg: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_bg'); }, + Main_Bar: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_bar'); }, + Main_Bar2: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_bar2'); }, + Main_Bar3: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_bar3'); }, + Main_Text: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_text'); }, + Main_Btns: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_btns'); }, + Main_Btns2: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_btns2'); }, + Main_Style: () => { grm.ui.displayPanel('details'); grm.cthMenu.initCustomThemeMenu(false, 'main_style'); }, + + Playlist_Bg: () => { grm.ui.displayPanel('playlist'); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Playlist_Text: () => { grm.ui.displayPanel('playlist'); grm.cthMenu.initCustomThemeMenu('pl_text1'); }, + Playlist_Text2: () => { grm.ui.displayPanel('playlist'); grm.cthMenu.initCustomThemeMenu('pl_text2'); }, + Playlist_Misc: () => { grm.ui.displayPanel('playlist'); grm.cthMenu.initCustomThemeMenu('pl_misc'); }, + Playlist_Btns: () => { grm.ui.displayPanel('playlist'); grm.cthMenu.initCustomThemeMenu('pl_btns'); }, + + Library_Bg: () => { grm.ui.displayPanel('library'); grm.cthMenu.initCustomThemeMenu(false, false, 'lib_bg'); }, + Library_Text: () => { grm.ui.displayPanel('library'); grm.cthMenu.initCustomThemeMenu(false, false, 'lib_text'); }, + Library_Node: () => { grm.ui.displayPanel('library'); grm.cthMenu.initCustomThemeMenu(false, false, 'lib_node'); }, + Library_Btns: () => { grm.ui.displayPanel('library'); grm.cthMenu.initCustomThemeMenu(false, false, 'lib_btns'); }, + + Biography_Bg: () => { grm.ui.displayPanel('biography'); grm.cthMenu.initCustomThemeMenu(false, false, false, 'bio_bg'); }, + Biography_Text: () => { grm.ui.displayPanel('biography'); grm.cthMenu.initCustomThemeMenu(false, false, false, 'bio_text'); }, + Biography_Misc: () => { grm.ui.displayPanel('biography'); grm.cthMenu.initCustomThemeMenu(false, false, false, 'bio_misc'); }, + Biography_Btns: () => { grm.ui.displayPanel('biography'); grm.cthMenu.initCustomThemeMenu(false, false, false, 'bio_btns'); }, + + Options_Info: () => { grm.cthMenu.initCustomThemeMenu(false, false, false, false, true); window.Repaint(); }, + Options_Theme01: () => { grSet.theme = 'custom01'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme02: () => { grSet.theme = 'custom02'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme03: () => { grSet.theme = 'custom03'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme04: () => { grSet.theme = 'custom04'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme05: () => { grSet.theme = 'custom05'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme06: () => { grSet.theme = 'custom06'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme07: () => { grSet.theme = 'custom07'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme08: () => { grSet.theme = 'custom08'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme09: () => { grSet.theme = 'custom09'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Theme10: () => { grSet.theme = 'custom10'; grm.ui.initCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); }, + Options_Rename: () => { grm.inputBox.renameCustomTheme(); }, + Options_Reset: () => { grm.ui.initCustomTheme(); grm.cthMenu.resetCustomTheme(); grm.ui.initTheme(); grm.cthMenu.initCustomThemeMenu('pl_bg'); } + }; + + const metadataGridMenu = { + Page1: () => { CustomMenu.activeControl.isSelectUp = false; grm.gridMenu.initMetadataGridMenu(1); }, + Page2: () => { CustomMenu.activeControl.isSelectUp = false; grm.gridMenu.initMetadataGridMenu(2); }, + Page3: () => { CustomMenu.activeControl.isSelectUp = false; grm.gridMenu.initMetadataGridMenu(3); }, + Page4: () => { CustomMenu.activeControl.isSelectUp = false; grm.gridMenu.initMetadataGridMenu(4); }, + Info: () => { grm.gridMenu.initMetadataGridMenu(false, true); window.Repaint(); }, + Reset: () => { grm.gridMenu.resetMetadataGrid(); grm.gridMenu.initMetadataGridMenu(1); } + } + + const closeBtn = { + Close: () => { + CustomMenu.activeControl.isSelectUp = false; + if (grm.ui.displayCustomThemeMenu) grm.ui.displayPanel('playlist'); + grm.ui.displayCustomThemeMenu = false; + grm.ui.displayMetadataGridMenu = false; + } + } + + const index = labelArray[activeIndex]; + const topIndex = label.replace(/\s/g, ''); + const subIndex = `${label}_${index}`.replace(/\s/g, ''); + + // * Button handler + if (grm.ui.displayCustomThemeMenu && customThemeMenu[subIndex]) { + customThemeMenu[subIndex](); + } + else if (grm.ui.displayMetadataGridMenu && (metadataGridMenu[topIndex] || metadataGridMenu[index])) { + if (metadataGridMenu[index]) { + metadataGridMenu[index](); + } else { + metadataGridMenu[topIndex](); + } + } + else if (CustomMenu.activeControl.closeBtn) { + closeBtn.Close(); + } + } + // #endregion + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the drop down menu. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { gr.SetSmoothingMode(SmoothingMode.None); gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); const lightBg = - new Color(col.bg).brightness + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) + new Color(grCol.bg).brightness + grCol.imgBrightness > 285 && (grSet.styleBlend || grSet.styleBlend2) || - new Color(col.bg).brightness > 150 && !pref.styleBlend && !pref.styleBlend2; + new Color(grCol.bg).brightness > 150 && !grSet.styleBlend && !grSet.styleBlend2; const textColor = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); - const lineCorr = this.label === 'Main' ? 2 : 0; const lineHeight = this.hovered || this.focus ? this.h : 1; const optionH = this.optionHeight + this.padding * 2; let optionY = this.getFirstOptionY(); - gr.FillSolidRect(this.x, this.y, this.w, this.h, col.bg); + gr.FillSolidRect(this.x, this.y, this.w, this.h, grCol.bg); if (this.selectUp) { gr.SetSmoothingMode(SmoothingMode.AntiAlias); - gr.FillSolidRect(this.x, this.y, this.w - 1, this.selectUpHeight + this.h, TintColor(col.bg, 10)); - gr.DrawLine(this.x, optionY, this.x + this.w - 1, optionY, 2, ShadeColor(col.bg, 20)); + gr.FillSolidRect(this.x, this.y, this.w - 1, this.selectUpHeight + this.h, TintColor(grCol.bg, 10)); + gr.DrawLine(this.x, optionY, this.x + this.w - 1, optionY, 2, ShadeColor(grCol.bg, 20)); for (const [i, option] of this.labelArray.entries()) { const isActive = this.activeIndex === i; if (isActive || this.selectUpHoveredOption === i) { - const color = isActive ? col.progressBarFill : ShadeColor(col.bg, 10); + const color = isActive ? grCol.progressBarFill : ShadeColor(grCol.bg, 10); gr.FillSolidRect(this.x, optionY, this.w - 1, optionH, color); } - gr.DrawLine(this.x, optionY, this.x + this.w - 1, optionY, 1, ShadeColor(col.bg, 10)); + gr.DrawLine(this.x, optionY, this.x + this.w - 1, optionY, 1, ShadeColor(grCol.bg, 10)); gr.GdiDrawText(option, this.font, textColor, this.x + this.padding * 2, optionY + this.padding, this.w - this.padding * 4, optionH, StringFormat(0, 0, 4)); optionY += optionH; } } else { // Line is not visible if select is up - gr.FillSolidRect(this.x, this.y + this.h - lineHeight, this.w - 1, lineHeight, TintColor(col.bg, 10)); + gr.FillSolidRect(this.x, this.y + this.h - lineHeight, this.w - 1, lineHeight, TintColor(grCol.bg, 10)); } gr.GdiDrawText(this.label, this.font, textColor, this.x + this.padding * 2, this.y + Math.ceil(this.padding / 2), this.w - this.padding * 2, this.h, StringFormat(1, 1)); } @@ -385,14 +530,6 @@ class DropDownMenu extends BaseControl { super.destructor(); } - /** - * Gets the y-coordinate of the main drop down menu item. - * @returns {number} The y-coordinate of the first option. - */ - getFirstOptionY() { - return this.y + this.labelHeight + this.padding * 2; - } - /** * Clears the focus of the main drop down menu item. */ @@ -404,8 +541,8 @@ class DropDownMenu extends BaseControl { /** * Handles click events on a drop down menu item and shows the selected page based on the clicked position. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ clicked(x, y) { if (this.selectUp) { // Made a selection @@ -418,23 +555,31 @@ class DropDownMenu extends BaseControl { this.selectUpActiveIndex = this.activeIndex; this.activeIndex = -1; } - customMenuButtonHandler(this.label, this.labelArray, this.activeIndex); + this._buttonHandler(this.label, this.labelArray, this.activeIndex); window.Repaint(); } /** * Handles double click events on a drop down menu item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ doubleClicked(x, y) { this.clicked(x, y); } + /** + * Gets the y-coordinate of the main drop down menu item. + * @returns {number} The y-coordinate of the first option. + */ + getFirstOptionY() { + return this.y + this.labelHeight + this.padding * 2; + } + /** * Checks if the mouse is over a drop down menu item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { @@ -445,12 +590,12 @@ class DropDownMenu extends BaseControl { } return !this.disabled && ((!this.selectUp && x >= this.x && x <= this.x + this.w && y >= this.y && y <= this.y + this.h) || - (this.selectUp && x >= this.x && x <= this.x + this.w && y >= firstOptionY && y <= firstOptionY + this.selectUpHeight)); + (this.selectUp && x >= this.x && x <= this.x + this.w && y >= firstOptionY && y <= firstOptionY + this.selectUpHeight)); } /** * Handles keyboard events and performs different actions based on the key pressed. - * @param {number} vkey The virtual key code. + * @param {number} vkey - The virtual key code. */ onKey(vkey) { switch (vkey) { @@ -486,48 +631,67 @@ class DropDownMenu extends BaseControl { const repaintY = Math.min(this.getFirstOptionY(), this.y); window.RepaintRect(this.x - this.borderPadding, repaintY - this.borderPadding + this.borderPadding, this.w + this.borderPadding * 2, this.labelHeight + this.selectUpHeight + this.borderPadding * 3 + this.padding); } + // #endregion } -////////////////////////////////// -// * CUSTOM MENU STRING INPUT * // -////////////////////////////////// +///////////////////////////////// +// * CUSTOM MENU INPUT FIELD * // +///////////////////////////////// /** - * The first string input object of the custom menu, draws stored values of variables. - * @extends {BaseControl} + * A class that creates the first input field object of the custom menu. + * @augments {CustomMenu} */ -class StringInput extends BaseControl { +class CustomMenuInputField extends CustomMenu { /** - * Creates a text input field with a label, value, and position in the custom menu. - * @param {string} id The unique identifier for the input field. - * @param {string} label The description for the input field. - * @param {string|number} value The stored value of the input field. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} labelWidth The width of the label area. - * @param {number} inputWidth The width of the input field. + * Creates the `CustomMenuInputField` instance. + * @param {string} id - The unique identifier for the input field. + * @param {string} label - The description for the input field. + * @param {string|number} value - The stored value of the input field. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} labelWidth - The width of the label area. + * @param {number} inputWidth - The width of the input field. */ constructor(id, label, value, x, y, labelWidth, inputWidth) { super(x, y, label); + + /** @private @type {string} */ this.id = id; - /** @private */ this.h = this.calcTextHeight('Ag', this.font); - /** @private */ this.padding = SCALE(3); - /** @private */ this.lineThickness = SCALE(2); - /** @private */ this.inputX = this.x + (displayMetadataGridMenu ? SCALE(20) : this.h); - /** @private */ this.value = value; - /** @private */ this.labelW = labelWidth; - /** @private */ this.inputW = inputWidth - this.padding * 2 + this.popupFontSize; // Subtract out padding - /** @constant @private */ this.cursorRefreshInterval = 350; // Ms - /** @private */ this.timerId = undefined; - /** @private */ this.showCursor = false; - /** @private */ this.selEnd = -1; - /** @private */ this.selAnchor = -1; - /** @private */ this.cursorPos = 0; - /** @private */ this.offsetChars = 0; // Number of chars that are not visible in the input field (scrolled to the left) - } - - /** - * Checks the selection of the string input field. + /** @private */ + this.h = this.calcTextHeight('Ag', this.font); + /** @private @constant @type {number} */ + this.padding = SCALE(3); + /** @private @constant @type {number} */ + this.lineThickness = SCALE(2); + /** @private @type {number} */ + this.inputX = this.x + (grm.ui.displayMetadataGridMenu ? SCALE(20) : this.h); + /** @private @type {string} */ + this.value = value; + /** @private @type {number} */ + this.labelW = labelWidth; + /** @private @type {number} */ + this.inputW = inputWidth - this.padding * 2 + this.popupFontSize; // Subtract out padding + /** @private @constant @type {number} */ + this.cursorRefreshInterval = 350; + /** @private @type {number} */ + this.timerId = undefined; + /** @private @type {boolean} */ + this.showCursor = false; + /** @private @type {number} */ + this.selEnd = -1; + /** @private @type {number} */ + this.selAnchor = -1; + /** @private @type {number} */ + this.cursorPos = 0; + /** @private @type {number} The number of chars that are not visible in the input field (scrolled to the left). */ + this.offsetChars = 0; + } + + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS + /** + * Checks the selection of the input field. * @returns {boolean} True or false. */ get hasSelection() { @@ -535,7 +699,7 @@ class StringInput extends BaseControl { } /** - * Gets the string input field hover state. + * Gets the input field hover state. * @returns {boolean} True or false. */ get hovered() { @@ -543,28 +707,29 @@ class StringInput extends BaseControl { } /** - * Sets the string input field hover state. - * @param {boolean} value + * Sets the input field hover state. + * @param {boolean} value - The input field hover state to set. */ set hovered(value) { this._hovered = value; this.repaint(); } + // #endregion - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** - * Draws the string input field with a label and value. - * @param {GdiGraphics} gr + * Draws the input field with a label and value. + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { gr.SetTextRenderingHint(TextRenderingHint.AntiAlias); const margin = SCALE(20); const textX = this.inputX + this.padding * 4; const lightBg = - new Color(g_pl_colors.bg).brightness + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) + new Color(pl.col.bg).brightness + grCol.imgBrightness > 285 && (grSet.styleBlend || grSet.styleBlend2) || - new Color(g_pl_colors.bg).brightness > 150 && !pref.styleBlend && !pref.styleBlend2; + new Color(pl.col.bg).brightness > 150 && !grSet.styleBlend && !grSet.styleBlend2; const textColor = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); const outlineColor = this.focus ? HEXtoRGB(this.value) : RGB(120, 120, 120); @@ -586,9 +751,9 @@ class StringInput extends BaseControl { const maxWidth = Math.min(this.inputW - (start - textX), end - start); const lightBg = - new Color(HEXtoRGB(this.value)).brightness + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) + new Color(HEXtoRGB(this.value)).brightness + grCol.imgBrightness > 285 && (grSet.styleBlend || grSet.styleBlend2) || - new Color(HEXtoRGB(this.value)).brightness > 150 && !pref.styleBlend && !pref.styleBlend2; + new Color(HEXtoRGB(this.value)).brightness > 150 && !grSet.styleBlend && !grSet.styleBlend2; const textColor = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); @@ -622,7 +787,7 @@ class StringInput extends BaseControl { } /** - * Clears the focus of the string input field. + * Clears the focus of the input field. */ clearFocus() { clearTimeout(this.timerId); @@ -635,8 +800,8 @@ class StringInput extends BaseControl { /** * Handles mouse button click events and updates the cursor position and selection. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ clicked(x, y) { if (!this.mouseInThis(x, y)) return; @@ -656,14 +821,14 @@ class StringInput extends BaseControl { /** * Handles mouse double click events and selects a word or a range of words on a specific position in the text. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ doubleClicked(x, y) { const clickPos = this.getCursorIndex(x); if (this.hasSelection && Math.abs(this.selAnchor - this.selEnd) !== this.value.length && ((clickPos >= this.selAnchor && clickPos <= this.selEnd) || - (clickPos <= this.selAnchor && clickPos >= this.selEnd))) { + (clickPos <= this.selAnchor && clickPos >= this.selEnd))) { this.onChar(VK_SELECT_ALL); } else { this.selAnchor = Math.max(0, this.value.substr(0, clickPos).lastIndexOf(' ') + 1); @@ -675,7 +840,7 @@ class StringInput extends BaseControl { /** * Flashes the mouse cursor and repaints the control. - * @param {boolean=} showImmediate + * @param {boolean} [showImmediate] - If true, the cursor is shown immediately, otherwise the cursor visibility is toggled. * @private */ flashCursor(showImmediate) { @@ -689,7 +854,7 @@ class StringInput extends BaseControl { /** * Calculates the index of the cursor position based on the x-coordinate inside a control. - * @param {number} x The x-coordinate. + * @param {number} x - The x-coordinate. * @returns {number} The index of the cursor position. * @private */ @@ -708,7 +873,7 @@ class StringInput extends BaseControl { /** * Calculates the x-coordinate of the cursor position based on the given index to determine where to draw the cursor. - * @param {number} index The index of the character where the cursor is located. + * @param {number} index - The index of the character where the cursor is located. * @returns {number} The x-position of the cursor at that index. * @private */ @@ -720,9 +885,9 @@ class StringInput extends BaseControl { } /** - * Checks if the mouse is within the boundaries of the string input field. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * Checks if the mouse is within the boundaries of the input field. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { @@ -731,7 +896,7 @@ class StringInput extends BaseControl { /** * Handles various keyboard inputs and performs corresponding actions on the text value. - * @param {number} code The character code. + * @param {number} code - The character code. */ onChar(code) { let clearSelection = true; @@ -742,8 +907,8 @@ class StringInput extends BaseControl { switch (code) { case VK_RETURN: this.value = this.value.substring(0, start) + this.value.substring(end); - if (displayCustomThemeMenu) this.updateColors(); - if (displayMetadataGridMenu) this.updateMetadata(); + if (grm.ui.displayCustomThemeMenu) this.updateColors(); + if (grm.ui.displayMetadataGridMenu) this.updateMetadata(); this.clearFocus(); break; case VK_BACK: @@ -789,8 +954,8 @@ class StringInput extends BaseControl { case VK_PASTE: text = doc.parentWindow.clipboardData.getData('text'); this.value = this.value === '' ? this.value : text; - if (displayCustomThemeMenu) this.updateColors(); - if (displayMetadataGridMenu) this.updateMetadata(); + if (grm.ui.displayCustomThemeMenu) this.updateColors(); + if (grm.ui.displayMetadataGridMenu) this.updateMetadata(); // Fall through default: this.value = this.value.substring(0, start) + text + this.value.substring(end); @@ -808,7 +973,7 @@ class StringInput extends BaseControl { /** * Handles keyboard events and performs different actions based on the key pressed. - * @param {number} vkey The virtual key code. + * @param {number} vkey - The virtual key code. */ onKey(vkey) { const ShiftKeyPressed = utils.IsKeyPressed(VK_SHIFT); @@ -870,7 +1035,7 @@ class StringInput extends BaseControl { } /** - * Updates the state of the string input field. + * Updates the state of the input field. */ repaint() { window.RepaintRect(this.x - 1, this.y - this.padding - 1, this.x + this.labelW + this.inputW + this.padding * 2 + 2, this.h * this.padding * 2 + 2); @@ -878,7 +1043,7 @@ class StringInput extends BaseControl { /** * Returns the clamped value of `index` within the range of 0 and the length of `this.value`. - * @param {number} index The position within a string or array that you want to ensure is within a certain range. + * @param {number} index - The position within a string or array that you want to ensure is within a certain range. * @returns {number} The clamped value. * @private */ @@ -890,10 +1055,10 @@ class StringInput extends BaseControl { * Updates the colors of the current active custom theme. */ updateColors() { - initCustomTheme(); - updateColorsFromConfig(this.id, this.value); - initThemeFull = true; - initTheme(); + grm.ui.initCustomTheme(); + grm.cthMenu.updateCustomThemesConfig(this.id, this.value); + grm.ui.initThemeFull = true; + grm.ui.initTheme(); this.repaint(); } @@ -901,52 +1066,71 @@ class StringInput extends BaseControl { * Updates the metadata grid based on the provided configuration values and repaints the grid. */ updateMetadata() { - updateMetadataGridFromConfig(this.id, this.value, this.value2); - initMetadataGridMenu(); + grm.gridMenu.updateMetadataGridFromConfig(this.id, this.value, this.value2); + grm.gridMenu.grMain.gridMenu.initMetadataGridMenu(); this.repaint(); } + // #endregion } -//////////////////////////////////// -// * CUSTOM MENU STRING INPUT 2 * // -//////////////////////////////////// +/////////////////////////////////// +// * CUSTOM MENU INPUT FIELD 2 * // +/////////////////////////////////// /** - * Second string input field object of the custom menu, used in the metadata grid custom menu. - * @extends {BaseControl} + * A class that creates the second input field object of the custom menu, used in the metadata grid custom menu. + * @augments {CustomMenu} */ -class StringInput2 extends BaseControl { +class CustomMenuInputField2 extends CustomMenu { /** - * Creates a text input field with a label, value, and position in the custom menu. - * @param {string} id The unique identifier for the input field. - * @param {string} label The description for the input field. - * @param {string|number} value The stored value of the input field. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} labelWidth The width of the label area. - * @param {number} inputWidth The width of the input field. + * Creates the `CustomMenuInputField2` instance. + * @param {string} id - The unique identifier for the input field. + * @param {string} label - The description for the input field. + * @param {string|number} value2 - The stored value of the input field. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} labelWidth - The width of the label area. + * @param {number} inputWidth - The width of the input field. */ constructor(id, label, value2, x, y, labelWidth, inputWidth) { super(x, y, label); + + /** @private @type {string} */ this.id = id; - /** @private */ this.h = this.calcTextHeight('Ag', this.font); - /** @private */ this.padding = SCALE(3); - /** @private */ this.lineThickness = SCALE(2); - /** @private */ this.inputX = this.x + this.h + SCALE(18); - /** @private */ this.value2 = value2; - /** @private */ this.labelW = labelWidth; - /** @private */ this.inputW = inputWidth - this.padding * 2; // Subtract out padding - /** @constant @private */ this.cursorRefreshInterval = 350; // Ms - /** @private */ this.timerId = undefined; - /** @private */ this.showCursor = false; - /** @private */ this.selEnd = -1; - /** @private */ this.selAnchor = -1; - /** @private */ this.cursorPos = 0; - /** @private */ this.offsetChars = 0; // Number of chars that are not visible in the string input field (scrolled to the left) - } - - /** - * Checks the selection of the string input field. + /** @private @type {number} */ + this.h = this.calcTextHeight('Ag', this.font); + /** @private @constant @type {number} */ + this.padding = SCALE(3); + /** @private @constant @type {number} */ + this.lineThickness = SCALE(2); + /** @private @type {number} */ + this.inputX = this.x + this.h + SCALE(18); + /** @private @type {string} */ + this.value2 = value2; + /** @private @type {number} */ + this.labelW = labelWidth; + /** @private @type {number} */ + this.inputW = inputWidth - this.padding * 2; // Subtract out padding + /** @private @constant @type {number} */ + this.cursorRefreshInterval = 350; + /** @private @type {number} */ + this.timerId = undefined; + /** @private @type {boolean} */ + this.showCursor = false; + /** @private @type {number} */ + this.selEnd = -1; + /** @private @type {number} */ + this.selAnchor = -1; + /** @private @type {number} */ + this.cursorPos = 0; + /** @private @type {number} The number of chars that are not visible in the input field (scrolled to the left). */ + this.offsetChars = 0; + } + + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS + /** + * Checks the selection of the input field. * @returns {boolean} True or false. */ get hasSelection() { @@ -954,7 +1138,7 @@ class StringInput2 extends BaseControl { } /** - * Gets the string input field hover state. + * Gets the input field hover state. * @returns {boolean} True or false. */ get hovered() { @@ -962,24 +1146,25 @@ class StringInput2 extends BaseControl { } /** - * Sets the string input field hover state. - * @param {boolean} value + * Sets the input field hover state. + * @param {boolean} value - The input field hover state to set. */ set hovered(value) { this._hovered = value; this.repaint(); } + // #endregion - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** - * Draws the string input field with a label and value. - * @param {GdiGraphics} gr + * Draws the input field with a label and value. + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { const margin = SCALE(20); const textX = this.inputX + this.padding * 4; - const outlineColor = this.focus ? col.lowerBarArtist : this.hovered ? RGB(150, 150, 150) : RGB(120, 120, 120); + const outlineColor = this.focus ? grCol.lowerBarArtist : this.hovered ? RGB(150, 150, 150) : RGB(120, 120, 120); gr.SetTextRenderingHint(TextRenderingHint.AntiAlias); gr.FillSolidRect(this.inputX, this.y - this.padding, this.inputW + margin, this.h + this.padding * 2, RGB(255, 255, 255)); @@ -997,9 +1182,9 @@ class StringInput2 extends BaseControl { const end = textX + this.getCursorX(selEndIndex); const maxWidth = Math.min(this.inputW - (start - textX), end - start); const lightBg = - new Color(g_pl_colors.bg).brightness + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) + new Color(pl.col.bg).brightness + grCol.imgBrightness > 285 && (grSet.styleBlend || grSet.styleBlend2) || - new Color(g_pl_colors.bg).brightness > 150 && !pref.styleBlend && !pref.styleBlend2; + new Color(pl.col.bg).brightness > 150 && !grSet.styleBlend && !grSet.styleBlend2; const textColor = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); gr.FillSolidRect(start, this.y, maxWidth, this.h, HEXtoRGB(this.value2)); @@ -1032,21 +1217,21 @@ class StringInput2 extends BaseControl { } /** - * Clears the focus of the string input field. + * Clears the focus of the input field. */ clearFocus() { clearTimeout(this.timerId); this.focus = false; this.showCursor = false; this.offsetChars = 0; - this.selAnchor = -1; // I think we want to do this? + this.selAnchor = -1; // I think we want to do this? this.repaint(); } /** * Handles mouse button click events and updates the cursor position and selection. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ clicked(x, y) { if (!this.mouseInThis(x, y)) return; @@ -1066,8 +1251,8 @@ class StringInput2 extends BaseControl { /** * Handles mouse double click events and selects a word or a range of words on a specific position in the text. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ doubleClicked(x, y) { const clickPos = this.getCursorIndex(x); @@ -1086,8 +1271,8 @@ class StringInput2 extends BaseControl { /** * Flashes the mouse cursor and repaints the control. + * @param {boolean} [showImmediate] - If true, the cursor is shown immediately, otherwise the cursor visibility is toggled. * @private - * @param {boolean=} showImmediate */ flashCursor(showImmediate) { clearTimeout(this.timerId); @@ -1100,7 +1285,7 @@ class StringInput2 extends BaseControl { /** * Calculates the index of the cursor position based on the x-coordinate inside a control. - * @param {number} x The x-coordinate. + * @param {number} x - The x-coordinate. * @returns {number} The index of the cursor position. * @private */ @@ -1119,7 +1304,7 @@ class StringInput2 extends BaseControl { /** * Calculates the x-coordinate of the cursor position based on the given index to determine where to draw the cursor. - * @param {number} index The index of the character where the cursor is located. + * @param {number} index - The index of the character where the cursor is located. * @returns {number} The x-position of the cursor at that index. * @private */ @@ -1131,18 +1316,20 @@ class StringInput2 extends BaseControl { } /** - * Checks if the mouse is within the boundaries of the string input field. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * Checks if the mouse is within the boundaries of the input field. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { - return !this.disabled && x >= this.inputX && x <= this.inputX + this.inputW && y >= this.y - this.padding && y <= this.y + this.h + this.padding; + return !this.disabled && + x >= this.inputX && x <= this.inputX + this.inputW && + y >= this.y - this.padding && y <= this.y + this.h + this.padding; } /** * Handles various keyboard inputs and performs corresponding actions on the text value. - * @param {number} code The character code. + * @param {number} code - The character code. */ onChar(code) { let clearSelection = true; @@ -1200,7 +1387,7 @@ class StringInput2 extends BaseControl { case VK_PASTE: text = doc.parentWindow.clipboardData.getData('text'); this.value2 = this.value2 === '' ? this.value2 : text; - if (displayMetadataGridMenu) this.updateMetadata(); + if (grm.ui.displayMetadataGridMenu) this.updateMetadata(); // Fall through default: this.value2 = this.value2.substring(0, start) + text + this.value2.substring(end); @@ -1218,7 +1405,7 @@ class StringInput2 extends BaseControl { /** * Handles keyboard events and performs different actions based on the key pressed. - * @param {number} vkey The virtual key code. + * @param {number} vkey - The virtual key code. */ onKey(vkey) { const ShiftKeyPressed = utils.IsKeyPressed(VK_SHIFT); @@ -1280,7 +1467,7 @@ class StringInput2 extends BaseControl { } /** - * Updates the state of the string input field. + * Updates the state of the input field. */ repaint() { window.RepaintRect(this.x - 1, this.y - this.padding - 1, this.x + this.labelW + this.inputW + this.padding * 2 + 2, this.h * this.padding * 2 + 2); @@ -1288,7 +1475,7 @@ class StringInput2 extends BaseControl { /** * Returns the clamped value of `index` within the range of 0 and the length of `this.value`. - * @param {number} index The position within a string or array that you want to ensure is within a certain range. + * @param {number} index - The position within a string or array that you want to ensure is within a certain range. * @returns {number} The clamped value. * @private */ @@ -1300,10 +1487,11 @@ class StringInput2 extends BaseControl { * Updates the metadata grid based on the provided configuration values and repaints the grid. */ updateMetadata() { - updateMetadataGridFromConfig(this.id, this.value, this.value2); - initMetadataGridMenu(); + grm.gridMenu.updateMetadataGridFromConfig(this.id, this.value, this.value2); + grm.gridMenu.initMetadataGridMenu(); this.repaint(); } + // #endregion } @@ -1311,35 +1499,44 @@ class StringInput2 extends BaseControl { // * CUSTOM MENU COLOR PICKER * // ////////////////////////////////// /** - * The color picker object of the custom menu, opens utils.ColourPicker. - * @extends {BaseControl} + * A class that creates the color picker object for the custom menu, opens utils.ColourPicker. + * @augments {CustomMenu} */ -class ColorPicker extends BaseControl { +class CustomMenuColorPicker extends CustomMenu { /** - * @param {string} id The id for each individual unique color picker. - * @param {number} value The value of the color will be passed to the color picker. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * Creates the `CustomMenuColorPicker` instance. + * @param {string} id - The id for each individual unique color picker. + * @param {string|number} value - The value of the color will be passed to the color picker. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ constructor(id, value, x, y) { super(x, y); + + /** @private @type {string} */ this.id = id; + /** @private @type {string} */ this.value = value; + /** @private @type {string} */ this.colorPickerColor = value; + /** @private @type {number} */ this.x = x; + /** @private @type {number} */ this.y = y - SCALE(1); + /** @private @type {number} */ this.h = this.calcTextHeight('Ag', this.font) + SCALE(2); + /** @private @type {number} */ this.w = this.h; } - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** - * Draws the color picker left of the string input field. - * @param {GdiGraphics} gr + * Draws the color picker left of the input field. + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { - const lineCol = this.focus ? col.lowerBarArtist : this.hovered ? RGB(150, 150, 150) : RGB(120, 120, 120); + const lineCol = this.focus ? grCol.lowerBarArtist : this.hovered ? RGB(150, 150, 150) : RGB(120, 120, 120); gr.FillSolidRect(this.x, this.y, this.w, this.h, HEXtoRGB(this.value)); gr.DrawRect(this.x, this.y, this.w, this.h, SCALE(2), RGB(255, 255, 255)); gr.DrawRect(this.x - SCALE(2), this.y - SCALE(2), this.w + SCALE(4), this.h + SCALE(4), SCALE(2), lineCol); @@ -1347,8 +1544,8 @@ class ColorPicker extends BaseControl { /** * Updates the color picker color based on the value passed as an argument. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ clicked(x, y) { this.colorPickerColor = utils.ColourPicker(0, HEXtoRGB(this.value)); @@ -1357,8 +1554,8 @@ class ColorPicker extends BaseControl { /** * Checks if the mouse is within the boundaries of the color picker. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { @@ -1376,13 +1573,14 @@ class ColorPicker extends BaseControl { * Updates and writes color to config if the user has selected a color via input or the color picker. */ updateColors() { - initCustomTheme(); + grm.ui.initCustomTheme(); this.value = RGBFtoHEX(this.colorPickerColor); - updateColorsFromConfig(this.id, this.value); - initThemeFull = true; - initTheme(); + grm.cthMenu.updateCustomThemesConfig(this.id, this.value); + grm.ui.initThemeFull = true; + grm.ui.initTheme(); this.repaint(); } + // #endregion } @@ -1390,55 +1588,64 @@ class ColorPicker extends BaseControl { // * CUSTOM MENU COLOR MARKER * // ////////////////////////////////// /** - * The color marker of the custom menu drawn as a lightbulb icon, displays UI elements in red color. - * @extends {BaseControl} + * A class that creates the color marker of the custom menu drawn as a lightbulb icon, displays UI elements in red color. + * @augments {CustomMenu} */ -class ColorMarker extends BaseControl { +class CustomMenuColorMarker extends CustomMenu { /** - * @param {string} id The id for each individual unique color marker. - * @param value The value of the color will be passed to the color marker. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * Creates the `CustomMenuColorMarker` instance. + * @param {string} id - The id for each individual unique color marker. + * @param {string|number} value - The value of the color will be passed to the color marker. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ constructor(id, value, x, y) { super(x, y); + + /** @private @type {string} */ this.id = id; + /** @private @type {string} */ this.value = value; - this.fontAwesome = Font(fontAwesome, this.popupFontSize + 4, 0); + /** @private @type {GdiFont} */ + this.fontAwesome = Font(grFont.fontAwesome, this.popupFontSize + 4, 0); + /** @private @type {number} */ this.x = x; + /** @private @type {number} */ this.y = y - SCALE(1); + /** @private @type {number} */ this.h = this.calcTextHeight('Ag', this.font) + SCALE(2); + /** @private @type {number} */ this.w = this.h; } - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the color marker background field. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { - const lineCol = this.focus ? col.lowerBarArtist : this.hovered ? RGB(150, 150, 150) : RGB(120, 120, 120); + const lineCol = this.focus ? grCol.lowerBarArtist : this.hovered ? RGB(150, 150, 150) : RGB(120, 120, 120); gr.DrawRect(this.x - 2, this.y - 2, this.w + 4, this.h + 4, SCALE(1), lineCol); gr.DrawString('\uF0EB', this.fontAwesome, RGB(0, 0, 0), this.x, this.y, this.w, this.h, StringFormat(1, 1, 4)); } /** * Displays the UI element as an indicator with a red blinking color. - * @param {string} id The id of the string input field. - * @param {number} value The value of the string input field. + * @param {string} id - The id of the input field. + * @param {number} value - The value of the input field. */ colorMarker(id, value) { const showSelColor = () => { setTimeout(() => { - updateColorsFromConfig(id, RGBtoHEX(255, 0, 0)); - initThemeFull = true; - initTheme(); + grm.cthMenu.updateCustomThemesConfig(id, RGBtoHEX(255, 0, 0)); + grm.ui.initThemeFull = true; + grm.ui.initTheme(); }, 0); setTimeout(() => { - updateColorsFromConfig(id, value); - initThemeFull = true; - initTheme(); + grm.cthMenu.updateCustomThemesConfig(id, value); + grm.ui.initThemeFull = true; + grm.ui.initTheme(); }, 200); } setTimeout(() => { showSelColor(); }, 100); @@ -1447,8 +1654,8 @@ class ColorMarker extends BaseControl { /** * Handles mouse click events and activates the color marker. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ clicked(x, y) { this.colorMarker(this.id, this.value); @@ -1456,13 +1663,14 @@ class ColorMarker extends BaseControl { /** * Checks if the mouse is within the boundaries of the color marker. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { return x >= this.x && x <= this.x + this.w && y >= this.y && y <= this.y + this.h; } + // #endregion } @@ -1470,40 +1678,47 @@ class ColorMarker extends BaseControl { // * CUSTOM MENU INFO * // ////////////////////////// /** - * The info page of the custom menu. - * @extends {BaseControl} + * A class that creates the info page in the custom menu. + * @augments {CustomMenu} */ -class Info extends BaseControl { +class CustomMenuInfo extends CustomMenu { /** - * Creates a clickable object with specified dimensions, text, and link. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} text The text content of the button. - * @param {string} link The URL that the button should navigate to when clicked. + * Creates the `CustomMenuInfo` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} text - The text content of the button. + * @param {string} link - The URL that the button should navigate to when clicked. */ constructor(x, y, w, h, text, link) { super(x, y, w, h); + + /** @private @type {number} */ this.x = x; + /** @private @type {number} */ this.y = y; + /** @private @type {number} */ this.w = w; + /** @private @type {number} */ this.h = h; + /** @private @type {string} */ this.text = text; + /** @private @type {string} */ this.link = link; } - // * METHODS * // - + // * PUBLIC METHODS * // + // #region PUBLIC METHODS /** * Draws the info page in the custom menu. - * @param {GdiGraphics} gr + * @param {GdiGraphics} gr - The GDI graphics object. */ draw(gr) { const lightBg = - new Color(g_pl_colors.bg).brightness + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) + new Color(pl.col.bg).brightness + grCol.imgBrightness > 285 && (grSet.styleBlend || grSet.styleBlend2) || - new Color(g_pl_colors.bg).brightness > 150 && !pref.styleBlend && !pref.styleBlend2; + new Color(pl.col.bg).brightness > 150 && !grSet.styleBlend && !grSet.styleBlend2; const textColor = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); gr.DrawString(this.text, this.font, textColor, this.x, this.y, this.w, this.h, StringFormat(0, 0, 4)); @@ -1518,8 +1733,8 @@ class Info extends BaseControl { /** * Handles mouse click events and opens the url when link was clicked. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ clicked(x, y) { if (this.link) { @@ -1530,8 +1745,8 @@ class Info extends BaseControl { /** * Handles mouse double click events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. */ doubleClicked(x, y) { this.clicked(); @@ -1539,106 +1754,14 @@ class Info extends BaseControl { /** * Checks if the mouse is within the boundaries info page. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. * @returns {boolean} True or false. */ mouseInThis(x, y) { return x >= this.x && x <= this.x + this.w && y >= this.y && y <= this.y + this.h; } -} - - -//////////////////////////////////// -// * CUSTOM MENU BUTTON HANDLER * // -//////////////////////////////////// -/** - * Displays and handles the color page of the panel in the custom menu. - * @param {string} label The current label of the menu item. - * @param {string} labelArray The panel color page to display. - * @param {number} activeIndex The index of the currently selected item in the labelArray. - */ -function customMenuButtonHandler(label, labelArray, activeIndex) { - customThemeMenuCall = true; - - const customThemeMenu = { - Main_Pre: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_pre'); }, - Main_Bg: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_bg'); }, - Main_Bar: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_bar'); }, - Main_Bar2: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_bar2'); }, - Main_Bar3: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_bar3'); }, - Main_Text: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_text'); }, - Main_Btns: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_btns'); }, - Main_Btns2: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_btns2'); }, - Main_Style: () => { displayPanel('details'); initCustomThemeMenu(false, 'main_style'); }, - - Playlist_Bg: () => { displayPanel('playlist'); initCustomThemeMenu('pl_bg'); }, - Playlist_Text: () => { displayPanel('playlist'); initCustomThemeMenu('pl_text1'); }, - Playlist_Text2: () => { displayPanel('playlist'); initCustomThemeMenu('pl_text2'); }, - Playlist_Misc: () => { displayPanel('playlist'); initCustomThemeMenu('pl_misc'); }, - Playlist_Btns: () => { displayPanel('playlist'); initCustomThemeMenu('pl_btns'); }, - - Library_Bg: () => { displayPanel('library'); initCustomThemeMenu(false, false, 'lib_bg'); }, - Library_Text: () => { displayPanel('library'); initCustomThemeMenu(false, false, 'lib_text'); }, - Library_Node: () => { displayPanel('library'); initCustomThemeMenu(false, false, 'lib_node'); }, - Library_Btns: () => { displayPanel('library'); initCustomThemeMenu(false, false, 'lib_btns'); }, - - Biography_Bg: () => { displayPanel('biography'); initCustomThemeMenu(false, false, false, 'bio_bg'); }, - Biography_Text: () => { displayPanel('biography'); initCustomThemeMenu(false, false, false, 'bio_text'); }, - Biography_Misc: () => { displayPanel('biography'); initCustomThemeMenu(false, false, false, 'bio_misc'); }, - Biography_Btns: () => { displayPanel('biography'); initCustomThemeMenu(false, false, false, 'bio_btns'); }, - - Options_Info: () => { initCustomThemeMenu(false, false, false, false, true); window.Repaint(); }, - Options_Theme01: () => { pref.theme = 'custom01'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme02: () => { pref.theme = 'custom02'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme03: () => { pref.theme = 'custom03'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme04: () => { pref.theme = 'custom04'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme05: () => { pref.theme = 'custom05'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme06: () => { pref.theme = 'custom06'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme07: () => { pref.theme = 'custom07'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme08: () => { pref.theme = 'custom08'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme09: () => { pref.theme = 'custom09'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Theme10: () => { pref.theme = 'custom10'; initCustomTheme(); initTheme(); initCustomThemeMenu('pl_bg'); }, - Options_Rename: () => { inputBox('renameCustomTheme'); }, - Options_Reset: () => { initCustomTheme(); resetCustomColors(); initTheme(); initCustomThemeMenu('pl_bg'); } - }; - - const metadataGridMenu = { - Page1: () => { activeControl.isSelectUp = false; initMetadataGridMenu(1); }, - Page2: () => { activeControl.isSelectUp = false; initMetadataGridMenu(2); }, - Page3: () => { activeControl.isSelectUp = false; initMetadataGridMenu(3); }, - Page4: () => { activeControl.isSelectUp = false; initMetadataGridMenu(4); }, - Info: () => { initMetadataGridMenu(false, true); window.Repaint(); }, - Reset: () => { resetMetadataGrid(); initMetadataGridMenu(1); } - } - - const closeBtn = { - Close: () => { - activeControl.isSelectUp = false; - if (displayCustomThemeMenu) displayPanel('playlist'); - displayCustomThemeMenu = false; - displayMetadataGridMenu = false; - } - } - - const index = labelArray[activeIndex]; - const topIndex = label.replace(/\s/g, ''); - const subIndex = `${label}_${index}`.replace(/\s/g, ''); - - // * Button handler - if (displayCustomThemeMenu && customThemeMenu[subIndex]) { - customThemeMenu[subIndex](); - } - else if (displayMetadataGridMenu && (metadataGridMenu[topIndex] || metadataGridMenu[index])) { - if (metadataGridMenu[index]) { - metadataGridMenu[index](); - } else { - metadataGridMenu[topIndex](); - } - } - else if (activeControl.closeBtn) { - closeBtn.Close(); - } + // #endregion } @@ -1646,1175 +1769,1123 @@ function customMenuButtonHandler(label, labelArray, activeIndex) { // * CUSTOM THEME MENU * // /////////////////////////// /** - * Draws the custom theme menu. - * @param {GdiGraphics} gr - */ -function drawCustomThemeMenu(gr) { - if (!displayCustomThemeMenu || pref.layout !== 'default') return; - - const x = displayBiography || pref.displayLyrics ? ww * 0.5 : displayDetails ? albumArtSize.x : 0; - const y = geo.topMenuHeight; - const width = !fb.IsPlaying && !displayPlaylist && !displayLibrary && !displayBiography || pref.displayLyrics && !albumArt ? ww : displayDetails ? albumArtSize.w : ww * 0.5; - const height = wh - geo.topMenuHeight - geo.lowerBarHeight; - - gr.SetSmoothingMode(SmoothingMode.None); - gr.FillSolidRect(x, y, width, height, g_pl_colors.bg); - for (const c of controlList) c.draw(gr); - - if (activeControl && activeControl instanceof DropDownMenu && activeControl.isSelectUp) { - activeControl.draw(gr); - } -} - - -/** - * Initializes the custom theme menu with panel sections and options for customizing the theme colors. - * @param {string} playlist_section The playlist color page to be opened. - * @param {string} main_section The main color page to be opened. - * @param {string} library_section The library color page to be opened. - * @param {string} biography_section The biography color page to be opened. - * @param {boolean} info The custom theme menu info page to be opened. + * A class that creates and handles the full custom theme menu. */ -function initCustomThemeMenu(playlist_section, main_section, library_section, biography_section, info) { - if (pref.libraryLayout === 'full') { pref.libraryLayout = 'normal'; setLibrarySize(); } - if (pref.biographyLayout === 'full') { pref.biographyLayout = 'normal'; setBiographySize(); } - if (pref.lyricsLayout === 'full') { pref.lyricsLayout = 'normal'; resizeArtwork(true); } - - controlList = []; - - const margin = SCALE(40); - const baseX = displayBiography || pref.displayLyrics ? ww * 0.5 + margin : !displayPlaylist && !displayLibrary && !displayBiography ? noAlbumArtStub ? ww * 0.3 : albumArtSize.x + margin : margin; - let x = baseX; - let y = geo.topMenuHeight + margin * 0.75; - const w = ww * 0.5; - const h = wh - geo.topMenuHeight - geo.lowerBarHeight; - - const mainSection = ['main_pre', 'main_bg', 'main_bar', 'main_bar2', 'main_bar3', 'main_text', 'main_btns', 'main_btns2', 'main_style'].includes(main_section); - const playlistSection = ['pl_bg', 'pl_text1', 'pl_text2', 'pl_misc', 'pl_btns'].includes(playlist_section); - const librarySection = ['lib_bg', 'lib_text', 'lib_node', 'lib_btns'].includes(library_section); - const biographySection = ['bio_bg', 'bio_text', 'bio_misc', 'bio_btns'].includes(biography_section); - - const menu = new DropDownMenu(x, y, 'Main', ['Pre', 'Bg', 'Bar', 'Bar 2', 'Bar 3', 'Text', 'Btns', 'Btns 2', 'Style'], 0); - controlList.push(menu); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Playlist', ['Bg', 'Text', 'Text 2', 'Misc', 'Btns'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Library', ['Bg', 'Text', 'Node', 'Btns'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Biography', ['Bg', 'Text', 'Misc', 'Btns'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Options', ['Info', '', 'Theme 01', 'Theme 02', 'Theme 03', 'Theme 04', 'Theme 05', 'Theme 06', 'Theme 07', 'Theme 08', 'Theme 09', 'Theme 10', '', 'Rename', 'Reset'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, '\u2715', [''])); - x = baseX; - y += menu.h + margin * 0.75; - - switch (true) { - case playlistSection: customPlaylistColors(x, y, w, h, playlist_section); break; - case mainSection: customMainColors(x, y, w, h, main_section); break; - case librarySection: customLibraryColors(x, y, w, h, library_section); break; - case biographySection: customBiographyColors(x, y, w, h, biography_section); break; - case info: customThemeInfo(x, y, w, h); break; +class CustomThemeMenu { + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * The Main colors in the custom theme menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} main_section - The main color page to be opened. + * @private + */ + _customMainColors(x, y, w, h, main_section) { + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const margin = SCALE(20); + const labelW = SCALE(300) + popupFontSize; + const inputW = SCALE(80) + popupFontSize; + const markerX = x + margin + inputW + (popupFontSize * (RES._4K ? 0.25 : 0.5)) - SCALE(2); + + switch (main_section) { + case 'main_pre': + { + const mainColors = new CustomMenuInputField('main_pre_01', 'grCol.preloaderBg', grCfg.cTheme.grCol_preloaderBg, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_01', grCfg.cTheme.grCol_preloaderBg, x + 2, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_pre_02', 'grCol.preloaderLogo', grCfg.cTheme.grCol_preloaderLogo, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_02', grCfg.cTheme.grCol_preloaderLogo, x + 2, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_pre_03', 'grCol.preloaderLowerBarTitle', grCfg.cTheme.grCol_preloaderLowerBarTitle, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_03', grCfg.cTheme.grCol_preloaderLowerBarTitle, x + 2, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_pre_04', 'grCol.preloaderProgressBar', grCfg.cTheme.grCol_preloaderProgressBar, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_04', grCfg.cTheme.grCol_preloaderProgressBar, x + 2, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_pre_05', 'grCol.preloaderProgressBarFill', grCfg.cTheme.grCol_preloaderProgressBarFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_05', grCfg.cTheme.grCol_preloaderProgressBarFill, x + 2, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_pre_06', 'grCol.preloaderProgressBarFrame', grCfg.cTheme.grCol_preloaderProgressBarFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_06', grCfg.cTheme.grCol_preloaderProgressBarFrame, x + 2, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_pre_07', 'grCol.preloaderUIHacksFrame', grCfg.cTheme.grCol_preloaderUIHacksFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_pre_07', grCfg.cTheme.grCol_preloaderUIHacksFrame, x + 2, y, () => {})); + } + break; + case 'main_bg': + { + const mainColors = new CustomMenuInputField('main_bg_01', 'grCol.bg', grCfg.cTheme.grCol_bg, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bg_01', grCfg.cTheme.grCol_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bg_01', grCfg.cTheme.grCol_bg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bg_02', 'grCol.popupBg', grCfg.cTheme.grCol_popupBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bg_02', grCfg.cTheme.grCol_popupBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bg_02', grCfg.cTheme.grCol_popupBg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bg_03', 'grCol.detailsBg', grCfg.cTheme.grCol_detailsBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bg_03', grCfg.cTheme.grCol_detailsBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bg_03', grCfg.cTheme.grCol_detailsBg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bg_04', 'grCol.shadow', grCfg.cTheme.grCol_shadow, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bg_04', grCfg.cTheme.grCol_shadow, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bg_04', grCfg.cTheme.grCol_shadow, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bg_05', 'grCol.discArtShadow', grCfg.cTheme.grCol_discArtShadow, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bg_05', grCfg.cTheme.grCol_discArtShadow, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bg_05', grCfg.cTheme.grCol_discArtShadow, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bg_06', 'grCol.noAlbumArtStub', grCfg.cTheme.grCol_noAlbumArtStub, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bg_06', grCfg.cTheme.grCol_noAlbumArtStub, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bg_06', grCfg.cTheme.grCol_noAlbumArtStub, markerX, y, () => {})); + } + break; + case 'main_bar': + { + const mainColors = new CustomMenuInputField('main_bar_01', 'grCol.timelineAdded', grCfg.cTheme.grCol_timelineAdded, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_01', grCfg.cTheme.grCol_timelineAdded, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_01', grCfg.cTheme.grCol_timelineAdded, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_02', 'grCol.timelinePlayed', grCfg.cTheme.grCol_timelinePlayed, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_02', grCfg.cTheme.grCol_timelinePlayed, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_02', grCfg.cTheme.grCol_timelinePlayed, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_03', 'grCol.timelineUnplayed', grCfg.cTheme.grCol_timelineUnplayed, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_03', grCfg.cTheme.grCol_timelineUnplayed, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_03', grCfg.cTheme.grCol_timelineUnplayed, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_04', 'grCol.timelineFrame', grCfg.cTheme.grCol_timelineFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_04', grCfg.cTheme.grCol_timelineFrame, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_04', grCfg.cTheme.grCol_timelineFrame, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_05', 'grCol.progressBar', grCfg.cTheme.grCol_progressBar, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_05', grCfg.cTheme.grCol_progressBar, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_05', grCfg.cTheme.grCol_progressBar, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_06', 'grCol.progressBarStreaming', grCfg.cTheme.grCol_progressBarStreaming, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_06', grCfg.cTheme.grCol_progressBarStreaming, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_06', grCfg.cTheme.grCol_progressBarStreaming, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_07', 'grCol.progressBarFrame', grCfg.cTheme.grCol_progressBarFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_07', grCfg.cTheme.grCol_progressBarFrame, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_07', grCfg.cTheme.grCol_progressBarFrame, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_08', 'grCol.progressBarFill', grCfg.cTheme.grCol_progressBarFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_08', grCfg.cTheme.grCol_progressBarFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_08', grCfg.cTheme.grCol_progressBarFill, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_09', 'grCol.volumeBar', grCfg.cTheme.grCol_volumeBar, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_09', grCfg.cTheme.grCol_volumeBar, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_09', grCfg.cTheme.grCol_volumeBar, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_10', 'grCol.volumeBarFrame', grCfg.cTheme.grCol_volumeBarFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_10', grCfg.cTheme.grCol_volumeBarFrame, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_10', grCfg.cTheme.grCol_volumeBarFrame, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_11', 'grCol.volumeBarFill', grCfg.cTheme.grCol_volumeBarFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_11', grCfg.cTheme.grCol_volumeBarFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_11', grCfg.cTheme.grCol_volumeBarFill, markerX, y, () => {})); + } + break; + case 'main_bar2': + { + const mainColors = new CustomMenuInputField('main_bar_12', 'grCol.peakmeterBarProg', grCfg.cTheme.grCol_peakmeterBarProg, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_12', grCfg.cTheme.grCol_peakmeterBarProg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_12', grCfg.cTheme.grCol_peakmeterBarProg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_13', 'grCol.peakmeterBarProgFill', grCfg.cTheme.grCol_peakmeterBarProgFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_13', grCfg.cTheme.grCol_peakmeterBarProgFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_13', grCfg.cTheme.grCol_peakmeterBarProgFill, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_14', 'grCol.peakmeterBarFillTop', grCfg.cTheme.grCol_peakmeterBarFillTop, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_14', grCfg.cTheme.grCol_peakmeterBarFillTop, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_14', grCfg.cTheme.grCol_peakmeterBarFillTop, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_15', 'grCol.peakmeterBarFillMiddle', grCfg.cTheme.grCol_peakmeterBarFillMiddle, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_15', grCfg.cTheme.grCol_peakmeterBarFillMiddle, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_15', grCfg.cTheme.grCol_peakmeterBarFillMiddle, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_16', 'grCol.peakmeterBarFillBack', grCfg.cTheme.grCol_peakmeterBarFillBack, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_16', grCfg.cTheme.grCol_peakmeterBarFillBack, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_16', grCfg.cTheme.grCol_peakmeterBarFillBack, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_17', 'grCol.peakmeterBarVertProgFill', grCfg.cTheme.grCol_peakmeterBarVertProgFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_17', grCfg.cTheme.grCol_peakmeterBarVertProgFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_17', grCfg.cTheme.grCol_peakmeterBarVertProgFill, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_18', 'grCol.peakmeterBarVertFill', grCfg.cTheme.grCol_peakmeterBarVertFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_18', grCfg.cTheme.grCol_peakmeterBarVertFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_18', grCfg.cTheme.grCol_peakmeterBarVertFill, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_19', 'grCol.peakmeterBarVertFillPeaks', grCfg.cTheme.grCol_peakmeterBarVertFillPeaks, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_19', grCfg.cTheme.grCol_peakmeterBarVertFillPeaks, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_19', grCfg.cTheme.grCol_peakmeterBarVertFillPeaks, markerX, y, () => {})); + } + break; + case 'main_bar3': + { + const mainColors = new CustomMenuInputField('main_bar_20', 'grCol.waveformBarFillFront', grCfg.cTheme.grCol_waveformBarFillFront, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_20', grCfg.cTheme.grCol_waveformBarFillFront, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_20', grCfg.cTheme.grCol_waveformBarFillFront, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_21', 'grCol.waveformBarFillBack', grCfg.cTheme.grCol_waveformBarFillBack, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_21', grCfg.cTheme.grCol_waveformBarFillBack, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_21', grCfg.cTheme.grCol_waveformBarFillBack, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_22', 'grCol.waveformBarFillPreFront', grCfg.cTheme.grCol_waveformBarFillPreFront, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_22', grCfg.cTheme.grCol_waveformBarFillPreFront, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_22', grCfg.cTheme.grCol_waveformBarFillPreFront, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_23', 'grCol.waveformBarFillPreBack', grCfg.cTheme.grCol_waveformBarFillPreBack, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_23', grCfg.cTheme.grCol_waveformBarFillPreBack, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_23', grCfg.cTheme.grCol_waveformBarFillPreBack, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_bar_24', 'grCol.waveformBarIndicator', grCfg.cTheme.grCol_waveformBarIndicator, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_bar_24', grCfg.cTheme.grCol_waveformBarIndicator, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_bar_24', grCfg.cTheme.grCol_waveformBarIndicator, markerX, y, () => {})); + } + break; + case 'main_text': + { + const mainColors = new CustomMenuInputField('main_text_01', 'grCol.lowerBarArtist', grCfg.cTheme.grCol_lowerBarArtist, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_01', grCfg.cTheme.grCol_lowerBarArtist, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_01', grCfg.cTheme.grCol_lowerBarArtist, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_02', 'grCol.lowerBarTitle', grCfg.cTheme.grCol_lowerBarTitle, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_02', grCfg.cTheme.grCol_lowerBarTitle, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_02', grCfg.cTheme.grCol_lowerBarTitle, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_03', 'grCol.lowerBarTime', grCfg.cTheme.grCol_lowerBarTime, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_03', grCfg.cTheme.grCol_lowerBarTime, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_03', grCfg.cTheme.grCol_lowerBarTime, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_04', 'grCol.lowerBarLength', grCfg.cTheme.grCol_lowerBarLength, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_04', grCfg.cTheme.grCol_lowerBarLength, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_04', grCfg.cTheme.grCol_lowerBarLength, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_05', 'grCol.detailsText', grCfg.cTheme.grCol_detailsText, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_05', grCfg.cTheme.grCol_detailsText, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_05', grCfg.cTheme.grCol_detailsText, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_06', 'grCol.detailsRating', grCfg.cTheme.grCol_detailsRating, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_06', grCfg.cTheme.grCol_detailsRating, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_06', grCfg.cTheme.grCol_detailsRating, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_08', 'grCol.popupText', grCfg.cTheme.grCol_popupText, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_08', grCfg.cTheme.grCol_popupText, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_08', grCfg.cTheme.grCol_popupText, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_09', 'grCol.lyricsNormal', grCfg.cTheme.grCol_lyricsNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_09', grCfg.cTheme.grCol_lyricsNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_09', grCfg.cTheme.grCol_lyricsNormal, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_10', 'grCol.lyricsHighlight', grCfg.cTheme.grCol_lyricsHighlight, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_10', grCfg.cTheme.grCol_lyricsHighlight, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_10', grCfg.cTheme.grCol_lyricsHighlight, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_text_11', 'grCol.lyricsShadow', grCfg.cTheme.grCol_lyricsShadow, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_text_11', grCfg.cTheme.grCol_lyricsShadow, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_text_11', grCfg.cTheme.grCol_lyricsShadow, markerX, y, () => {})); + } + break; + case 'main_btns': + { + const mainColors = new CustomMenuInputField('main_btns_01', 'grCol.menuBgColor', grCfg.cTheme.grCol_menuBgColor, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_01', grCfg.cTheme.grCol_menuBgColor, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_01', grCfg.cTheme.grCol_menuBgColor, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_02', 'grCol.menuStyleBg', grCfg.cTheme.grCol_menuStyleBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_02', grCfg.cTheme.grCol_menuStyleBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_02', grCfg.cTheme.grCol_menuStyleBg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_03', 'grCol.menuRectStyleEmbossTop', grCfg.cTheme.grCol_menuRectStyleEmbossTop, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_03', grCfg.cTheme.grCol_menuRectStyleEmbossTop, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_03', grCfg.cTheme.grCol_menuRectStyleEmbossTop, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_04', 'grCol.menuRectStyleEmbossBottom', grCfg.cTheme.grCol_menuRectStyleEmbossBottom, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_04', grCfg.cTheme.grCol_menuRectStyleEmbossBottom, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_04', grCfg.cTheme.grCol_menuRectStyleEmbossBottom, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_05', 'grCol.menuRectNormal', grCfg.cTheme.grCol_menuRectNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_05', grCfg.cTheme.grCol_menuRectNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_05', grCfg.cTheme.grCol_menuRectNormal, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_06', 'grCol.menuRectHovered', grCfg.cTheme.grCol_menuRectHovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_06', grCfg.cTheme.grCol_menuRectHovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_06', grCfg.cTheme.grCol_menuRectHovered, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_07', 'grCol.menuRectDown', grCfg.cTheme.grCol_menuRectDown, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_07', grCfg.cTheme.grCol_menuRectDown, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_07', grCfg.cTheme.grCol_menuRectDown, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_08', 'grCol.menuTextNormal', grCfg.cTheme.grCol_menuTextNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_08', grCfg.cTheme.grCol_menuTextNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_08', grCfg.cTheme.grCol_menuTextNormal, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_09', 'grCol.menuTextHovered', grCfg.cTheme.grCol_menuTextHovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_09', grCfg.cTheme.grCol_menuTextHovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_09', grCfg.cTheme.grCol_menuTextHovered, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_10', 'grCol.menuTextDown', grCfg.cTheme.grCol_menuTextDown, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_10', grCfg.cTheme.grCol_menuTextDown, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_10', grCfg.cTheme.grCol_menuTextDown, markerX, y, () => {})); + } + break; + case 'main_btns2': + { + const mainColors = new CustomMenuInputField('main_btns_11', 'grCol.transportEllipseBg', grCfg.cTheme.grCol_transportEllipseBg, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_11', grCfg.cTheme.grCol_transportEllipseBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_11', grCfg.cTheme.grCol_transportEllipseBg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_12', 'grCol.transportEllipseNormal', grCfg.cTheme.grCol_transportEllipseNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_12', grCfg.cTheme.grCol_transportEllipseNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_12', grCfg.cTheme.grCol_transportEllipseNormal, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_13', 'grCol.transportEllipseHovered', grCfg.cTheme.grCol_transportEllipseHovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_13', grCfg.cTheme.grCol_transportEllipseHovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_13', grCfg.cTheme.grCol_transportEllipseHovered, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_14', 'grCol.transportEllipseDown', grCfg.cTheme.grCol_transportEllipseDown, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_14', grCfg.cTheme.grCol_transportEllipseDown, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_14', grCfg.cTheme.grCol_transportEllipseDown, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_15', 'grCol.transportStyleBg', grCfg.cTheme.grCol_transportStyleBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_15', grCfg.cTheme.grCol_transportStyleBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_15', grCfg.cTheme.grCol_transportStyleBg, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_16', 'grCol.transportStyleTop', grCfg.cTheme.grCol_transportStyleTop, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_16', grCfg.cTheme.grCol_transportStyleTop, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_16', grCfg.cTheme.grCol_transportStyleTop, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_17', 'grCol.transportStyleBottom', grCfg.cTheme.grCol_transportStyleBottom, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_17', grCfg.cTheme.grCol_transportStyleBottom, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_17', grCfg.cTheme.grCol_transportStyleBottom, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_18', 'grCol.transportIconNormal', grCfg.cTheme.grCol_transportIconNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_18', grCfg.cTheme.grCol_transportIconNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_18', grCfg.cTheme.grCol_transportIconNormal, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_19', 'grCol.transportIconHovered', grCfg.cTheme.grCol_transportIconHovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_19', grCfg.cTheme.grCol_transportIconHovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_19', grCfg.cTheme.grCol_transportIconHovered, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_btns_20', 'grCol.transportIconDown', grCfg.cTheme.grCol_transportIconDown, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_btns_20', grCfg.cTheme.grCol_transportIconDown, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_btns_20', grCfg.cTheme.grCol_transportIconDown, markerX, y, () => {})); + } + break; + case 'main_style': + { + const mainColors = new CustomMenuInputField('main_style_01', 'grCol.styleBevel', grCfg.cTheme.grCol_styleBevel, x, y, labelW, inputW); + CustomMenu.controlList.push(mainColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_01', grCfg.cTheme.grCol_styleBevel, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_01', grCfg.cTheme.grCol_styleBevel, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_02', 'grCol.styleGradient', grCfg.cTheme.grCol_styleGradient, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_02', grCfg.cTheme.grCol_styleGradient, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_02', grCfg.cTheme.grCol_styleGradient, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_03', 'grCol.styleGradient2', grCfg.cTheme.grCol_styleGradient2, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_03', grCfg.cTheme.grCol_styleGradient2, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_03', grCfg.cTheme.grCol_styleGradient2, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_04', 'grCol.styleProgressBar', grCfg.cTheme.grCol_styleProgressBar, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_04', grCfg.cTheme.grCol_styleProgressBar, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_04', grCfg.cTheme.grCol_styleProgressBar, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_05', 'grCol.styleProgressBarLineTop', grCfg.cTheme.grCol_styleProgressBarLineTop, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_05', grCfg.cTheme.grCol_styleProgressBarLineTop, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_05', grCfg.cTheme.grCol_styleProgressBarLineTop, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_06', 'grCol.styleProgressBarLineBottom', grCfg.cTheme.grCol_styleProgressBarLineBottom, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_06', grCfg.cTheme.grCol_styleProgressBarLineBottom, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_06', grCfg.cTheme.grCol_styleProgressBarLineBottom, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_07', 'grCol.styleProgressBarFill', grCfg.cTheme.grCol_styleProgressBarFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_07', grCfg.cTheme.grCol_styleProgressBarFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_07', grCfg.cTheme.grCol_styleProgressBarFill, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_08', 'grCol.styleVolumeBar', grCfg.cTheme.grCol_styleVolumeBar, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_08', grCfg.cTheme.grCol_styleVolumeBar, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_08', grCfg.cTheme.grCol_styleVolumeBar, markerX, y, () => {})); + y += mainColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('main_style_09', 'grCol.styleVolumeBarFill', grCfg.cTheme.grCol_styleVolumeBarFill, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('main_style_09', grCfg.cTheme.grCol_styleVolumeBarFill, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('main_style_09', grCfg.cTheme.grCol_styleVolumeBarFill, markerX, y, () => {})); + } + break; + } } -} - - -/** - * Reinitializes the custom theme menu. - */ -function reinitCustomThemeMenu() { - if (!displayCustomThemeMenu) return; - if (displayPlaylist) initCustomThemeMenu('pl_bg'); - if (displayDetails) initCustomThemeMenu(false, 'main_bg'); - if (displayLibrary) initCustomThemeMenu(false, false, 'lib_bg'); - if (displayBiography) initCustomThemeMenu(false, false, false, 'bio_bg'); - if (pref.displayLyrics) initCustomThemeMenu(false, 'main_text'); -} - -/** - * The Main colors in the custom theme menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} main_section The main color page to be opened. - */ -function customMainColors(x, y, w, h, main_section) { - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const margin = SCALE(20); - const labelW = SCALE(300) + popupFontSize; - const inputW = SCALE(80) + popupFontSize; - const markerX = x + margin + inputW + (popupFontSize * (RES_4K ? 0.25 : 0.5)) - SCALE(2); - - switch (main_section) { - case 'main_pre': - { - const mainColors = new StringInput('main_pre_01', 'preloaderBg', customColor.preloaderBg, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_pre_01', customColor.preloaderBg, x + 2, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_pre_02', 'preloaderLogo', customColor.preloaderLogo, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_pre_02', customColor.preloaderLogo, x + 2, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_pre_03', 'preloaderLowerBarTitle', customColor.preloaderLowerBarTitle, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_pre_03', customColor.preloaderLowerBarTitle, x + 2, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_pre_04', 'preloaderProgressBar', customColor.preloaderProgressBar, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_pre_04', customColor.preloaderProgressBar, x + 2, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_pre_05', 'preloaderProgressBarFill', customColor.preloaderProgressBarFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_pre_05', customColor.preloaderProgressBarFill, x + 2, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_pre_06', 'preloaderProgressBarFrame', customColor.preloaderProgressBarFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_pre_06', customColor.preloaderProgressBarFrame, x + 2, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_pre_07', 'preloaderUIHacksFrame', customColor.preloaderUIHacksFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_pre_07', customColor.preloaderUIHacksFrame, x + 2, y, () => {})); - } - break; - case 'main_bg': - { - const mainColors = new StringInput('main_bg_01', 'col.bg', customColor.col_bg, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_bg_01', customColor.col_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bg_01', customColor.col_bg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bg_02', 'col.popupBg', customColor.col_popupBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bg_02', customColor.col_popupBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bg_02', customColor.col_popupBg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bg_03', 'col.detailsBg', customColor.col_detailsBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bg_03', customColor.col_detailsBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bg_03', customColor.col_detailsBg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bg_04', 'col.shadow', customColor.col_shadow, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bg_04', customColor.col_shadow, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bg_04', customColor.col_shadow, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bg_05', 'col.discArtShadow', customColor.col_discArtShadow, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bg_05', customColor.col_discArtShadow, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bg_05', customColor.col_discArtShadow, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bg_06', 'col.noAlbumArtStub', customColor.col_noAlbumArtStub, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bg_06', customColor.col_noAlbumArtStub, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bg_06', customColor.col_noAlbumArtStub, markerX, y, () => {})); - } - break; - case 'main_bar': - { - const mainColors = new StringInput('main_bar_01', 'col.timelineAdded', customColor.col_timelineAdded, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_bar_01', customColor.col_timelineAdded, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_01', customColor.col_timelineAdded, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_02', 'col.timelinePlayed', customColor.col_timelinePlayed, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_02', customColor.col_timelinePlayed, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_02', customColor.col_timelinePlayed, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_03', 'col.timelineUnplayed', customColor.col_timelineUnplayed, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_03', customColor.col_timelineUnplayed, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_03', customColor.col_timelineUnplayed, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_04', 'col.timelineFrame', customColor.col_timelineFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_04', customColor.col_timelineFrame, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_04', customColor.col_timelineFrame, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_05', 'col.progressBar', customColor.col_progressBar, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_05', customColor.col_progressBar, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_05', customColor.col_progressBar, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_06', 'col.progressBarStreaming', customColor.col_progressBarStreaming, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_06', customColor.col_progressBarStreaming, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_06', customColor.col_progressBarStreaming, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_07', 'col.progressBarFrame', customColor.col_progressBarFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_07', customColor.col_progressBarFrame, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_07', customColor.col_progressBarFrame, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_08', 'col.progressBarFill', customColor.col_progressBarFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_08', customColor.col_progressBarFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_08', customColor.col_progressBarFill, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_09', 'col.volumeBar', customColor.col_volumeBar, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_09', customColor.col_volumeBar, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_09', customColor.col_volumeBar, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_10', 'col.volumeBarFrame', customColor.col_volumeBarFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_10', customColor.col_volumeBarFrame, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_10', customColor.col_volumeBarFrame, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_11', 'col.volumeBarFill', customColor.col_volumeBarFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_11', customColor.col_volumeBarFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_11', customColor.col_volumeBarFill, markerX, y, () => {})); - } - break; - case 'main_bar2': - { - const mainColors = new StringInput('main_bar_12', 'col.peakmeterBarProg', customColor.col_peakmeterBarProg, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_bar_12', customColor.col_peakmeterBarProg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_12', customColor.col_peakmeterBarProg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_13', 'col.peakmeterBarProgFill', customColor.col_peakmeterBarProgFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_13', customColor.col_peakmeterBarProgFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_13', customColor.col_peakmeterBarProgFill, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_14', 'col.peakmeterBarFillTop', customColor.col_peakmeterBarFillTop, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_14', customColor.col_peakmeterBarFillTop, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_14', customColor.col_peakmeterBarFillTop, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_15', 'col.peakmeterBarFillMiddle', customColor.col_peakmeterBarFillMiddle, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_15', customColor.col_peakmeterBarFillMiddle, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_15', customColor.col_peakmeterBarFillMiddle, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_16', 'col.peakmeterBarFillBack', customColor.col_peakmeterBarFillBack, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_16', customColor.col_peakmeterBarFillBack, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_16', customColor.col_peakmeterBarFillBack, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_17', 'col.peakmeterBarVertProgFill', customColor.col_peakmeterBarVertProgFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_17', customColor.col_peakmeterBarVertProgFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_17', customColor.col_peakmeterBarVertProgFill, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_18', 'col.peakmeterBarVertFill', customColor.col_peakmeterBarVertFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_18', customColor.col_peakmeterBarVertFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_18', customColor.col_peakmeterBarVertFill, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_19', 'col.peakmeterBarVertFillPeaks', customColor.col_peakmeterBarVertFillPeaks, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_19', customColor.col_peakmeterBarVertFillPeaks, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_19', customColor.col_peakmeterBarVertFillPeaks, markerX, y, () => {})); - } - break; - case 'main_bar3': - { - const mainColors = new StringInput('main_bar_20', 'col.waveformBarFillFront', customColor.col_waveformBarFillFront, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_bar_20', customColor.col_waveformBarFillFront, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_20', customColor.col_waveformBarFillFront, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_21', 'col.waveformBarFillBack', customColor.col_waveformBarFillBack, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_21', customColor.col_waveformBarFillBack, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_21', customColor.col_waveformBarFillBack, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_22', 'col.waveformBarFillPreFront', customColor.col_waveformBarFillPreFront, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_22', customColor.col_waveformBarFillPreFront, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_22', customColor.col_waveformBarFillPreFront, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_23', 'col.waveformBarFillPreBack', customColor.col_waveformBarFillPreBack, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_23', customColor.col_waveformBarFillPreBack, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_23', customColor.col_waveformBarFillPreBack, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_bar_24', 'col.waveformBarIndicator', customColor.col_waveformBarIndicator, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_bar_24', customColor.col_waveformBarIndicator, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_bar_24', customColor.col_waveformBarIndicator, markerX, y, () => {})); - } - break; - case 'main_text': - { - const mainColors = new StringInput('main_text_01', 'col.lowerBarArtist', customColor.col_lowerBarArtist, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_text_01', customColor.col_lowerBarArtist, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_01', customColor.col_lowerBarArtist, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_02', 'col.lowerBarTitle', customColor.col_lowerBarTitle, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_02', customColor.col_lowerBarTitle, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_02', customColor.col_lowerBarTitle, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_03', 'col.lowerBarTime', customColor.col_lowerBarTime, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_03', customColor.col_lowerBarTime, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_03', customColor.col_lowerBarTime, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_04', 'col.lowerBarLength', customColor.col_lowerBarLength, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_04', customColor.col_lowerBarLength, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_04', customColor.col_lowerBarLength, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_05', 'col.detailsText', customColor.col_detailsText, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_05', customColor.col_detailsText, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_05', customColor.col_detailsText, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_06', 'col.detailsRating', customColor.col_detailsRating, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_06', customColor.col_detailsRating, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_06', customColor.col_detailsRating, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_08', 'col.popupText', customColor.col_popupText, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_08', customColor.col_popupText, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_08', customColor.col_popupText, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_09', 'col.lyricsNormal', customColor.col_lyricsNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_09', customColor.col_lyricsNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_09', customColor.col_lyricsNormal, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_10', 'col.lyricsHighlight', customColor.col_lyricsHighlight, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_10', customColor.col_lyricsHighlight, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_10', customColor.col_lyricsHighlight, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_text_11', 'col.lyricsShadow', customColor.col_lyricsShadow, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_text_11', customColor.col_lyricsShadow, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_text_11', customColor.col_lyricsShadow, markerX, y, () => {})); - } - break; - case 'main_btns': - { - const mainColors = new StringInput('main_btns_01', 'col.menuBgColor', customColor.col_menuBgColor, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_btns_01', customColor.col_menuBgColor, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_01', customColor.col_menuBgColor, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_02', 'col.menuStyleBg', customColor.col_menuStyleBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_02', customColor.col_menuStyleBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_02', customColor.col_menuStyleBg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_03', 'col.menuRectStyleEmbossTop', customColor.col_menuRectStyleEmbossTop, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_03', customColor.col_menuRectStyleEmbossTop, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_03', customColor.col_menuRectStyleEmbossTop, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_04', 'col.menuRectStyleEmbossBottom', customColor.col_menuRectStyleEmbossBottom, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_04', customColor.col_menuRectStyleEmbossBottom, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_04', customColor.col_menuRectStyleEmbossBottom, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_05', 'col.menuRectNormal', customColor.col_menuRectNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_05', customColor.col_menuRectNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_05', customColor.col_menuRectNormal, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_06', 'col.menuRectHovered', customColor.col_menuRectHovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_06', customColor.col_menuRectHovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_06', customColor.col_menuRectHovered, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_07', 'col.menuRectDown', customColor.col_menuRectDown, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_07', customColor.col_menuRectDown, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_07', customColor.col_menuRectDown, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_08', 'col.menuTextNormal', customColor.col_menuTextNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_08', customColor.col_menuTextNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_08', customColor.col_menuTextNormal, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_09', 'col.menuTextHovered', customColor.col_menuTextHovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_09', customColor.col_menuTextHovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_09', customColor.col_menuTextHovered, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_10', 'col.menuTextDown', customColor.col_menuTextDown, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_10', customColor.col_menuTextDown, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_10', customColor.col_menuTextDown, markerX, y, () => {})); - } - break; - case 'main_btns2': - { - const mainColors = new StringInput('main_btns_11', 'col.transportEllipseBg', customColor.col_transportEllipseBg, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_btns_11', customColor.col_transportEllipseBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_11', customColor.col_transportEllipseBg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_12', 'col.transportEllipseNormal', customColor.col_transportEllipseNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_12', customColor.col_transportEllipseNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_12', customColor.col_transportEllipseNormal, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_13', 'col.transportEllipseHovered', customColor.col_transportEllipseHovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_13', customColor.col_transportEllipseHovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_13', customColor.col_transportEllipseHovered, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_14', 'col.transportEllipseDown', customColor.col_transportEllipseDown, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_14', customColor.col_transportEllipseDown, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_14', customColor.col_transportEllipseDown, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_15', 'col.transportStyleBg', customColor.col_transportStyleBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_15', customColor.col_transportStyleBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_15', customColor.col_transportStyleBg, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_16', 'col.transportStyleTop', customColor.col_transportStyleTop, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_16', customColor.col_transportStyleTop, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_16', customColor.col_transportStyleTop, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_17', 'col.transportStyleBottom', customColor.col_transportStyleBottom, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_17', customColor.col_transportStyleBottom, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_17', customColor.col_transportStyleBottom, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_18', 'col.transportIconNormal', customColor.col_transportIconNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_18', customColor.col_transportIconNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_18', customColor.col_transportIconNormal, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_19', 'col.transportIconHovered', customColor.col_transportIconHovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_19', customColor.col_transportIconHovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_19', customColor.col_transportIconHovered, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_btns_20', 'col.transportIconDown', customColor.col_transportIconDown, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_btns_20', customColor.col_transportIconDown, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_btns_20', customColor.col_transportIconDown, markerX, y, () => {})); - } - break; - case 'main_style': - { - const mainColors = new StringInput('main_style_01', 'col.styleBevel', customColor.col_styleBevel, x, y, labelW, inputW); - controlList.push(mainColors); - controlList.push(new ColorPicker('main_style_01', customColor.col_styleBevel, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_01', customColor.col_styleBevel, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_02', 'col.styleGradient', customColor.col_styleGradient, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_02', customColor.col_styleGradient, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_02', customColor.col_styleGradient, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_03', 'col.styleGradient2', customColor.col_styleGradient2, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_03', customColor.col_styleGradient2, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_03', customColor.col_styleGradient2, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_04', 'col.styleProgressBar', customColor.col_styleProgressBar, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_04', customColor.col_styleProgressBar, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_04', customColor.col_styleProgressBar, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_05', 'col.styleProgressBarLineTop', customColor.col_styleProgressBarLineTop, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_05', customColor.col_styleProgressBarLineTop, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_05', customColor.col_styleProgressBarLineTop, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_06', 'col.styleProgressBarLineBottom', customColor.col_styleProgressBarLineBottom, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_06', customColor.col_styleProgressBarLineBottom, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_06', customColor.col_styleProgressBarLineBottom, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_07', 'col.styleProgressBarFill', customColor.col_styleProgressBarFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_07', customColor.col_styleProgressBarFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_07', customColor.col_styleProgressBarFill, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_08', 'col.styleVolumeBar', customColor.col_styleVolumeBar, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_08', customColor.col_styleVolumeBar, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_08', customColor.col_styleVolumeBar, markerX, y, () => {})); - y += mainColors.h + margin; - controlList.push(new StringInput('main_style_09', 'col.styleVolumeBarFill', customColor.col_styleVolumeBarFill, x, y, labelW, inputW)); - controlList.push(new ColorPicker('main_style_09', customColor.col_styleVolumeBarFill, x + 2, y, () => {})); - controlList.push(new ColorMarker('main_style_09', customColor.col_styleVolumeBarFill, markerX, y, () => {})); - } - break; - } -} - - -/** - * The Playlist colors in the custom theme menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} playlist_section The playlist color page to be opened. - */ -function customPlaylistColors(x, y, w, h, playlist_section) { - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const margin = SCALE(20); - const labelW = SCALE(300) + popupFontSize; - const inputW = SCALE(80) + popupFontSize; - const markerX = x + margin + inputW + (popupFontSize * (RES_4K ? 0.25 : 0.5)) - SCALE(2); - - switch (playlist_section) { - case 'pl_bg': - { - const plColors = new StringInput('pl_bg_01', 'g_pl_colors.bg', customColor.g_pl_colors_bg, x, y, labelW, inputW); - controlList.push(plColors); - controlList.push(new ColorPicker('pl_bg_01', customColor.g_pl_colors_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_bg_01', customColor.g_pl_colors_bg, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_bg_02', 'g_pl_colors.header_nowplaying_bg', customColor.g_pl_colors_header_nowplaying_bg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_bg_02', customColor.g_pl_colors_header_nowplaying_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_bg_02', customColor.g_pl_colors_header_nowplaying_bg, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_bg_03', 'g_pl_colors.header_sideMarker', customColor.g_pl_colors_header_sideMarker, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_bg_03', customColor.g_pl_colors_header_sideMarker, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_bg_03', customColor.g_pl_colors_header_sideMarker, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_bg_04', 'g_pl_colors.row_nowplaying_bg', customColor.g_pl_colors_row_nowplaying_bg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_bg_04', customColor.g_pl_colors_row_nowplaying_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_bg_04', customColor.g_pl_colors_row_nowplaying_bg, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_bg_05', 'g_pl_colors.row_stripes_bg', customColor.g_pl_colors_row_stripes_bg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_bg_05', customColor.g_pl_colors_row_stripes_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_bg_05', customColor.g_pl_colors_row_stripes_bg, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_bg_06', 'g_pl_colors.row_sideMarker', customColor.g_pl_colors_row_sideMarker, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_bg_06', customColor.g_pl_colors_row_sideMarker, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_bg_06', customColor.g_pl_colors_row_sideMarker, markerX, y, () => {})); - } - break; - case 'pl_text1': - { - const plColors = new StringInput('pl_text_01', 'g_pl_colors.plman_text_normal', customColor.g_pl_colors_plman_text_normal, x, y, labelW, inputW); - controlList.push(plColors); - controlList.push(new ColorPicker('pl_text_01', customColor.g_pl_colors_plman_text_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_01', customColor.g_pl_colors_plman_text_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_02', 'g_pl_colors.plman_text_hovered', customColor.g_pl_colors_plman_text_hovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_02', customColor.g_pl_colors_plman_text_hovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_02', customColor.g_pl_colors_plman_text_hovered, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_03', 'g_pl_colors.plman_text_pressed', customColor.g_pl_colors_plman_text_pressed, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_03', customColor.g_pl_colors_plman_text_pressed, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_03', customColor.g_pl_colors_plman_text_pressed, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_04', 'g_pl_colors.header_artist_normal', customColor.g_pl_colors_header_artist_normal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_04', customColor.g_pl_colors_header_artist_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_04', customColor.g_pl_colors_header_artist_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_05', 'g_pl_colors.header_artist_playing', customColor.g_pl_colors_header_artist_playing, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_05', customColor.g_pl_colors_header_artist_playing, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_05', customColor.g_pl_colors_header_artist_playing, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_06', 'g_pl_colors.header_album_normal', customColor.g_pl_colors_header_album_normal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_06', customColor.g_pl_colors_header_album_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_06', customColor.g_pl_colors_header_album_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_07', 'g_pl_colors.header_album_playing', customColor.g_pl_colors_header_album_playing, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_07', customColor.g_pl_colors_header_album_playing, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_07', customColor.g_pl_colors_header_album_playing, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_08', 'g_pl_colors.header_info_normal', customColor.g_pl_colors_header_info_normal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_08', customColor.g_pl_colors_header_info_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_08', customColor.g_pl_colors_header_info_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_09', 'g_pl_colors.header_info_playing', customColor.g_pl_colors_header_info_playing, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_09', customColor.g_pl_colors_header_info_playing, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_09', customColor.g_pl_colors_header_info_playing, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_10', 'g_pl_colors.header_date_normal', customColor.g_pl_colors_header_date_normal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_10', customColor.g_pl_colors_header_date_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_10', customColor.g_pl_colors_header_date_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_11', 'g_pl_colors.header_date_playing', customColor.g_pl_colors_header_date_playing, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_11', customColor.g_pl_colors_header_date_playing, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_11', customColor.g_pl_colors_header_date_playing, markerX, y, () => {})); - } - break; - case 'pl_text2': - { - const plColors = new StringInput('pl_text_12', 'g_pl_colors.row_title_normal', customColor.g_pl_colors_row_title_normal, x, y, labelW, inputW); - controlList.push(plColors); - controlList.push(new ColorPicker('pl_text_12', customColor.g_pl_colors_row_title_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_12', customColor.g_pl_colors_row_title_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_13', 'g_pl_colors.row_title_playing', customColor.g_pl_colors_row_title_playing, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_13', customColor.g_pl_colors_row_title_playing, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_13', customColor.g_pl_colors_row_title_playing, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_14', 'g_pl_colors.row_title_selected', customColor.g_pl_colors_row_title_selected, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_14', customColor.g_pl_colors_row_title_selected, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_14', customColor.g_pl_colors_row_title_selected, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_text_15', 'g_pl_colors.row_title_hovered', customColor.g_pl_colors_row_title_hovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_text_15', customColor.g_pl_colors_row_title_hovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_text_15', customColor.g_pl_colors_row_title_hovered, markerX, y, () => {})); - } - break; - case 'pl_misc': - { - const plColors = new StringInput('pl_misc_01', 'g_pl_colors.header_line_normal', customColor.g_pl_colors_header_line_normal, x, y, labelW, inputW); - controlList.push(plColors); - controlList.push(new ColorPicker('pl_misc_01', customColor.g_pl_colors_header_line_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_01', customColor.g_pl_colors_header_line_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_misc_02', 'g_pl_colors.header_line_playing', customColor.g_pl_colors_header_line_playing, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_misc_02', customColor.g_pl_colors_header_line_playing, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_02', customColor.g_pl_colors_header_line_playing, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_misc_03', 'g_pl_colors.row_disc_subheader_line', customColor.g_pl_colors_row_disc_subheader_line, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_misc_03', customColor.g_pl_colors_row_disc_subheader_line, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_03', customColor.g_pl_colors_row_disc_subheader_line, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_misc_04', 'g_pl_colors.row_drag_line', customColor.g_pl_colors_row_drag_line, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_misc_04', customColor.g_pl_colors_row_drag_line, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_04', customColor.g_pl_colors_row_drag_line, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_misc_05', 'g_pl_colors.row_drag_line_reached', customColor.g_pl_colors_row_drag_line_reached, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_misc_05', customColor.g_pl_colors_row_drag_line_reached, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_05', customColor.g_pl_colors_row_drag_line_reached, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_misc_06', 'g_pl_colors.row_selection_frame', customColor.g_pl_colors_row_selection_frame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_misc_06', customColor.g_pl_colors_row_selection_frame, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_06', customColor.g_pl_colors_row_selection_frame, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_misc_07', 'g_pl_colors.row_rating_color', customColor.g_pl_colors_row_rating_color, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_misc_07', customColor.g_pl_colors_row_rating_color, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_misc_07', customColor.g_pl_colors_row_rating_color, markerX, y, () => {})); - } - break; - case 'pl_btns': - { - const plColors = new StringInput('pl_btns_01', 'g_pl_colors.sbar_btn_normal', customColor.g_pl_colors_sbar_btn_normal, x, y, labelW, inputW); - controlList.push(plColors); - controlList.push(new ColorPicker('pl_btns_01', customColor.g_pl_colors_sbar_btn_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_btns_01', customColor.g_pl_colors_sbar_btn_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_btns_02', 'g_pl_colors.sbar_btn_hovered', customColor.g_pl_colors_sbar_btn_hovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_btns_02', customColor.g_pl_colors_sbar_btn_hovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_btns_02', customColor.g_pl_colors_sbar_btn_hovered, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_btns_03', 'g_pl_colors.sbar_thumb_normal', customColor.g_pl_colors_sbar_thumb_normal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_btns_03', customColor.g_pl_colors_sbar_thumb_normal, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_btns_03', customColor.g_pl_colors_sbar_thumb_normal, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_btns_04', 'g_pl_colors.sbar_thumb_hovered', customColor.g_pl_colors_sbar_thumb_hovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_btns_04', customColor.g_pl_colors_sbar_thumb_hovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_btns_04', customColor.g_pl_colors_sbar_thumb_hovered, markerX, y, () => {})); - y += plColors.h + margin; - controlList.push(new StringInput('pl_btns_05', 'g_pl_colors.sbar_thumb_drag', customColor.g_pl_colors_sbar_thumb_drag, x, y, labelW, inputW)); - controlList.push(new ColorPicker('pl_btns_05', customColor.g_pl_colors_sbar_thumb_drag, x + 2, y, () => {})); - controlList.push(new ColorMarker('pl_btns_05', customColor.g_pl_colors_sbar_thumb_drag, markerX, y, () => {})); - } - break; + /** + * The Playlist colors in the custom theme menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} playlist_section - The playlist color page to be opened. + * @private + */ + _customPlaylistColors(x, y, w, h, playlist_section) { + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const margin = SCALE(20); + const labelW = SCALE(300) + popupFontSize; + const inputW = SCALE(80) + popupFontSize; + const markerX = x + margin + inputW + (popupFontSize * (RES._4K ? 0.25 : 0.5)) - SCALE(2); + + switch (playlist_section) { + case 'pl_bg': + { + const plColors = new CustomMenuInputField('pl_bg_01', 'pl.col.bg', grCfg.cTheme.pl_col_bg, x, y, labelW, inputW); + CustomMenu.controlList.push(plColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_bg_01', grCfg.cTheme.pl_col_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_bg_01', grCfg.cTheme.pl_col_bg, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_bg_02', 'pl.col.header_nowplaying_bg', grCfg.cTheme.pl_col_header_nowplaying_bg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_bg_02', grCfg.cTheme.pl_col_header_nowplaying_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_bg_02', grCfg.cTheme.pl_col_header_nowplaying_bg, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_bg_03', 'pl.col.header_sideMarker', grCfg.cTheme.pl_col_header_sideMarker, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_bg_03', grCfg.cTheme.pl_col_header_sideMarker, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_bg_03', grCfg.cTheme.pl_col_header_sideMarker, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_bg_04', 'pl.col.row_nowplaying_bg', grCfg.cTheme.pl_col_row_nowplaying_bg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_bg_04', grCfg.cTheme.pl_col_row_nowplaying_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_bg_04', grCfg.cTheme.pl_col_row_nowplaying_bg, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_bg_05', 'pl.col.row_stripes_bg', grCfg.cTheme.pl_col_row_stripes_bg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_bg_05', grCfg.cTheme.pl_col_row_stripes_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_bg_05', grCfg.cTheme.pl_col_row_stripes_bg, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_bg_06', 'pl.col.row_sideMarker', grCfg.cTheme.pl_col_row_sideMarker, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_bg_06', grCfg.cTheme.pl_col_row_sideMarker, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_bg_06', grCfg.cTheme.pl_col_row_sideMarker, markerX, y, () => {})); + } + break; + case 'pl_text1': + { + const plColors = new CustomMenuInputField('pl_text_01', 'pl.col.plman_text_normal', grCfg.cTheme.pl_col_plman_text_normal, x, y, labelW, inputW); + CustomMenu.controlList.push(plColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_01', grCfg.cTheme.pl_col_plman_text_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_01', grCfg.cTheme.pl_col_plman_text_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_02', 'pl.col.plman_text_hovered', grCfg.cTheme.pl_col_plman_text_hovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_02', grCfg.cTheme.pl_col_plman_text_hovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_02', grCfg.cTheme.pl_col_plman_text_hovered, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_03', 'pl.col.plman_text_pressed', grCfg.cTheme.pl_col_plman_text_pressed, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_03', grCfg.cTheme.pl_col_plman_text_pressed, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_03', grCfg.cTheme.pl_col_plman_text_pressed, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_04', 'pl.col.header_artist_normal', grCfg.cTheme.pl_col_header_artist_normal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_04', grCfg.cTheme.pl_col_header_artist_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_04', grCfg.cTheme.pl_col_header_artist_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_05', 'pl.col.header_artist_playing', grCfg.cTheme.pl_col_header_artist_playing, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_05', grCfg.cTheme.pl_col_header_artist_playing, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_05', grCfg.cTheme.pl_col_header_artist_playing, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_06', 'pl.col.header_album_normal', grCfg.cTheme.pl_col_header_album_normal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_06', grCfg.cTheme.pl_col_header_album_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_06', grCfg.cTheme.pl_col_header_album_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_07', 'pl.col.header_album_playing', grCfg.cTheme.pl_col_header_album_playing, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_07', grCfg.cTheme.pl_col_header_album_playing, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_07', grCfg.cTheme.pl_col_header_album_playing, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_08', 'pl.col.header_info_normal', grCfg.cTheme.pl_col_header_info_normal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_08', grCfg.cTheme.pl_col_header_info_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_08', grCfg.cTheme.pl_col_header_info_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_09', 'pl.col.header_info_playing', grCfg.cTheme.pl_col_header_info_playing, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_09', grCfg.cTheme.pl_col_header_info_playing, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_09', grCfg.cTheme.pl_col_header_info_playing, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_10', 'pl.col.header_date_normal', grCfg.cTheme.pl_col_header_date_normal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_10', grCfg.cTheme.pl_col_header_date_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_10', grCfg.cTheme.pl_col_header_date_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_11', 'pl.col.header_date_playing', grCfg.cTheme.pl_col_header_date_playing, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_11', grCfg.cTheme.pl_col_header_date_playing, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_11', grCfg.cTheme.pl_col_header_date_playing, markerX, y, () => {})); + } + break; + case 'pl_text2': + { + const plColors = new CustomMenuInputField('pl_text_12', 'pl.col.row_title_normal', grCfg.cTheme.pl_col_row_title_normal, x, y, labelW, inputW); + CustomMenu.controlList.push(plColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_12', grCfg.cTheme.pl_col_row_title_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_12', grCfg.cTheme.pl_col_row_title_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_13', 'pl.col.row_title_playing', grCfg.cTheme.pl_col_row_title_playing, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_13', grCfg.cTheme.pl_col_row_title_playing, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_13', grCfg.cTheme.pl_col_row_title_playing, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_14', 'pl.col.row_title_selected', grCfg.cTheme.pl_col_row_title_selected, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_14', grCfg.cTheme.pl_col_row_title_selected, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_14', grCfg.cTheme.pl_col_row_title_selected, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_text_15', 'pl.col.row_title_hovered', grCfg.cTheme.pl_col_row_title_hovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_text_15', grCfg.cTheme.pl_col_row_title_hovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_text_15', grCfg.cTheme.pl_col_row_title_hovered, markerX, y, () => {})); + } + break; + case 'pl_misc': + { + const plColors = new CustomMenuInputField('pl_misc_01', 'pl.col.header_line_normal', grCfg.cTheme.pl_col_header_line_normal, x, y, labelW, inputW); + CustomMenu.controlList.push(plColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_01', grCfg.cTheme.pl_col_header_line_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_01', grCfg.cTheme.pl_col_header_line_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_misc_02', 'pl.col.header_line_playing', grCfg.cTheme.pl_col_header_line_playing, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_02', grCfg.cTheme.pl_col_header_line_playing, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_02', grCfg.cTheme.pl_col_header_line_playing, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_misc_03', 'pl.col.row_disc_subheader_line', grCfg.cTheme.pl_col_row_disc_subheader_line, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_03', grCfg.cTheme.pl_col_row_disc_subheader_line, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_03', grCfg.cTheme.pl_col_row_disc_subheader_line, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_misc_04', 'pl.col.row_drag_line', grCfg.cTheme.pl_col_row_drag_line, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_04', grCfg.cTheme.pl_col_row_drag_line, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_04', grCfg.cTheme.pl_col_row_drag_line, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_misc_05', 'pl.col.row_drag_line_reached', grCfg.cTheme.pl_col_row_drag_line_reached, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_05', grCfg.cTheme.pl_col_row_drag_line_reached, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_05', grCfg.cTheme.pl_col_row_drag_line_reached, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_misc_06', 'pl.col.row_selection_frame', grCfg.cTheme.pl_col_row_selection_frame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_06', grCfg.cTheme.pl_col_row_selection_frame, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_06', grCfg.cTheme.pl_col_row_selection_frame, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_misc_07', 'pl.col.row_rating_color', grCfg.cTheme.pl_col_row_rating_color, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_misc_07', grCfg.cTheme.pl_col_row_rating_color, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_misc_07', grCfg.cTheme.pl_col_row_rating_color, markerX, y, () => {})); + } + break; + case 'pl_btns': + { + const plColors = new CustomMenuInputField('pl_btns_01', 'pl.col.sbar_btn_normal', grCfg.cTheme.pl_col_sbar_btn_normal, x, y, labelW, inputW); + CustomMenu.controlList.push(plColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_btns_01', grCfg.cTheme.pl_col_sbar_btn_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_btns_01', grCfg.cTheme.pl_col_sbar_btn_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_btns_02', 'pl.col.sbar_btn_hovered', grCfg.cTheme.pl_col_sbar_btn_hovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_btns_02', grCfg.cTheme.pl_col_sbar_btn_hovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_btns_02', grCfg.cTheme.pl_col_sbar_btn_hovered, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_btns_03', 'pl.col.sbar_thumb_normal', grCfg.cTheme.pl_col_sbar_thumb_normal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_btns_03', grCfg.cTheme.pl_col_sbar_thumb_normal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_btns_03', grCfg.cTheme.pl_col_sbar_thumb_normal, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_btns_04', 'pl.col.sbar_thumb_hovered', grCfg.cTheme.pl_col_sbar_thumb_hovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_btns_04', grCfg.cTheme.pl_col_sbar_thumb_hovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_btns_04', grCfg.cTheme.pl_col_sbar_thumb_hovered, markerX, y, () => {})); + y += plColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('pl_btns_05', 'pl.col.sbar_thumb_drag', grCfg.cTheme.pl_col_sbar_thumb_drag, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('pl_btns_05', grCfg.cTheme.pl_col_sbar_thumb_drag, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('pl_btns_05', grCfg.cTheme.pl_col_sbar_thumb_drag, markerX, y, () => {})); + } + break; + } } -} + /** + * The Library colors in the custom theme menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} library_section - The library color page to be opened. + * @private + */ + _customLibraryColors(x, y, w, h, library_section) { + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const margin = SCALE(20); + const labelW = SCALE(300) + popupFontSize; + const inputW = SCALE(80) + popupFontSize; + const markerX = x + margin + inputW + (popupFontSize * (RES._4K ? 0.25 : 0.5)) - SCALE(2); + + switch (library_section) { + case 'lib_bg': + { + const libColors = new CustomMenuInputField('lib_bg_01', 'lib.ui.col.bg', grCfg.cTheme.lib_ui_col_bg, x, y, labelW, inputW); + CustomMenu.controlList.push(libColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_01', grCfg.cTheme.lib_ui_col_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_01', grCfg.cTheme.lib_ui_col_bg, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_bg_02', 'lib.ui.col.rowStripes', grCfg.cTheme.lib_ui_col_rowStripes, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_02', grCfg.cTheme.lib_ui_col_rowStripes, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_02', grCfg.cTheme.lib_ui_col_rowStripes, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_bg_03', 'lib.ui.col.nowPlayingBg', grCfg.cTheme.lib_ui_col_nowPlayingBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_03', grCfg.cTheme.lib_ui_col_nowPlayingBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_03', grCfg.cTheme.lib_ui_col_nowPlayingBg, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_bg_04', 'lib.ui.col.sideMarker', grCfg.cTheme.lib_ui_col_sideMarker, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_04', grCfg.cTheme.lib_ui_col_sideMarker, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_04', grCfg.cTheme.lib_ui_col_sideMarker, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_bg_05', 'lib.ui.col.selectionFrame', grCfg.cTheme.lib_ui_col_selectionFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_05', grCfg.cTheme.lib_ui_col_selectionFrame, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_05', grCfg.cTheme.lib_ui_col_selectionFrame, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_bg_06', 'lib.ui.col.selectionFrame2', grCfg.cTheme.lib_ui_col_selectionFrame2, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_06', grCfg.cTheme.lib_ui_col_selectionFrame2, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_06', grCfg.cTheme.lib_ui_col_selectionFrame2, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_bg_07', 'lib.ui.col.hoverFrame', grCfg.cTheme.lib_ui_col_hoverFrame, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_bg_07', grCfg.cTheme.lib_ui_col_hoverFrame, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_bg_07', grCfg.cTheme.lib_ui_col_hoverFrame, markerX, y, () => {})); + } + break; + case 'lib_text': + { + const libColors = new CustomMenuInputField('lib_text_01', 'lib.ui.col.text', grCfg.cTheme.lib_ui_col_text, x, y, labelW, inputW); + CustomMenu.controlList.push(libColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_01', grCfg.cTheme.lib_ui_col_text, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_01', grCfg.cTheme.lib_ui_col_text, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_02', 'lib.ui.col.text_h', grCfg.cTheme.lib_ui_col_text_h, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_02', grCfg.cTheme.lib_ui_col_text_h, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_02', grCfg.cTheme.lib_ui_col_text_h, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_03', 'lib.ui.col.text_nowp', grCfg.cTheme.lib_ui_col_text_nowp, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_03', grCfg.cTheme.lib_ui_col_text_nowp, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_03', grCfg.cTheme.lib_ui_col_text_nowp, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_04', 'lib.ui.col.textSel', grCfg.cTheme.lib_ui_col_textSel, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_04', grCfg.cTheme.lib_ui_col_textSel, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_04', grCfg.cTheme.lib_ui_col_textSel, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_05', 'lib.ui.col.txt', grCfg.cTheme.lib_ui_col_txt, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_05', grCfg.cTheme.lib_ui_col_txt, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_05', grCfg.cTheme.lib_ui_col_txt, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_06', 'lib.ui.col.txt_h', grCfg.cTheme.lib_ui_col_txt_h, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_06', grCfg.cTheme.lib_ui_col_txt_h, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_06', grCfg.cTheme.lib_ui_col_txt_h, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_07', 'lib.ui.col.txt_box', grCfg.cTheme.lib_ui_col_txt_box, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_07', grCfg.cTheme.lib_ui_col_txt_box, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_07', grCfg.cTheme.lib_ui_col_txt_box, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_text_08', 'lib.ui.col.search', grCfg.cTheme.lib_ui_col_search, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_text_08', grCfg.cTheme.lib_ui_col_search, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_text_08', grCfg.cTheme.lib_ui_col_search, markerX, y, () => {})); + } + break; + case 'lib_node': + { + const libColors = new CustomMenuInputField('lib_node_01', 'lib.ui.col.iconPlus', grCfg.cTheme.lib_ui_col_iconPlus, x, y, labelW, inputW); + CustomMenu.controlList.push(libColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_01', grCfg.cTheme.lib_ui_col_iconPlus, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_01', grCfg.cTheme.lib_ui_col_iconPlus, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_node_02', 'lib.ui.col.iconPlus_h', grCfg.cTheme.lib_ui_col_iconPlus_h, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_02', grCfg.cTheme.lib_ui_col_iconPlus_h, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_02', grCfg.cTheme.lib_ui_col_iconPlus_h, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_node_03', 'lib.ui.col.iconPlus_sel', grCfg.cTheme.lib_ui_col_iconPlus_sel, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_03', grCfg.cTheme.lib_ui_col_iconPlus_sel, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_03', grCfg.cTheme.lib_ui_col_iconPlus_sel, markerX, y, () => {})); + y += libColors.h + margin; -/** - * The Library colors in the custom theme menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} library_section The library color page to be opened. - */ -function customLibraryColors(x, y, w, h, library_section) { - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const margin = SCALE(20); - const labelW = SCALE(300) + popupFontSize; - const inputW = SCALE(80) + popupFontSize; - const markerX = x + margin + inputW + (popupFontSize * (RES_4K ? 0.25 : 0.5)) - SCALE(2); - - switch (library_section) { - case 'lib_bg': - { - const libColors = new StringInput('lib_bg_01', 'ui.col.bg', customColor.ui_col_bg, x, y, labelW, inputW); - controlList.push(libColors); - controlList.push(new ColorPicker('lib_bg_01', customColor.ui_col_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_01', customColor.ui_col_bg, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_bg_02', 'ui.col.rowStripes', customColor.ui_col_rowStripes, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_bg_02', customColor.ui_col_rowStripes, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_02', customColor.ui_col_rowStripes, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_bg_03', 'ui.col.nowPlayingBg', customColor.ui_col_nowPlayingBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_bg_03', customColor.ui_col_nowPlayingBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_03', customColor.ui_col_nowPlayingBg, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_bg_04', 'ui.col.sideMarker', customColor.ui_col_sideMarker, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_bg_04', customColor.ui_col_sideMarker, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_04', customColor.ui_col_sideMarker, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_bg_05', 'ui.col.selectionFrame', customColor.ui_col_selectionFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_bg_05', customColor.ui_col_selectionFrame, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_05', customColor.ui_col_selectionFrame, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_bg_06', 'ui.col.selectionFrame2', customColor.ui_col_selectionFrame2, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_bg_06', customColor.ui_col_selectionFrame2, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_06', customColor.ui_col_selectionFrame2, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_bg_07', 'ui.col.hoverFrame', customColor.ui_col_hoverFrame, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_bg_07', customColor.ui_col_hoverFrame, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_bg_07', customColor.ui_col_hoverFrame, markerX, y, () => {})); - } - break; - case 'lib_text': - { - const libColors = new StringInput('lib_text_01', 'ui.col.text', customColor.ui_col_text, x, y, labelW, inputW); - controlList.push(libColors); - controlList.push(new ColorPicker('lib_text_01', customColor.ui_col_text, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_01', customColor.ui_col_text, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_02', 'ui.col.text_h', customColor.ui_col_text_h, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_02', customColor.ui_col_text_h, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_02', customColor.ui_col_text_h, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_03', 'ui.col.text_nowp', customColor.ui_col_text_nowp, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_03', customColor.ui_col_text_nowp, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_03', customColor.ui_col_text_nowp, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_04', 'ui.col.textSel', customColor.ui_col_textSel, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_04', customColor.ui_col_textSel, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_04', customColor.ui_col_textSel, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_05', 'ui.col.txt', customColor.ui_col_txt, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_05', customColor.ui_col_txt, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_05', customColor.ui_col_txt, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_06', 'ui.col.txt_h', customColor.ui_col_txt_h, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_06', customColor.ui_col_txt_h, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_06', customColor.ui_col_txt_h, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_07', 'ui.col.txt_box', customColor.ui_col_txt_box, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_07', customColor.ui_col_txt_box, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_07', customColor.ui_col_txt_box, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_text_08', 'ui.col.search', customColor.ui_col_search, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_text_08', customColor.ui_col_search, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_text_08', customColor.ui_col_search, markerX, y, () => {})); - } - break; - case 'lib_node': - { - const libColors = new StringInput('lib_node_01', 'ui.col.iconPlus', customColor.ui_col_iconPlus, x, y, labelW, inputW); - controlList.push(libColors); - controlList.push(new ColorPicker('lib_node_01', customColor.ui_col_iconPlus, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_01', customColor.ui_col_iconPlus, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_node_02', 'ui.col.iconPlus_h', customColor.ui_col_iconPlus_h, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_node_02', customColor.ui_col_iconPlus_h, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_02', customColor.ui_col_iconPlus_h, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_node_03', 'ui.col.iconPlus_sel', customColor.ui_col_iconPlus_sel, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_node_03', customColor.ui_col_iconPlus_sel, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_03', customColor.ui_col_iconPlus_sel, markerX, y, () => {})); - y += libColors.h + margin; - - if (pref.libraryDesign === 'traditional') { - controlList.push(new StringInput('lib_node_04', 'ui.col.iconPlusBg', customColor.ui_col_iconPlusBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_node_04', customColor.ui_col_iconPlusBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_04', customColor.ui_col_iconPlusBg, markerX, y, () => {})); + if (grSet.libraryDesign === 'traditional') { + CustomMenu.controlList.push(new CustomMenuInputField('lib_node_04', 'lib.ui.col.iconPlusBg', grCfg.cTheme.lib_ui_col_iconPlusBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_04', grCfg.cTheme.lib_ui_col_iconPlusBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_04', grCfg.cTheme.lib_ui_col_iconPlusBg, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_node_05', 'lib.ui.col.iconMinus_e', grCfg.cTheme.lib_ui_col_iconMinus_e, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_05', grCfg.cTheme.lib_ui_col_iconMinus_e, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_05', grCfg.cTheme.lib_ui_col_iconMinus_e, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_node_06', 'lib.ui.col.iconMinus_c', grCfg.cTheme.lib_ui_col_iconMinus_c, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_06', grCfg.cTheme.lib_ui_col_iconMinus_c, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_06', grCfg.cTheme.lib_ui_col_iconMinus_c, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_node_07', 'lib.ui.col.iconMinus_h', grCfg.cTheme.lib_ui_col_iconMinus_h, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_node_07', grCfg.cTheme.lib_ui_col_iconMinus_h, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_node_07', grCfg.cTheme.lib_ui_col_iconMinus_h, markerX, y, () => {})); + } + } + break; + case 'lib_btns': + { + const libColors = new CustomMenuInputField('lib_btns_01', 'lib.ui.col.searchBtn', grCfg.cTheme.lib_ui_col_searchBtn, x, y, labelW, inputW); + CustomMenu.controlList.push(libColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_01', grCfg.cTheme.lib_ui_col_searchBtn, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_01', grCfg.cTheme.lib_ui_col_searchBtn, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_02', 'lib.ui.col.crossBtn', grCfg.cTheme.lib_ui_col_crossBtn, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_02', grCfg.cTheme.lib_ui_col_crossBtn, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_02', grCfg.cTheme.lib_ui_col_crossBtn, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_03', 'lib.ui.col.filterBtn', grCfg.cTheme.lib_ui_col_filterBtn, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_03', grCfg.cTheme.lib_ui_col_filterBtn, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_03', grCfg.cTheme.lib_ui_col_filterBtn, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_04', 'lib.ui.col.settingsBtn', grCfg.cTheme.lib_ui_col_settingsBtn, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_04', grCfg.cTheme.lib_ui_col_settingsBtn, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_04', grCfg.cTheme.lib_ui_col_settingsBtn, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_05', 'lib.ui.col.line', grCfg.cTheme.lib_ui_col_line, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_05', grCfg.cTheme.lib_ui_col_line, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_05', grCfg.cTheme.lib_ui_col_line, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_06', 'lib.ui.col.s_line', grCfg.cTheme.lib_ui_col_s_line, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_06', grCfg.cTheme.lib_ui_col_s_line, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_06', grCfg.cTheme.lib_ui_col_s_line, markerX, y, () => {})); y += libColors.h + margin; - controlList.push(new StringInput('lib_node_05', 'ui.col.iconMinus_e', customColor.ui_col_iconMinus_e, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_node_05', customColor.ui_col_iconMinus_e, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_05', customColor.ui_col_iconMinus_e, markerX, y, () => {})); + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_07', 'lib.ui.col.sbarBtns', grCfg.cTheme.lib_ui_col_sbarBtns, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_07', grCfg.cTheme.lib_ui_col_sbarBtns, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_07', grCfg.cTheme.lib_ui_col_sbarBtns, markerX, y, () => {})); y += libColors.h + margin; - controlList.push(new StringInput('lib_node_06', 'ui.col.iconMinus_c', customColor.ui_col_iconMinus_c, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_node_06', customColor.ui_col_iconMinus_c, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_06', customColor.ui_col_iconMinus_c, markerX, y, () => {})); + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_08', 'lib.ui.col.sbarNormal', grCfg.cTheme.lib_ui_col_sbarNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_08', grCfg.cTheme.lib_ui_col_sbarNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_08', grCfg.cTheme.lib_ui_col_sbarNormal, markerX, y, () => {})); y += libColors.h + margin; - controlList.push(new StringInput('lib_node_07', 'ui.col.iconMinus_h', customColor.ui_col_iconMinus_h, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_node_07', customColor.ui_col_iconMinus_h, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_node_07', customColor.ui_col_iconMinus_h, markerX, y, () => {})); + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_09', 'lib.ui.col.sbarHovered', grCfg.cTheme.lib_ui_col_sbarHovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_09', grCfg.cTheme.lib_ui_col_sbarHovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_09', grCfg.cTheme.lib_ui_col_sbarHovered, markerX, y, () => {})); + y += libColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('lib_btns_10', 'lib.ui.col.sbarDrag', grCfg.cTheme.lib_ui_col_sbarDrag, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('lib_btns_10', grCfg.cTheme.lib_ui_col_sbarDrag, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('lib_btns_10', grCfg.cTheme.lib_ui_col_sbarDrag, markerX, y, () => {})); } - } - break; - case 'lib_btns': - { - const libColors = new StringInput('lib_btns_01', 'ui.col.searchBtn', customColor.ui_col_searchBtn, x, y, labelW, inputW); - controlList.push(libColors); - controlList.push(new ColorPicker('lib_btns_01', customColor.ui_col_searchBtn, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_01', customColor.ui_col_searchBtn, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_02', 'ui.col.crossBtn', customColor.ui_col_crossBtn, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_02', customColor.ui_col_crossBtn, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_02', customColor.ui_col_crossBtn, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_03', 'ui.col.filterBtn', customColor.ui_col_filterBtn, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_03', customColor.ui_col_filterBtn, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_03', customColor.ui_col_filterBtn, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_04', 'ui.col.settingsBtn', customColor.ui_col_settingsBtn, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_04', customColor.ui_col_settingsBtn, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_04', customColor.ui_col_settingsBtn, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_05', 'ui.col.line', customColor.ui_col_line, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_05', customColor.ui_col_line, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_05', customColor.ui_col_line, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_06', 'ui.col.s_line', customColor.ui_col_s_line, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_06', customColor.ui_col_s_line, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_06', customColor.ui_col_s_line, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_07', 'ui.col.sbarBtns', customColor.ui_col_sbarBtns, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_07', customColor.ui_col_sbarBtns, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_07', customColor.ui_col_sbarBtns, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_08', 'ui.col.sbarNormal', customColor.ui_col_sbarNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_08', customColor.ui_col_sbarNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_08', customColor.ui_col_sbarNormal, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_09', 'ui.col.sbarHovered', customColor.ui_col_sbarHovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_09', customColor.ui_col_sbarHovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_09', customColor.ui_col_sbarHovered, markerX, y, () => {})); - y += libColors.h + margin; - controlList.push(new StringInput('lib_btns_10', 'ui.col.sbarDrag', customColor.ui_col_sbarDrag, x, y, labelW, inputW)); - controlList.push(new ColorPicker('lib_btns_10', customColor.ui_col_sbarDrag, x + 2, y, () => {})); - controlList.push(new ColorMarker('lib_btns_10', customColor.ui_col_sbarDrag, markerX, y, () => {})); - } - break; + break; + } } -} - -/** - * The Biography colors in the custom theme menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} biography_section The biography color page to be opened. - */ -function customBiographyColors(x, y, w, h, biography_section) { - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const margin = SCALE(20); - const labelW = SCALE(300) + popupFontSize; - const inputW = SCALE(80) + popupFontSize; - const markerX = x + margin + inputW + (popupFontSize * (RES_4K ? 0.25 : 0.5)) - SCALE(2); - - switch (biography_section) { - case 'bio_bg': - { - const bioColors = new StringInput('bio_bg_01', 'uiBio.col.bg', customColor.uiBio_col_bg, x, y, labelW, inputW); - controlList.push(bioColors); - controlList.push(new ColorPicker('bio_bg_01', customColor.uiBio_col_bg, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_bg_01', customColor.uiBio_col_bg, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_bg_02', 'uiBio.col.rowStripes', customColor.uiBio_col_rowStripes, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_bg_02', customColor.uiBio_col_rowStripes, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_bg_02', customColor.uiBio_col_rowStripes, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_bg_03', 'uiBio.col.noPhotoStubBg', customColor.uiBio_col_noPhotoStubBg, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_bg_03', customColor.uiBio_col_noPhotoStubBg, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_bg_03', customColor.uiBio_col_noPhotoStubBg, markerX, y, () => {})); - } - break; - case 'bio_text': - { - const bioColors = new StringInput('bio_text_01', 'uiBio.col.headingText', customColor.uiBio_col_headingText, x, y, labelW, inputW); - controlList.push(bioColors); - controlList.push(new ColorPicker('bio_text_01', customColor.uiBio_col_headingText, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_01', customColor.uiBio_col_headingText, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_02', 'uiBio.col.source', customColor.uiBio_col_source, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_02', customColor.uiBio_col_source, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_02', customColor.uiBio_col_source, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_03', 'uiBio.col.accent', customColor.uiBio_col_accent, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_03', customColor.uiBio_col_accent, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_03', customColor.uiBio_col_accent, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_04', 'uiBio.col.summary', customColor.uiBio_col_summary, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_04', customColor.uiBio_col_summary, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_04', customColor.uiBio_col_summary, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_05', 'uiBio.col.text', customColor.uiBio_col_text, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_05', customColor.uiBio_col_text, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_05', customColor.uiBio_col_text, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_06', 'uiBio.col.lyricsNormal', customColor.uiBio_col_lyricsNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_06', customColor.uiBio_col_lyricsNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_06', customColor.uiBio_col_lyricsNormal, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_07', 'uiBio.col.lyricsHighlight', customColor.uiBio_col_lyricsHighlight, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_07', customColor.uiBio_col_lyricsHighlight, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_07', customColor.uiBio_col_lyricsHighlight, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_text_08', 'uiBio.col.noPhotoStubText', customColor.uiBio_col_noPhotoStubText, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_text_08', customColor.uiBio_col_noPhotoStubText, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_text_08', customColor.uiBio_col_noPhotoStubText, markerX, y, () => {})); - } - break; - case 'bio_misc': - { - const bioColors = new StringInput('bio_misc_01', 'uiBio.col.bottomLine', customColor.uiBio_col_bottomLine, x, y, labelW, inputW); - controlList.push(bioColors); - controlList.push(new ColorPicker('bio_misc_01', customColor.uiBio_col_bottomLine, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_misc_01', customColor.uiBio_col_bottomLine, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_misc_02', 'uiBio.col.centerLine', customColor.uiBio_col_centerLine, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_misc_02', customColor.uiBio_col_centerLine, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_misc_02', customColor.uiBio_col_centerLine, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_misc_03', 'uiBio.col.sectionLine', customColor.uiBio_col_sectionLine, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_misc_03', customColor.uiBio_col_sectionLine, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_misc_03', customColor.uiBio_col_sectionLine, markerX, y, () => {})); - } - break; - case 'bio_btns': - { - const bioColors = new StringInput('bio_btns_01', 'uiBio.col.sbarBtns', customColor.uiBio_col_sbarBtns, x, y, labelW, inputW); - controlList.push(bioColors); - controlList.push(new ColorPicker('bio_btns_01', customColor.uiBio_col_sbarBtns, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_btns_01', customColor.uiBio_col_sbarBtns, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_btns_02', 'uiBio.col.sbarNormal', customColor.uiBio_col_sbarNormal, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_btns_02', customColor.uiBio_col_sbarNormal, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_btns_02', customColor.uiBio_col_sbarNormal, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_btns_03', 'uiBio.col.sbarHovered', customColor.uiBio_col_sbarHovered, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_btns_03', customColor.uiBio_col_sbarHovered, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_btns_03', customColor.uiBio_col_sbarHovered, markerX, y, () => {})); - y += bioColors.h + margin; - controlList.push(new StringInput('bio_btns_04', 'uiBio.col.sbarDrag', customColor.uiBio_col_sbarDrag, x, y, labelW, inputW)); - controlList.push(new ColorPicker('bio_btns_04', customColor.uiBio_col_sbarDrag, x + 2, y, () => {})); - controlList.push(new ColorMarker('bio_btns_04', customColor.uiBio_col_sbarDrag, markerX, y, () => {})); - } - break; + /** + * The Biography colors in the custom theme menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} biography_section - The biography color page to be opened. + * @private + */ + _customBiographyColors(x, y, w, h, biography_section) { + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const margin = SCALE(20); + const labelW = SCALE(300) + popupFontSize; + const inputW = SCALE(80) + popupFontSize; + const markerX = x + margin + inputW + (popupFontSize * (RES._4K ? 0.25 : 0.5)) - SCALE(2); + + switch (biography_section) { + case 'bio_bg': + { + const bioColors = new CustomMenuInputField('bio_bg_01', 'bio.ui.col.bg', grCfg.cTheme.bio_ui_col_bg, x, y, labelW, inputW); + CustomMenu.controlList.push(bioColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_bg_01', grCfg.cTheme.bio_ui_col_bg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_bg_01', grCfg.cTheme.bio_ui_col_bg, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_bg_02', 'bio.ui.col.rowStripes', grCfg.cTheme.bio_ui_col_rowStripes, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_bg_02', grCfg.cTheme.bio_ui_col_rowStripes, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_bg_02', grCfg.cTheme.bio_ui_col_rowStripes, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_bg_03', 'bio.ui.col.noPhotoStubBg', grCfg.cTheme.bio_ui_col_noPhotoStubBg, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_bg_03', grCfg.cTheme.bio_ui_col_noPhotoStubBg, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_bg_03', grCfg.cTheme.bio_ui_col_noPhotoStubBg, markerX, y, () => {})); + } + break; + case 'bio_text': + { + const bioColors = new CustomMenuInputField('bio_text_01', 'bio.ui.col.headingText', grCfg.cTheme.bio_ui_col_headingText, x, y, labelW, inputW); + CustomMenu.controlList.push(bioColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_01', grCfg.cTheme.bio_ui_col_headingText, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_01', grCfg.cTheme.bio_ui_col_headingText, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_02', 'bio.ui.col.source', grCfg.cTheme.bio_ui_col_source, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_02', grCfg.cTheme.bio_ui_col_source, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_02', grCfg.cTheme.bio_ui_col_source, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_03', 'bio.ui.col.accent', grCfg.cTheme.bio_ui_col_accent, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_03', grCfg.cTheme.bio_ui_col_accent, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_03', grCfg.cTheme.bio_ui_col_accent, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_04', 'bio.ui.col.summary', grCfg.cTheme.bio_ui_col_summary, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_04', grCfg.cTheme.bio_ui_col_summary, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_04', grCfg.cTheme.bio_ui_col_summary, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_05', 'bio.ui.col.text', grCfg.cTheme.bio_ui_col_text, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_05', grCfg.cTheme.bio_ui_col_text, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_05', grCfg.cTheme.bio_ui_col_text, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_06', 'bio.ui.col.lyricsNormal', grCfg.cTheme.bio_ui_col_lyricsNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_06', grCfg.cTheme.bio_ui_col_lyricsNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_06', grCfg.cTheme.bio_ui_col_lyricsNormal, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_07', 'bio.ui.col.lyricsHighlight', grCfg.cTheme.bio_ui_col_lyricsHighlight, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_07', grCfg.cTheme.bio_ui_col_lyricsHighlight, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_07', grCfg.cTheme.bio_ui_col_lyricsHighlight, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_text_08', 'bio.ui.col.noPhotoStubText', grCfg.cTheme.bio_ui_col_noPhotoStubText, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_text_08', grCfg.cTheme.bio_ui_col_noPhotoStubText, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_text_08', grCfg.cTheme.bio_ui_col_noPhotoStubText, markerX, y, () => {})); + } + break; + case 'bio_misc': + { + const bioColors = new CustomMenuInputField('bio_misc_01', 'bio.ui.col.bottomLine', grCfg.cTheme.bio_ui_col_bottomLine, x, y, labelW, inputW); + CustomMenu.controlList.push(bioColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_misc_01', grCfg.cTheme.bio_ui_col_bottomLine, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_misc_01', grCfg.cTheme.bio_ui_col_bottomLine, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_misc_02', 'bio.ui.col.centerLine', grCfg.cTheme.bio_ui_col_centerLine, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_misc_02', grCfg.cTheme.bio_ui_col_centerLine, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_misc_02', grCfg.cTheme.bio_ui_col_centerLine, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_misc_03', 'bio.ui.col.sectionLine', grCfg.cTheme.bio_ui_col_sectionLine, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_misc_03', grCfg.cTheme.bio_ui_col_sectionLine, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_misc_03', grCfg.cTheme.bio_ui_col_sectionLine, markerX, y, () => {})); + } + break; + case 'bio_btns': + { + const bioColors = new CustomMenuInputField('bio_btns_01', 'bio.ui.col.sbarBtns', grCfg.cTheme.bio_ui_col_sbarBtns, x, y, labelW, inputW); + CustomMenu.controlList.push(bioColors); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_btns_01', grCfg.cTheme.bio_ui_col_sbarBtns, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_btns_01', grCfg.cTheme.bio_ui_col_sbarBtns, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_btns_02', 'bio.ui.col.sbarNormal', grCfg.cTheme.bio_ui_col_sbarNormal, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_btns_02', grCfg.cTheme.bio_ui_col_sbarNormal, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_btns_02', grCfg.cTheme.bio_ui_col_sbarNormal, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_btns_03', 'bio.ui.col.sbarHovered', grCfg.cTheme.bio_ui_col_sbarHovered, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_btns_03', grCfg.cTheme.bio_ui_col_sbarHovered, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_btns_03', grCfg.cTheme.bio_ui_col_sbarHovered, markerX, y, () => {})); + y += bioColors.h + margin; + CustomMenu.controlList.push(new CustomMenuInputField('bio_btns_04', 'bio.ui.col.sbarDrag', grCfg.cTheme.bio_ui_col_sbarDrag, x, y, labelW, inputW)); + CustomMenu.controlList.push(new CustomMenuColorPicker('bio_btns_04', grCfg.cTheme.bio_ui_col_sbarDrag, x + 2, y, () => {})); + CustomMenu.controlList.push(new CustomMenuColorMarker('bio_btns_04', grCfg.cTheme.bio_ui_col_sbarDrag, markerX, y, () => {})); + } + break; + } } -} - - -/** - * Sets the info page in the custom theme menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} link The url to be opened. - */ -function customThemeInfo(x, y, w, h, link) { - link = 'https://github.com/TT-ReBORN/Georgia-ReBORN/discussions/99'; - const margin = SCALE(20); - const maxWidth = (ww * 0.5) - (margin * 4); - const text = 'You can modify the main colors, the playlist colors, the library colors and the biography colors. First select a custom theme slot in the drop down menu "Options" that you want to modify. You can either select the color via the color picker or paste a HEX value in the input field.\nIt will apply all changes in real time and saves it automatically in the georgia-reborn-custom.jsonc config file. Each color has a name that you can also find in the georgia-reborn-custom.jsonc config file and modify it there.\n\nTo reset the colors to the default ones, select the "Reset" option from the drop down menu.\n\nTip: Download the resource pack from the Github page to open the custom theme template and modify colors in Photoshop or Gimp.\nIf you are happy with the result, just copy and paste the HEX values.\n\nYou can showcase your custom themes and share your configs here: Click on this text.'; + /** + * Sets the info page in the custom theme menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} link - The url to be opened. + * @private + */ + _customThemeInfo(x, y, w, h, link) { + link = 'https://github.com/TT-ReBORN/Georgia-ReBORN/discussions/99'; + const margin = SCALE(20); + const maxWidth = (grm.ui.ww * 0.5) - (margin * 4); + + const text = 'You can modify the main colors, the playlist colors, the library colors and the biography colors. First select a custom theme slot in the drop down menu "Options" that you want to modify. You can either select the color via the color picker or paste a HEX value in the input field.\nIt will apply all changes in real time and saves it automatically in the georgia-reborn-custom.jsonc config file. Each color has a name that you can also find in the georgia-reborn-custom.jsonc config file and modify it there.\n\nTo reset the colors to the default ones, select the "Reset" option from the drop down menu.\n\nTip: Download the resource pack from the Github page to open the custom theme template and modify colors in Photoshop or Gimp.\nIf you are happy with the result, just copy and paste the HEX values.\n\nYou can showcase your custom themes and share your configs here: Click on this text.'; + + const height = MeasureString(text, grFont.popup, 0, 0, maxWidth, grm.ui.wh).Height; + + const info = new CustomMenuInfo(x, y, maxWidth, height, text, link); + CustomMenu.controlList.push(info); + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Initializes the custom theme menu with panel sections and options for customizing the theme colors. + * @param {string} playlist_section - The playlist color page to be opened. + * @param {string} main_section - The main color page to be opened. + * @param {string} library_section - The library color page to be opened. + * @param {string} biography_section - The biography color page to be opened. + * @param {boolean} info - The custom theme menu info page to be opened. + */ + initCustomThemeMenu(playlist_section, main_section, library_section, biography_section, info) { + if (grSet.libraryLayout === 'full') { grSet.libraryLayout = 'normal'; grm.ui.setLibrarySize(); } + if (grSet.biographyLayout === 'full') { grSet.biographyLayout = 'normal'; grm.ui.setBiographySize(); } + if (grSet.lyricsLayout === 'full') { grSet.lyricsLayout = 'normal'; grm.ui.resizeArtwork(true); } + + CustomMenu.controlList = []; + + const margin = SCALE(40); + const baseX = grm.ui.displayBiography || grm.ui.displayLyrics ? grm.ui.ww * 0.5 + margin : grm.ui.displayDetails ? grm.ui.noAlbumArtStub ? grm.ui.ww * 0.3 : grm.ui.albumArtSize.x + margin : margin; + let x = baseX; + let y = grm.ui.topMenuHeight + margin * 0.75; + const w = grm.ui.ww * 0.5; + const h = grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight; + + const mainSection = ['main_pre', 'main_bg', 'main_bar', 'main_bar2', 'main_bar3', 'main_text', 'main_btns', 'main_btns2', 'main_style'].includes(main_section); + const playlistSection = ['pl_bg', 'pl_text1', 'pl_text2', 'pl_misc', 'pl_btns'].includes(playlist_section); + const librarySection = ['lib_bg', 'lib_text', 'lib_node', 'lib_btns'].includes(library_section); + const biographySection = ['bio_bg', 'bio_text', 'bio_misc', 'bio_btns'].includes(biography_section); + + const menu = new CustomMenuDropDown(x, y, 'Main', ['Pre', 'Bg', 'Bar', 'Bar 2', 'Bar 3', 'Text', 'Btns', 'Btns 2', 'Style'], 0); + CustomMenu.controlList.push(menu); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Playlist', ['Bg', 'Text', 'Text 2', 'Misc', 'Btns'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Library', ['Bg', 'Text', 'Node', 'Btns'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Biography', ['Bg', 'Text', 'Misc', 'Btns'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Options', ['Info', '', 'Theme 01', 'Theme 02', 'Theme 03', 'Theme 04', 'Theme 05', 'Theme 06', 'Theme 07', 'Theme 08', 'Theme 09', 'Theme 10', '', 'Rename', 'Reset'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, '\u2715', [''])); + x = baseX; + y += menu.h + margin * 0.75; + + switch (true) { + case playlistSection: this._customPlaylistColors(x, y, w, h, playlist_section); break; + case mainSection: this._customMainColors(x, y, w, h, main_section); break; + case librarySection: this._customLibraryColors(x, y, w, h, library_section); break; + case biographySection: this._customBiographyColors(x, y, w, h, biography_section); break; + case info: this._customThemeInfo(x, y, w, h); break; + } + } - const height = MeasureString(text, ft.popup, 0, 0, maxWidth, wh).Height; + /** + * Reinitializes the custom theme menu. + */ + reinitCustomThemeMenu() { + if (!grm.ui.displayCustomThemeMenu) return; + if (grm.ui.displayPlaylist) this.initCustomThemeMenu('pl_bg'); + if (grm.ui.displayDetails) this.initCustomThemeMenu(false, 'main_bg'); + if (grm.ui.displayLibrary) this.initCustomThemeMenu(false, false, 'lib_bg'); + if (grm.ui.displayBiography) this.initCustomThemeMenu(false, false, false, 'bio_bg'); + if (grm.ui.displayLyrics) this.initCustomThemeMenu(false, 'main_text'); + } - const info = new Info(x, y, maxWidth, height, text, link); - controlList.push(info); -} + /** + * Resets all colors in the active custom theme to defaults. + */ + resetCustomTheme() { + const customThemes = { + custom01: { schema: grDef.customTheme01Schema, customTheme: 'customTheme01' }, + custom02: { schema: grDef.customTheme02Schema, customTheme: 'customTheme02' }, + custom03: { schema: grDef.customTheme03Schema, customTheme: 'customTheme03' }, + custom04: { schema: grDef.customTheme04Schema, customTheme: 'customTheme04' }, + custom05: { schema: grDef.customTheme05Schema, customTheme: 'customTheme05' }, + custom06: { schema: grDef.customTheme06Schema, customTheme: 'customTheme06' }, + custom07: { schema: grDef.customTheme07Schema, customTheme: 'customTheme07' }, + custom08: { schema: grDef.customTheme08Schema, customTheme: 'customTheme08' }, + custom09: { schema: grDef.customTheme09Schema, customTheme: 'customTheme09' }, + custom10: { schema: grDef.customTheme10Schema, customTheme: 'customTheme10' } + }; + if (!customThemes[grSet.theme]) return; + + const { schema, customTheme } = customThemes[grSet.theme]; + const themeDefaults = grCfg.configCustom.updateConfigObjValues(customTheme, grDef.customThemeDefaults, true); + grCfg.cTheme = grCfg.configCustom.addConfigurationObject(schema, Object.assign({}, themeDefaults, grDef.customThemeDefaults), grDef.customThemeComments); + } + + /** + * Updates all colors in the georgia-reborn-custom config file. + * @param {string} id - The id of the UI element. + * @param {string} value - The color of the UI element. + */ + updateCustomThemesConfig(id, value) { + const customThemeColors = { + // * PRELOADER * // + main_pre_01: 'grCol_preloaderBg', + main_pre_02: 'grCol_preloaderLogo', + main_pre_03: 'grCol_preloaderLowerBarTitle', + main_pre_04: 'grCol_preloaderProgressBar', + main_pre_05: 'grCol_preloaderProgressBarFill', + main_pre_06: 'grCol_preloaderProgressBarFrame', + main_pre_07: 'grCol_preloaderUIHacksFrame', + // * MAIN - BG * // + main_bg_01: 'grCol_bg', + main_bg_02: 'grCol_popupBg', + main_bg_03: 'grCol_detailsBg', + main_bg_04: 'grCol_shadow', + main_bg_05: 'grCol_discArtShadow', + main_bg_06: 'grCol_noAlbumArtStub', + // * MAIN - BAR * // + main_bar_01: 'grCol_timelineAdded', + main_bar_02: 'grCol_timelinePlayed', + main_bar_03: 'grCol_timelineUnplayed', + main_bar_04: 'grCol_timelineFrame', + main_bar_05: 'grCol_progressBar', + main_bar_06: 'grCol_progressBarStreaming', + main_bar_07: 'grCol_progressBarFrame', + main_bar_08: 'grCol_progressBarFill', + main_bar_09: 'grCol_volumeBar', + main_bar_10: 'grCol_volumeBarFrame', + main_bar_11: 'grCol_volumeBarFill', + // * MAIN - BAR 2 * // + main_bar_12: 'grCol_peakmeterBarProg', + main_bar_13: 'grCol_peakmeterBarProgFill', + main_bar_14: 'grCol_peakmeterBarFillTop', + main_bar_15: 'grCol_peakmeterBarFillMiddle', + main_bar_16: 'grCol_peakmeterBarFillBack', + main_bar_17: 'grCol_peakmeterBarVertProgFill', + main_bar_18: 'grCol_peakmeterBarVertFill', + main_bar_19: 'grCol_peakmeterBarVertFillPeaks', + // * MAIN - BAR 3 * // + main_bar_20: 'grCol_waveformBarFillFront', + main_bar_21: 'grCol_waveformBarFillBack', + main_bar_22: 'grCol_waveformBarFillPreFront', + main_bar_23: 'grCol_waveformBarFillPreBack', + main_bar_24: 'grCol_waveformBarIndicator', + // * MAIN - TEXT * // + main_text_01: 'grCol_lowerBarArtist', + main_text_02: 'grCol_lowerBarTitle', + main_text_03: 'grCol_lowerBarTime', + main_text_04: 'grCol_lowerBarLength', + main_text_05: 'grCol_detailsText', + main_text_06: 'grCol_detailsRating', + main_text_08: 'grCol_popupText', + main_text_09: 'grCol_lyricsNormal', + main_text_10: 'grCol_lyricsHighlight', + main_text_11: 'grCol_lyricsShadow', + // * MAIN - BTNS * // + main_btns_01: 'grCol_menuBgColor', + main_btns_02: 'grCol_menuStyleBg', + main_btns_03: 'grCol_menuRectStyleEmbossTop', + main_btns_04: 'grCol_menuRectStyleEmbossBottom', + main_btns_05: 'grCol_menuRectNormal', + main_btns_06: 'grCol_menuRectHovered', + main_btns_07: 'grCol_menuRectDown', + main_btns_08: 'grCol_menuTextNormal', + main_btns_09: 'grCol_menuTextHovered', + main_btns_10: 'grCol_menuTextDown', + // * MAIN - BTNS 2 * // + main_btns_11: 'grCol_transportEllipseBg', + main_btns_12: 'grCol_transportEllipseNormal', + main_btns_13: 'grCol_transportEllipseHovered', + main_btns_14: 'grCol_transportEllipseDown', + main_btns_15: 'grCol_transportStyleBg', + main_btns_16: 'grCol_transportStyleTop', + main_btns_17: 'grCol_transportStyleBottom', + main_btns_18: 'grCol_transportIconNormal', + main_btns_19: 'grCol_transportIconHovered', + main_btns_20: 'grCol_transportIconDown', + // * MAIN - STYLE * // + main_style_01: 'grCol_styleBevel', + main_style_02: 'grCol_styleGradient', + main_style_03: 'grCol_styleGradient2', + main_style_04: 'grCol_styleProgressBar', + main_style_05: 'grCol_styleProgressBarLineTop', + main_style_06: 'grCol_styleProgressBarLineBottom', + main_style_07: 'grCol_styleProgressBarFill', + main_style_08: 'grCol_styleVolumeBar', + main_style_09: 'grCol_styleVolumeBarFill', + + // * PLAYLIST - BG * // + pl_bg_01: 'pl_col_bg', + pl_bg_02: 'pl_col_header_nowplaying_bg', + pl_bg_03: 'pl_col_header_sideMarker', + pl_bg_04: 'pl_col_row_nowplaying_bg', + pl_bg_05: 'pl_col_row_stripes_bg', + pl_bg_06: 'pl_col_row_sideMarker', + // * PLAYLIST - TEXT * // + pl_text_01: 'pl_col_plman_text_normal', + pl_text_02: 'pl_col_plman_text_hovered', + pl_text_03: 'pl_col_plman_text_pressed', + pl_text_04: 'pl_col_header_artist_normal', + pl_text_05: 'pl_col_header_artist_playing', + pl_text_06: 'pl_col_header_album_normal', + pl_text_07: 'pl_col_header_album_playing', + pl_text_08: 'pl_col_header_info_normal', + pl_text_09: 'pl_col_header_info_playing', + pl_text_10: 'pl_col_header_date_normal', + pl_text_11: 'pl_col_header_date_playing', + pl_text_12: 'pl_col_row_title_normal', + pl_text_13: 'pl_col_row_title_playing', + pl_text_14: 'pl_col_row_title_selected', + pl_text_15: 'pl_col_row_title_hovered', + // * PLAYLIST - MISC * // + pl_misc_01: 'pl_col_header_line_normal', + pl_misc_02: 'pl_col_header_line_playing', + pl_misc_03: 'pl_col_row_disc_subheader_line', + pl_misc_04: 'pl_col_row_drag_line', + pl_misc_05: 'pl_col_row_drag_line_reached', + pl_misc_06: 'pl_col_row_selection_frame', + pl_misc_07: 'pl_col_row_rating_color', + // * PLAYLIST - BTNS * // + pl_btns_01: 'pl_col_sbar_btn_normal', + pl_btns_02: 'pl_col_sbar_btn_hovered', + pl_btns_03: 'pl_col_sbar_thumb_normal', + pl_btns_04: 'pl_col_sbar_thumb_hovered', + pl_btns_05: 'pl_col_sbar_thumb_drag', + + // * LIBRARY - BG * // + lib_bg_01: 'lib_ui_col_bg', + lib_bg_02: 'lib_ui_col_rowStripes', + lib_bg_03: 'lib_ui_col_nowPlayingBg', + lib_bg_04: 'lib_ui_col_sideMarker', + lib_bg_05: 'lib_ui_col_selectionFrame', + lib_bg_06: 'lib_ui_col_selectionFrame2', + lib_bg_07: 'lib_ui_col_hoverFrame', + // * LIBRARY - TEXT * // + lib_text_01: 'lib_ui_col_text', + lib_text_02: 'lib_ui_col_text_h', + lib_text_03: 'lib_ui_col_text_nowp', + lib_text_04: 'lib_ui_col_textSel', + lib_text_05: 'lib_ui_col_txt', + lib_text_06: 'lib_ui_col_txt_h', + lib_text_07: 'lib_ui_col_txt_box', + lib_text_08: 'lib_ui_col_search', + // * LIBRARY - NODE * // + lib_node_01: 'lib_ui_col_iconPlus', + lib_node_02: 'lib_ui_col_iconPlus_h', + lib_node_03: 'lib_ui_col_iconPlus_sel', + lib_node_04: 'lib_ui_col_iconPlusBg', + lib_node_05: 'lib_ui_col_iconMinus_e', + lib_node_06: 'lib_ui_col_iconMinus_c', + lib_node_07: 'lib_ui_col_iconMinus_h', + // * LIBRARY - BTNS * // + lib_btns_01: 'lib_ui_col_searchBtn', + lib_btns_02: 'lib_ui_col_crossBtn', + lib_btns_03: 'lib_ui_col_filterBtn', + lib_btns_04: 'lib_ui_col_settingsBtn', + lib_btns_05: 'lib_ui_col_line', + lib_btns_06: 'lib_ui_col_s_line', + lib_btns_07: 'lib_ui_col_sbarBtns', + lib_btns_08: 'lib_ui_col_sbarNormal', + lib_btns_09: 'lib_ui_col_sbarHovered', + lib_btns_10: 'lib_ui_col_sbarDrag', + + // * BIOGRAPHY - BG * // + bio_bg_01: 'bio_ui_col_bg', + bio_bg_02: 'bio_ui_col_rowStripes', + bio_bg_03: 'bio_ui_col_noPhotoStubBg', + // * BIOGRAPHY - TEXT * // + bio_text_01: 'bio_ui_col_headingText', + bio_text_02: 'bio_ui_col_source', + bio_text_03: 'bio_ui_col_accent', + bio_text_04: 'bio_ui_col_summary', + bio_text_05: 'bio_ui_col_text', + bio_text_06: 'bio_ui_col_lyricsNormal', + bio_text_07: 'bio_ui_col_lyricsHighlight', + bio_text_08: 'bio_ui_col_noPhotoStubText', + // * BIOGRAPHY - MISC * // + bio_misc_01: 'bio_ui_col_bottomLine', + bio_misc_02: 'bio_ui_col_centerLine', + bio_misc_03: 'bio_ui_col_sectionLine', + // * BIOGRAPHY - BTNS * // + bio_btns_01: 'bio_ui_col_sbarBtns', + bio_btns_02: 'bio_ui_col_sbarNormal', + bio_btns_03: 'bio_ui_col_sbarHovered', + bio_btns_04: 'bio_ui_col_sbarDrag' + }; -/** - * Updates all colors in the georgia-reborn-custom config file. - * @param {string} id The id of the UI element. - * @param {string} value The color of the UI element. - */ -function updateColorsFromConfig(id, value) { - const customThemeColors = { - // * PRELOADER * // - main_pre_01: 'preloaderBg', - main_pre_02: 'preloaderLogo', - main_pre_03: 'preloaderLowerBarTitle', - main_pre_04: 'preloaderProgressBar', - main_pre_05: 'preloaderProgressBarFill', - main_pre_06: 'preloaderProgressBarFrame', - main_pre_07: 'preloaderUIHacksFrame', - // * MAIN - BG * // - main_bg_01: 'col_bg', - main_bg_02: 'col_popupBg', - main_bg_03: 'col_detailsBg', - main_bg_04: 'col_shadow', - main_bg_05: 'col_discArtShadow', - main_bg_06: 'col_noAlbumArtStub', - // * MAIN - BAR * // - main_bar_01: 'col_timelineAdded', - main_bar_02: 'col_timelinePlayed', - main_bar_03: 'col_timelineUnplayed', - main_bar_04: 'col_timelineFrame', - main_bar_05: 'col_progressBar', - main_bar_06: 'col_progressBarStreaming', - main_bar_07: 'col_progressBarFrame', - main_bar_08: 'col_progressBarFill', - main_bar_09: 'col_volumeBar', - main_bar_10: 'col_volumeBarFrame', - main_bar_11: 'col_volumeBarFill', - // * MAIN - BAR 2 * // - main_bar_12: 'col_peakmeterBarProg', - main_bar_13: 'col_peakmeterBarProgFill', - main_bar_14: 'col_peakmeterBarFillTop', - main_bar_15: 'col_peakmeterBarFillMiddle', - main_bar_16: 'col_peakmeterBarFillBack', - main_bar_17: 'col_peakmeterBarVertProgFill', - main_bar_18: 'col_peakmeterBarVertFill', - main_bar_19: 'col_peakmeterBarVertFillPeaks', - // * MAIN - BAR 3 * // - main_bar_20: 'col_waveformBarFillFront', - main_bar_21: 'col_waveformBarFillBack', - main_bar_22: 'col_waveformBarFillPreFront', - main_bar_23: 'col_waveformBarFillPreBack', - main_bar_24: 'col_waveformBarIndicator', - // * MAIN - TEXT * // - main_text_01: 'col_lowerBarArtist', - main_text_02: 'col_lowerBarTitle', - main_text_03: 'col_lowerBarTime', - main_text_04: 'col_lowerBarLength', - main_text_05: 'col_detailsText', - main_text_06: 'col_detailsRating', - main_text_08: 'col_popupText', - main_text_09: 'col_lyricsNormal', - main_text_10: 'col_lyricsHighlight', - main_text_11: 'col_lyricsShadow', - // * MAIN - BTNS * // - main_btns_01: 'col_menuBgColor', - main_btns_02: 'col_menuStyleBg', - main_btns_03: 'col_menuRectStyleEmbossTop', - main_btns_04: 'col_menuRectStyleEmbossBottom', - main_btns_05: 'col_menuRectNormal', - main_btns_06: 'col_menuRectHovered', - main_btns_07: 'col_menuRectDown', - main_btns_08: 'col_menuTextNormal', - main_btns_09: 'col_menuTextHovered', - main_btns_10: 'col_menuTextDown', - // * MAIN - BTNS 2 * // - main_btns_11: 'col_transportEllipseBg', - main_btns_12: 'col_transportEllipseNormal', - main_btns_13: 'col_transportEllipseHovered', - main_btns_14: 'col_transportEllipseDown', - main_btns_15: 'col_transportStyleBg', - main_btns_16: 'col_transportStyleTop', - main_btns_17: 'col_transportStyleBottom', - main_btns_18: 'col_transportIconNormal', - main_btns_19: 'col_transportIconHovered', - main_btns_20: 'col_transportIconDown', - // * MAIN - STYLE * // - main_style_01: 'col_styleBevel', - main_style_02: 'col_styleGradient', - main_style_03: 'col_styleGradient2', - main_style_04: 'col_styleProgressBar', - main_style_05: 'col_styleProgressBarLineTop', - main_style_06: 'col_styleProgressBarLineBottom', - main_style_07: 'col_styleProgressBarFill', - main_style_08: 'col_styleVolumeBar', - main_style_09: 'col_styleVolumeBarFill', - - // * PLAYLIST - BG * // - pl_bg_01: 'g_pl_colors_bg', - pl_bg_02: 'g_pl_colors_header_nowplaying_bg', - pl_bg_03: 'g_pl_colors_header_sideMarker', - pl_bg_04: 'g_pl_colors_row_nowplaying_bg', - pl_bg_05: 'g_pl_colors_row_stripes_bg', - pl_bg_06: 'g_pl_colors_row_sideMarker', - // * PLAYLIST - TEXT * // - pl_text_01: 'g_pl_colors_plman_text_normal', - pl_text_02: 'g_pl_colors_plman_text_hovered', - pl_text_03: 'g_pl_colors_plman_text_pressed', - pl_text_04: 'g_pl_colors_header_artist_normal', - pl_text_05: 'g_pl_colors_header_artist_playing', - pl_text_06: 'g_pl_colors_header_album_normal', - pl_text_07: 'g_pl_colors_header_album_playing', - pl_text_08: 'g_pl_colors_header_info_normal', - pl_text_09: 'g_pl_colors_header_info_playing', - pl_text_10: 'g_pl_colors_header_date_normal', - pl_text_11: 'g_pl_colors_header_date_playing', - pl_text_12: 'g_pl_colors_row_title_normal', - pl_text_13: 'g_pl_colors_row_title_playing', - pl_text_14: 'g_pl_colors_row_title_selected', - pl_text_15: 'g_pl_colors_row_title_hovered', - // * PLAYLIST - MISC * // - pl_misc_01: 'g_pl_colors_header_line_normal', - pl_misc_02: 'g_pl_colors_header_line_playing', - pl_misc_03: 'g_pl_colors_row_disc_subheader_line', - pl_misc_04: 'g_pl_colors_row_drag_line', - pl_misc_05: 'g_pl_colors_row_drag_line_reached', - pl_misc_06: 'g_pl_colors_row_selection_frame', - pl_misc_07: 'g_pl_colors_row_rating_color', - // * PLAYLIST - BTNS * // - pl_btns_01: 'g_pl_colors_sbar_btn_normal', - pl_btns_02: 'g_pl_colors_sbar_btn_hovered', - pl_btns_03: 'g_pl_colors_sbar_thumb_normal', - pl_btns_04: 'g_pl_colors_sbar_thumb_hovered', - pl_btns_05: 'g_pl_colors_sbar_thumb_drag', - - // * LIBRARY - BG * // - lib_bg_01: 'ui_col_bg', - lib_bg_02: 'ui_col_rowStripes', - lib_bg_03: 'ui_col_nowPlayingBg', - lib_bg_04: 'ui_col_sideMarker', - lib_bg_05: 'ui_col_selectionFrame', - lib_bg_06: 'ui_col_selectionFrame2', - lib_bg_07: 'ui_col_hoverFrame', - // * LIBRARY - TEXT * // - lib_text_01: 'ui_col_text', - lib_text_02: 'ui_col_text_h', - lib_text_03: 'ui_col_text_nowp', - lib_text_04: 'ui_col_textSel', - lib_text_05: 'ui_col_txt', - lib_text_06: 'ui_col_txt_h', - lib_text_07: 'ui_col_txt_box', - lib_text_08: 'ui_col_search', - // * LIBRARY - NODE * // - lib_node_01: 'ui_col_iconPlus', - lib_node_02: 'ui_col_iconPlus_h', - lib_node_03: 'ui_col_iconPlus_sel', - lib_node_04: 'ui_col_iconPlusBg', - lib_node_05: 'ui_col_iconMinus_e', - lib_node_06: 'ui_col_iconMinus_c', - lib_node_07: 'ui_col_iconMinus_h', - // * LIBRARY - BTNS * // - lib_btns_01: 'ui_col_searchBtn', - lib_btns_02: 'ui_col_crossBtn', - lib_btns_03: 'ui_col_filterBtn', - lib_btns_04: 'ui_col_settingsBtn', - lib_btns_05: 'ui_col_line', - lib_btns_06: 'ui_col_s_line', - lib_btns_07: 'ui_col_sbarBtns', - lib_btns_08: 'ui_col_sbarNormal', - lib_btns_09: 'ui_col_sbarHovered', - lib_btns_10: 'ui_col_sbarDrag', - - // * BIOGRAPHY - BG * // - bio_bg_01: 'uiBio_col_bg', - bio_bg_02: 'uiBio_col_rowStripes', - bio_bg_03: 'uiBio_col_noPhotoStubBg', - // * BIOGRAPHY - TEXT * // - bio_text_01: 'uiBio_col_headingText', - bio_text_02: 'uiBio_col_source', - bio_text_03: 'uiBio_col_accent', - bio_text_04: 'uiBio_col_summary', - bio_text_05: 'uiBio_col_text', - bio_text_06: 'uiBio_col_lyricsNormal', - bio_text_07: 'uiBio_col_lyricsHighlight', - bio_text_08: 'uiBio_col_noPhotoStubText', - // * BIOGRAPHY - MISC * // - bio_misc_01: 'uiBio_col_bottomLine', - bio_misc_02: 'uiBio_col_centerLine', - bio_misc_03: 'uiBio_col_sectionLine', - // * BIOGRAPHY - BTNS * // - bio_btns_01: 'uiBio_col_sbarBtns', - bio_btns_02: 'uiBio_col_sbarNormal', - bio_btns_03: 'uiBio_col_sbarHovered', - bio_btns_04: 'uiBio_col_sbarDrag' - }; - - // Update custom theme colors - if (Object.prototype.hasOwnProperty.call(customThemeColors, id)) { - customColor[customThemeColors[id]] = value; - } - - // Update control list colors - for (const color of controlList) { - if (color.id === id) { - color.value = value; + // Update custom theme colors + if (Object.prototype.hasOwnProperty.call(customThemeColors, id)) { + grCfg.cTheme[customThemeColors[id]] = value; } - } -} - -/** - * Resets all colors in the active custom theme to defaults. - */ -function resetCustomColors() { - const customTheme = - pref.theme === 'custom01' ? 'customTheme01' : - pref.theme === 'custom02' ? 'customTheme02' : - pref.theme === 'custom03' ? 'customTheme03' : - pref.theme === 'custom04' ? 'customTheme04' : - pref.theme === 'custom05' ? 'customTheme05' : - pref.theme === 'custom06' ? 'customTheme06' : - pref.theme === 'custom07' ? 'customTheme07' : - pref.theme === 'custom08' ? 'customTheme08' : - pref.theme === 'custom09' ? 'customTheme09' : - pref.theme === 'custom10' ? 'customTheme10' : ''; - - const defaults = configCustom.updateConfigObjValues(customTheme, customThemeDefaults, true); - - switch (pref.theme) { - case 'custom01': - customTheme01 = configCustom.addConfigurationObject(customTheme01Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme01; - break; - case 'custom02': - customTheme02 = configCustom.addConfigurationObject(customTheme02Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme02; - break; - case 'custom03': - customTheme03 = configCustom.addConfigurationObject(customTheme03Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme03; - break; - case 'custom04': - customTheme04 = configCustom.addConfigurationObject(customTheme04Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme04; - break; - case 'custom05': - customTheme05 = configCustom.addConfigurationObject(customTheme05Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme05; - break; - case 'custom06': - customTheme06 = configCustom.addConfigurationObject(customTheme06Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme06; - break; - case 'custom07': - customTheme07 = configCustom.addConfigurationObject(customTheme07Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme07; - break; - case 'custom08': - customTheme08 = configCustom.addConfigurationObject(customTheme08Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme08; - break; - case 'custom09': - customTheme09 = configCustom.addConfigurationObject(customTheme09Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme09; - break; - case 'custom10': - customTheme10 = configCustom.addConfigurationObject(customTheme10Schema, Object.assign({}, defaults, customThemeDefaults), customThemeComments); - customColor = customTheme10; - break; + // Update control list colors + for (const color of CustomMenu.controlList) { + if (color.id === id) { + color.value = value; + } + } } + // #endregion } @@ -2822,153 +2893,141 @@ function resetCustomColors() { // * METADATA GRID MENU * // //////////////////////////// /** - * Draws the custom metadata grid menu. - * @param {GdiGraphics} gr + * A class that creates and handles the metadata grid menu. */ -function drawMetadataGridMenu(gr) { - if (!displayMetadataGridMenu || pref.layout !== 'default' || (displayPlaylist || displayLibrary || displayBiography || pref.displayLyrics)) return; - - const x = albumArtSize.x - 1; - const y = geo.topMenuHeight; - const width = ww; - const height = wh - geo.topMenuHeight - geo.lowerBarHeight; - - gr.FillSolidRect(x, y, width, height, g_pl_colors.bg); - for (const c of controlList) c.draw(gr); - - if (activeControl && activeControl instanceof DropDownMenu && activeControl.isSelectUp) { - activeControl.draw(gr); - } -} +class MetadataGridMenu { + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Shows the info page of the custom metadata grid menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {string} link - The url to a website. + * @private + */ + _info(x, y, w, h, link) { + link = 'https://wiki.hydrogenaud.io/index.php?title=Foobar2000:Title_Formatting_Reference'; + const maxWidth = grm.ui.ww * 0.66 - SCALE(100); + const text = 'You can modify existing entries or add your new custom patterns.\nTo confirm changes, press "Enter" or paste a new pattern into the input field.\nAll changes will be applied in real time and automatically saved in the\ngeorgia-reborn-config.jsonc file where it can be also manually modified.\n\nTo reset the metadata grid to its default patterns, select the "Reset" option\nfrom the drop down menu.\n\nTip: To reorder the entries, first copy the ones you want to change in your\nnotepad and paste the label and pattern afterwards.\n\nNote: Not all entries will be displayed if the height of the player size is too small,\nchange to a larger player size if desired.\n\nYou can learn more about patterns here, click on this text.'; -/** - * Initializes the custom metadata grid menu. - * @param {string} page The current page number of the metadata grid. - * @param {boolean} info Displays the metadata grid info page. - */ -function initMetadataGridMenu(page, info) { - controlList = []; - - const margin = SCALE(40); - const baseX = displayBiography || pref.displayLyrics ? ww * 0.5 + margin : displayDetails ? albumArtSize.x + margin : margin; - let x = baseX; - let y = albumArtSize.y + margin * 0.75; - - const menu = new DropDownMenu(x, y, 'Page 1', ['Page 1'], 0); - controlList.push(menu); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Page 2', ['Page 2'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Page 3', ['Page 3'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Page 4', ['Page 4'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, 'Options', ['Info', '', 'Reset'], 0)); - x += controlList[controlList.length - 1].w + 1; controlList.push(new DropDownMenu(x, y, '\u2715', [''])); - x = baseX; - y += menu.h + margin * 0.75; - - if (info) { - metadataGridInfo(x, y, ww * 0.5, wh - geo.topMenuHeight - geo.lowerBarHeight); - } else { - metadataGridPage(x - margin * 0.5, y, ww * 0.5, wh - geo.topMenuHeight - geo.lowerBarHeight, page); + const height = MeasureString(text, grFont.popup, 0, 0, maxWidth, grm.ui.wh).Height; + const info = new CustomMenuInfo(x, y, maxWidth, height, text, link); + CustomMenu.controlList.push(info); } -} - -/** - * Displays the current page of the custom metadata grid menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} page The metadata grid page to display, it can have values from 1 to 4. - */ -function metadataGridPage(x, y, w, h, page) { - const prefs = config.readConfiguration(); - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const margin = SCALE(20); - const inputW = SCALE(80) + popupFontSize; - const start = page === 1 ? 0 : page === 2 ? 8 : page === 3 ? 16 : page === 4 ? 24 : 0; - const end = page === 1 ? 8 : page === 2 ? 16 : page === 3 ? 24 : page === 4 ? 32 : 8; - - for (let i = start; i < end; i++) { - try { - const label = prefs.metadataGrid[i].label ? prefs.metadataGrid[i].label : '""'; - const value = prefs.metadataGrid[i].val ? prefs.metadataGrid[i].val : '""'; - const input = new StringInput(label, '', label, x + 1, y, '', inputW); - controlList.push(input); - controlList.push(new StringInput2(label, '', value, x + inputW, y, '', Math.ceil(ww * 0.5 + inputW - margin))); - y += input.h + margin * 1.25; - } - catch (e) { - break; + /** + * Displays the current page of the custom metadata grid menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} page - The metadata grid page to display, it can have values from 1 to 4. + * @private + */ + _page(x, y, w, h, page) { + const prefs = grCfg.config.readConfiguration(); + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const margin = SCALE(20); + const inputW = SCALE(80) + popupFontSize; + const start = page === 1 ? 0 : page === 2 ? 8 : page === 3 ? 16 : page === 4 ? 24 : 0; + const end = page === 1 ? 8 : page === 2 ? 16 : page === 3 ? 24 : page === 4 ? 32 : 8; + + for (let i = start; i < end; i++) { + try { + const label = prefs.metadataGrid[i].label ? prefs.metadataGrid[i].label : '""'; + const value = prefs.metadataGrid[i].val ? prefs.metadataGrid[i].val : '""'; + const input = new CustomMenuInputField(label, '', label, x + 1, y, '', inputW); + CustomMenu.controlList.push(input); + CustomMenu.controlList.push(new CustomMenuInputField2(label, '', value, x + inputW, y, '', Math.ceil(grm.ui.ww * 0.5 + inputW - margin))); + y += input.h + margin * 1.25; + } + catch (e) { + break; + } } } -} + // #endregion + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Initializes the custom metadata grid menu. + * @param {string} page - The current page number of the metadata grid. + * @param {boolean} info - Displays the metadata grid info page. + */ + initMetadataGridMenu(page, info) { + CustomMenu.controlList = []; -/** - * Shows the info page of the custom metadata grid menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {string} link The url to a website. - */ -function metadataGridInfo(x, y, w, h, link) { - link = 'https://wiki.hydrogenaud.io/index.php?title=Foobar2000:Title_Formatting_Reference'; - const maxWidth = ww * 0.66 - SCALE(100); + const margin = SCALE(40); + const baseX = grm.ui.displayBiography || grm.ui.displayLyrics ? grm.ui.ww * 0.5 + margin : grm.ui.displayDetails ? grm.ui.albumArtSize.x + margin : margin; + let x = baseX; + let y = grm.ui.albumArtSize.y + margin * 0.75; - const text = 'You can modify existing entries or add your new custom patterns.\nTo confirm changes, press "Enter" or paste a new pattern into the input field.\nAll changes will be applied in real time and automatically saved in the\ngeorgia-reborn-config.jsonc file where it can be also manually modified.\n\nTo reset the metadata grid to its default patterns, select the "Reset" option\nfrom the drop down menu.\n\nTip: To reorder the entries, first copy the ones you want to change in your\nnotepad and paste the label and pattern afterwards.\n\nNote: Not all entries will be displayed if the height of the player size is too small,\nchange to a larger player size if desired.\n\nYou can learn more about patterns here, click on this text.'; + const menu = new CustomMenuDropDown(x, y, 'Page 1', ['Page 1'], 0); + CustomMenu.controlList.push(menu); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Page 2', ['Page 2'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Page 3', ['Page 3'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Page 4', ['Page 4'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, 'Options', ['Info', '', 'Reset'], 0)); + x += CustomMenu.controlList[CustomMenu.controlList.length - 1].w + 1; CustomMenu.controlList.push(new CustomMenuDropDown(x, y, '\u2715', [''])); + x = baseX; + y += menu.h + margin * 0.75; - const height = MeasureString(text, ft.popup, 0, 0, maxWidth, wh).Height; - const info = new Info(x, y, maxWidth, height, text, link); - controlList.push(info); -} + if (info) { + this._info(x, y, grm.ui.ww * 0.5, grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight); + } else { + this._page(x - margin * 0.5, y, grm.ui.ww * 0.5, grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight, page); + } + } + /** + * Resets the labels and keys from the metadata grid in the georgia-reborn-config file. + */ + resetMetadataGrid() { + const prefs = grCfg.config.readConfiguration(); + const metadataGrid = prefs.metadataGrid; -/** - * Updates the labels and keys from the metadata grid in the georgia-reborn-config file. - * @param {string} id The metadata gird label name. - * @param {string} value1 The metadata grid tag name. - * @param {string} value2 The metadata grid tag value. - */ -function updateMetadataGridFromConfig(id, value1, value2) { - const prefs = config.readConfiguration(); - const metadataGrid = prefs.metadataGrid; - const index = metadataGrid.findIndex(x => x.label === id); - - if (index > -1) { - if (value1) { - metadataGrid[index].label = SanitizeJsonString(value1); - } else if (value1 === '') { - metadataGrid[index].label = `Blank ${index}`; + for (let i = 0; i < metadataGrid.length; i++) { + metadataGrid[i].label = grDef.metadataGridDefaults[i].label; + metadataGrid[i].val = grDef.metadataGridDefaults[i].val; } - if (value2) { - metadataGrid[index].val = SanitizeJsonString(value2); - } else if (value2 === '') { - metadataGrid[index].val = ''; - } + grCfg.config.updateConfigObjValues('metadataGrid', metadataGrid, true); + grm.ui.updateMetadataGrid(); + window.Repaint(); } - config.updateConfigObjValues('metadataGrid', metadataGrid, true); - updateMetadataGrid(); - window.Repaint(); -} + /** + * Updates the labels and keys from the metadata grid in the georgia-reborn-config file. + * @param {string} id - The metadata gird label name. + * @param {string} value1 - The metadata grid tag name. + * @param {string} value2 - The metadata grid tag value. + */ + updateMetadataGridFromConfig(id, value1, value2) { + const prefs = grCfg.config.readConfiguration(); + const metadataGrid = prefs.metadataGrid; + const index = metadataGrid.findIndex(x => x.label === id); + if (index > -1) { + if (value1) { + metadataGrid[index].label = SanitizeJsonString(value1); + } else if (value1 === '') { + metadataGrid[index].label = `Blank ${index}`; + } -/** - * Resets the labels and keys from the metadata grid in the georgia-reborn-config file. - */ -function resetMetadataGrid() { - const prefs = config.readConfiguration(); - const metadataGrid = prefs.metadataGrid; + if (value2) { + metadataGrid[index].val = SanitizeJsonString(value2); + } else if (value2 === '') { + metadataGrid[index].val = ''; + } + } - for (let i = 0; i < metadataGrid.length; i++) { - metadataGrid[i].label = defaultMetadataGrid[i].label; - metadataGrid[i].val = defaultMetadataGrid[i].val; + grCfg.config.updateConfigObjValues('metadataGrid', metadataGrid, true); + grm.ui.updateMetadataGrid(); + window.Repaint(); } - - config.updateConfigObjValues('metadataGrid', metadataGrid, true); - updateMetadataGrid(); - window.Repaint(); + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-menu.js b/profile/georgia-reborn/scripts/Base/gr-menu.js index 95286654..5603d5e4 100644 --- a/profile/georgia-reborn/scripts/Base/gr-menu.js +++ b/profile/georgia-reborn/scripts/Base/gr-menu.js @@ -1,3875 +1,3857 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Menu * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Menu * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -///////////////////////// -// * TOP MENU RATING * // -///////////////////////// +////////////////// +// * TOP MENU * // +////////////////// /** - * Top menu > Rating. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. + * A class that creates and handles all top menus in the top navigation bar. */ -function topMenuRating(x, y) { - const handle = new FbMetadbHandleList(); - const metadb = fb.GetFocusItem(); - if (!metadb) { - fb.ShowPopupMessage('No track selected, it seems like the playlist is empty.', 'Empty playlist'); - return; - } - const fileInfo = metadb.GetFileInfo(); - const ratingMetaIdx = fileInfo.MetaFind('RATING'); - const ratingMeta = ratingMetaIdx === -1 ? 0 : fileInfo.MetaValue(ratingMetaIdx, 0); - const ratingTags = g_properties.use_rating_from_tags; - const rating = ratingTags ? ratingMeta : $('$if2(%rating%,0)', metadb); - const selectedItems = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); - const menu = new Menu(); - activeMenu = true; - - menu.addRadioItems(['No rating', '1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'], parseInt(rating), [0, 1, 2, 3, 4, 5], (rating) => { - playlistAlbumRatings = new Map(); - - for (let i = 0; i < selectedItems.Count; i++) { - const metadb = selectedItems[i]; - const noStream = !metadb.RawPath.startsWith('http'); - - if (rating === 0) { - if (ratingTags && noStream) { - handle.Add(metadb); - handle.UpdateFileInfoFromJSON(JSON.stringify({ RATING: '' })); - } else { - fb.RunContextCommandWithMetadb('Playback Statistics/Rating/', metadb); - } - } - else if (ratingTags && noStream) { - handle.Add(metadb); - handle.UpdateFileInfoFromJSON(JSON.stringify({ RATING: rating })); - } - else { - fb.RunContextCommandWithMetadb(`Playback Statistics/Rating/${rating}`, metadb); +class TopMenu { + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Opens the main menu and handles different menu options based on the provided name. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} name - The name of the menu. + */ + topMenuMain(x, y, name) { + grm.button.mainMenuOpen = true; + grm.ui.activeMenu = true; + + if (name) { + const menu = new Menu(name); + + if (name === 'Help') { + const themeMenu = new Menu('Theme'); + + const statusMenu = new Menu('Status'); + statusMenu.addItem('All fonts installed', grm.ui.initFonts(), undefined, true); + statusMenu.addItem('Artist logos found', IsFile(`${grPath.artistlogos}Metallica.png`), undefined, true); + statusMenu.addItem('Record label logos found', IsFile(`${grPath.labelsBase}Republic.png`), undefined, true); + statusMenu.addItem('Flag images found', IsFile(`${grPath.flagsBase + (RES._4K ? '64\\' : '32\\')}United-States.png`), undefined, true); + statusMenu.addItem('foo_enhanced_playcount installed', Component.EnhancedPlaycount, () => { RunCmd('https://www.foobar2000.org/components/view/foo_enhanced_playcount'); }); + statusMenu.appendTo(themeMenu); + + const updatesMenu = new Menu('Updates'); + updatesMenu.addToggleItem('Auto-check for theme updates', grSet, 'checkForUpdates', () => { grCfg.scheduleUpdateCheck(1000); }); + updatesMenu.addItem('Check for latest theme update', false, () => { grCfg.checkForUpdates(true); }); + updatesMenu.appendTo(themeMenu); + + themeMenu.addItem('Releases', false, () => { RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/releases'); }); + themeMenu.addItem('Changelog', false, () => { RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/blob/master/profile/georgia-reborn/docs/CHANGELOG.md'); }); + themeMenu.addItem('Bug tracker', false, () => { RunCmd('https://github.com/TT-ReBORN/Georgia-ReBORN/issues'); }); + themeMenu.appendTo(menu); } + menu.initFoobarMenu(name); - const trackId = $('%rating%', metadb); - playlistTrackRatings.set(trackId, rating); + const ret = menu.trackPopupMenu(x, y); + menu.doCallback(ret); } - }); - const idx = menu.trackPopupMenu(x, y); - menu.doCallback(idx); - activeMenu = false; -} + grm.ui.activeMenu = false; + } + /** + * Creates a context menu for playlists allowing users to perform various actions such as + * creating new playlists, saving and loading playlists, locking and unlocking playlists, + * and creating auto playlists based on different criteria. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + topMenuPlaylists(x, y) { + grm.button.mainMenuOpen = true; + grm.ui.activeMenu = true; + + const playlistId = 21; + const cpm = window.CreatePopupMenu(); + const plTools = window.CreatePopupMenu(); + const autoPl = window.CreatePopupMenu(); + const isAutoPl = !plman.PlaylistCount ? '' : plman.IsAutoPlaylist(plman.ActivePlaylist); + const isLocked = !plman.PlaylistCount ? '' : plman.IsPlaylistLocked(plman.ActivePlaylist); + + plTools.AppendTo(cpm, MF_STRING, 'Playlist tools'); + plTools.AppendMenuItem(MF_STRING, 1, 'Playlist manager \tCtrl+M'); + plTools.AppendMenuItem(MF_STRING, 2, 'Playlist search \tCtrl+F'); + plTools.AppendMenuSeparator(); + plTools.AppendMenuItem(MF_STRING, 3, 'Create new playlist \tCtrl+N'); + autoPl.AppendTo(plTools, MF_STRING, 'Create new auto playlist'); + autoPl.AppendMenuItem(MF_STRING, 4, 'Custom auto playlist'); + autoPl.AppendMenuSeparator(); + autoPl.AppendMenuItem(MF_STRING, 5, 'Tracks from the library'); + autoPl.AppendMenuSeparator(); + autoPl.AppendMenuItem(MF_STRING, 6, 'Tracks most played'); + autoPl.AppendMenuItem(MF_STRING, 7, 'Tracks never played'); + autoPl.AppendMenuItem(MF_STRING, 8, 'Tracks played in the last week'); + autoPl.AppendMenuItem(MF_STRING, 9, 'Tracks played in the last month'); + autoPl.AppendMenuItem(MF_STRING, 10, 'Tracks played in the last year'); + autoPl.AppendMenuSeparator(); + autoPl.AppendMenuItem(MF_STRING, 11, 'Tracks unrated'); + autoPl.AppendMenuItem(MF_STRING, 12, 'Tracks rated 1 star'); + autoPl.AppendMenuItem(MF_STRING, 13, 'Tracks rated 2 stars'); + autoPl.AppendMenuItem(MF_STRING, 14, 'Tracks rated 3 stars'); + autoPl.AppendMenuItem(MF_STRING, 15, 'Tracks rated 4 stars'); + autoPl.AppendMenuItem(MF_STRING, 16, 'Tracks rated 5 stars'); + autoPl.AppendMenuSeparator(); + autoPl.AppendMenuItem(MF_STRING, 17, 'Loved tracks'); + plTools.AppendMenuSeparator(); + plTools.AppendMenuItem(MF_STRING, 18, 'Save playlist \tCtrl+S'); + plTools.AppendMenuItem(MF_STRING, 19, 'Load playlist'); + plTools.AppendMenuItem(isAutoPl ? MF_DISABLED : MF_STRING, 20, isLocked ? isAutoPl ? 'Unlock playlist (N/A for auto playlists)' : 'Unlock playlist' : 'Lock playlist'); + cpm.AppendMenuSeparator(); + for (let i = 0; i !== plman.PlaylistCount; i++) { + cpm.AppendMenuItem(MF_STRING, playlistId + i, `${plman.GetPlaylistName(i).replace(/&/g, '&&')} [${plman.PlaylistItemCount(i)}]${plman.IsAutoPlaylist(i) ? ' (Auto)' : ''}${i === plman.PlayingPlaylist ? ' (Now Playing)' : ''}`); + } + + const id = cpm.TrackPopupMenu(x, y); + const playlistIdx = id - playlistId; + + switch (id) { + case 1: + fb.RunMainMenuCommand('View/Playlist Manager'); + break; + case 2: + fb.RunMainMenuCommand('View/Playlist search'); + break; + case 3: + plman.CreatePlaylist(plman.PlaylistCount, ''); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 4: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) New custom auto playlist', '', '', 0); + plman.ActivePlaylist = plman.PlaylistCount; + plman.ShowAutoPlaylistUI(plman.PlaylistCount); + break; + case 5: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks from the library', 'ALL', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 6: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks most played', '%play_count% GREATER 9', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 7: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks never played', '%play_count% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 8: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks played in the last week', '%last_played% DURING LAST 1 WEEK', '%last_played%', 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 9: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks played in the last month', '%last_played% DURING LAST 4 WEEKS', '%last_played%', 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 10: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks played in the last year', '%last_played% DURING LAST 52 WEEKS', '%last_played%', 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 11: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks unrated', '%rating% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 12: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 1', '%rating% IS 1', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 13: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 2', '%rating% IS 2', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 14: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 3', '%rating% IS 3', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 15: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 4', '%rating% IS 4', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 16: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 5', '%rating% IS 5', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 17: + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Loved tracks', '%mood% GREATER 0', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + break; + case 18: + fb.RunMainMenuCommand('File/Save playlist...'); + break; + case 19: + fb.RunMainMenuCommand('File/Load playlist...'); + break; + case 20: + if (plman.GetPlaylistLockName(plman.ActivePlaylist) && !isAutoPl) { + plman.SetPlaylistLockedActions(plman.ActivePlaylist, null); + } else if (!isAutoPl) { + plman.SetPlaylistLockedActions(plman.ActivePlaylist, ['ExecuteDefaultAction']); + } + break; + } -////////////////// -// * TOP MENU * // -////////////////// -/** - * All top menus, also used to append menus in panel context menus. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {boolean} context_menu Appends panel related menus to the context menu. - * @param {boolean} playlist Appends main Playlist menu to the Playlist context menu. - * @param {boolean} details Appends main Details menu to the Details context menu. - * @param {boolean} library Appends main Library menu to the Library context menu. - * @param {boolean} biography Appends main Biography menu to the Biography context menu. - * @param {boolean} lyrics Appends main Lyrics menu to the Lyrics context menu. - */ -function topMenuOptions(x, y, context_menu, playlist, details, library, biography, lyrics) { - activeMenu = true; - state.mouse_x = x; - state.mouse_y = y; - const menu = new Menu(); - const themeDayNightSetup = pref.themeSetupDay || pref.themeSetupNight; - - if (!context_menu) { - themeOptions(menu); - styleOptions(menu); - presetOptions(menu); - - if (!themeDayNightSetup) { - playerSizeOptions(menu); - layoutOptions(menu); - displayOptions(menu); + if (playlistIdx < plman.PlaylistCount && playlistIdx >= 0) { + plman.ActivePlaylist = playlistIdx; } - brightnessOptions(menu); + for (let i = 0; i !== plman.PlaylistCount; i++) { + if (id === (playlistId + i)) plman.ActivePlaylist = i; // Playlist switch + } - if (!themeDayNightSetup) { - fontSizeOptions(menu); - menu.addSeparator(); - playerControlsOptions(menu); - menu.addSeparator(); - playlistOptions(menu); + grm.ui.activeMenu = false; + return true; + } - if (pref.layout !== 'compact') { - detailsOptions(menu); - libraryOptions(menu); - biographyOptions(menu); - lyricsOptions(menu); + /** + * All top menus, also used to append menus in panel context menus. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {boolean} context_menu - Appends panel related menus to the context menu. + * @param {boolean} playlist - Appends main Playlist menu to the Playlist context menu. + * @param {boolean} details - Appends main Details menu to the Details context menu. + * @param {boolean} library - Appends main Library menu to the Library context menu. + * @param {boolean} biography - Appends main Biography menu to the Biography context menu. + * @param {boolean} lyrics - Appends main Lyrics menu to the Lyrics context menu. + */ + topMenuOptions(x, y, context_menu, playlist, details, library, biography, lyrics) { + grm.ui.activeMenu = true; + grm.ui.state.mouse_x = x; + grm.ui.state.mouse_y = y; + const menu = new Menu(); + const themeDayNightSetup = grSet.themeSetupDay || grSet.themeSetupNight; + + if (!context_menu) { + grm.options.themeOptions(menu); + grm.options.styleOptions(menu); + grm.options.presetOptions(menu); + + if (!themeDayNightSetup) { + grm.options.playerSizeOptions(menu); + grm.options.layoutOptions(menu); + grm.options.displayOptions(menu); } - } - menu.addSeparator(); - settingsOptions(menu); + grm.options.brightnessOptions(menu); - if (pref.devTools) { - menu.addSeparator(); - developerToolsOptions(menu); - } - } - else if (playlist) playlistOptions(menu, context_menu); - else if (details) detailsOptions(menu, context_menu); - else if (library) libraryOptions(menu, context_menu); - else if (biography) biographyOptions(menu, context_menu); - else if (lyrics) lyricsOptions(menu, context_menu); - - const idx = menu.trackPopupMenu(x, y); - menu.doCallback(idx); - activeMenu = false; -} + if (!themeDayNightSetup) { + grm.options.fontSizeOptions(menu); + menu.addSeparator(); + grm.options.playerControlsOptions(menu); + menu.addSeparator(); + grm.options.playlistOptions(menu); + + if (grSet.layout !== 'compact') { + grm.options.detailsOptions(menu); + grm.options.libraryOptions(menu); + grm.options.biographyOptions(menu); + grm.options.lyricsOptions(menu); + } + } + menu.addSeparator(); + grm.options.settingsOptions(menu); -/////////////////////// -// * THEME OPTIONS * // -/////////////////////// -/** - * Top menu > Options > Theme. - * @param {Menu} menu Creates the Theme menu via a new Menu instance. - */ -function themeOptions(menu) { - const themeMenu = new Menu('Theme'); - - themeMenu.addRadioItems(['White', 'Black', 'Reborn', 'Random'], pref.theme, ['white', 'black', 'reborn', 'random'], (theme) => { - if (!pref.themeSandbox) pref.savedTheme = pref.theme = theme; else pref.theme = theme; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - resetTheme(); - initTheme(); - initThemePresetState(); - }); - themeMenu.addSeparator(); - themeMenu.addRadioItems(['Blue', 'Dark blue', 'Red', 'Cream'], pref.theme, ['blue', 'darkblue', 'red', 'cream'], (theme) => { - if (!pref.themeSandbox) pref.savedTheme = pref.theme = theme; else pref.theme = theme; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - resetTheme(); - initTheme(); - initThemePresetState(); - }); - themeMenu.addSeparator(); - themeMenu.addRadioItems(['Neon blue', 'Neon green', 'Neon red', 'Neon gold'], pref.theme, ['nblue', 'ngreen', 'nred', 'ngold'], (theme) => { - if (!pref.themeSandbox) pref.savedTheme = pref.theme = theme; else pref.theme = theme; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - resetTheme(); - initTheme(); - initThemePresetState(); - }); - themeMenu.addSeparator(); - - // * CUSTOM THEME * // - const customThemeMenu = new Menu('Custom'); - const customThemeName = [ - customTheme01.name || 'Theme 01', - customTheme02.name || 'Theme 02', - customTheme03.name || 'Theme 03', - customTheme04.name || 'Theme 04', - customTheme05.name || 'Theme 05', - customTheme06.name || 'Theme 06', - customTheme07.name || 'Theme 07', - customTheme08.name || 'Theme 08', - customTheme09.name || 'Theme 09', - customTheme10.name || 'Theme 10' - ]; - - customThemeMenu.addRadioItems(customThemeName, pref.theme, ['custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'], (theme) => { - if (!pref.themeSandbox) pref.savedTheme = pref.theme = theme; else pref.theme = theme; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - resetTheme(); - initCustomTheme(); - initTheme(); - initCustomThemeMenu('pl_bg'); - initThemePresetState(); - }); - customThemeMenu.addSeparator(); - - customThemeMenu.addItem('Edit custom theme', false, () => { - if (pref.layout === 'default') { - displayCustomThemeMenu = !displayCustomThemeMenu; - initCustomTheme(); - if (displayDetails || displayLibrary || displayBiography || pref.displayLyrics) { - displayPlaylist = true; - displayDetails = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - resizeArtwork(true); - initButtonState(); - } - initCustomThemeMenu('pl_bg'); - RepaintWindow(); - } else { - const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; - fb.ShowPopupMessage(`Custom theme can only be live edited in default layout:\nOptions > Layout > Default\n\nYou could manually edit your config file while reloading to take effect:\n${configPathCustom}\n`, 'Custom theme live editing'); + if (grSet.devTools) { + menu.addSeparator(); + grm.options.developerToolsOptions(menu); + } } - }); - - customThemeMenu.addItem('Rename custom theme', false, () => { inputBox('renameCustomTheme'); }); + else if (playlist) grm.options.playlistOptions(menu, context_menu); + else if (details) grm.options.detailsOptions(menu, context_menu); + else if (library) grm.options.libraryOptions(menu, context_menu); + else if (biography) grm.options.biographyOptions(menu, context_menu); + else if (lyrics) grm.options.lyricsOptions(menu, context_menu); - customThemeMenu.createRadioSubMenu('Save current colors', customThemeName, '', ['custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'], (theme) => { - const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; - const msg = `Do you want to save current used colors to the selected\ncustom theme slot?\n\nThis will overwrite all colors in the selected custom theme slot.\nIt is recommended to make a backup of your ${configPathCustom}\nconfig file.\n\nContinue?`; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) setCurrentColorsToCustomTheme(theme); - }); - }); - customThemeMenu.appendTo(themeMenu); - themeMenu.appendTo(menu, pref.presetSelectMode === 'harmonic'); + const idx = menu.trackPopupMenu(x, y); + menu.doCallback(idx); + grm.ui.activeMenu = false; + } + // #endregion } -/////////////////////// -// * STYLE OPTIONS * // -/////////////////////// +//////////////////////////// +// * TOP MENU - OPTIONS * // +//////////////////////////// /** - * Top menu > Options > Style. - * @param {Menu} menu Creates the Style menu via a new Menu instance. + * A class that provides the full collection of all menus in the `Options` top navigation menu. */ -function styleOptions(menu) { - const styleMenu = new Menu('Style'); - themePresetIndicator = true; - themePresetMatchMode = true; - - // * STYLES * // - styleMenu.addToggleItem('Default', pref, 'styleDefault', () => { - if (pref.themeSandbox) { - const msg = 'Theme style reset was canceled:\n\nActive theme sandbox needs to be deactivated first\nin order to reset theme styles.\n\n\n'; - ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); - pref.styleDefault = false; - return; - } - pref.preset = false; - resetStyle('all'); - resetStyle('all_theme_day_night'); - restoreThemeStylePreset(true); - resetTheme(); - initTheme(); - }); - styleMenu.addSeparator(); - if (pref.theme === 'reborn' || pref.theme === 'random' || pref.theme.startsWith('custom')) { - styleMenu.addToggleItem('Night', pref, 'styleNighttime', () => { - if (!pref.themeSandbox) pref.savedStyleNighttime = pref.styleNighttime; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - if (pref.styleRebornWhite) pref.styleRebornWhite = false; - if (pref.styleRebornBlack) pref.styleRebornBlack = false; - updateStyle(); +class TopMenuOptions { + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Top menu > Options > Theme. + * @param {Menu} menu - Creates the Theme menu via a new Menu instance. + * @protected + */ + themeOptions(menu) { + const themeMenu = new Menu('Theme'); + + themeMenu.addRadioItems(['White', 'Black', 'Reborn', 'Random'], grSet.theme, ['white', 'black', 'reborn', 'random'], (theme) => { + if (!grSet.themeSandbox) grSet.savedTheme = grSet.theme = theme; else grSet.theme = theme; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.resetTheme(); + grm.ui.initTheme(); + grm.preset.initThemePresetState(); }); - styleMenu.addSeparator(); - } - styleMenu.addToggleItem('Bevel', pref, 'styleBevel', () => { - if (!pref.themeSandbox) pref.savedStyleBevel = pref.styleBevel; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleMenu.addSeparator(); - - // * STYLES - GROUP ONE * // - styleMenu.addToggleItem('Blend', pref, 'styleBlend', () => { - if (!pref.themeSandbox) pref.savedStyleBlend = pref.styleBlend; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('blend', pref.styleBlend); - updateStyle(); - }); - styleMenu.addToggleItem('Blend 2', pref, 'styleBlend2', () => { - if (!pref.themeSandbox) pref.savedStyleBlend2 = pref.styleBlend2; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('blend2', pref.styleBlend2); - updateStyle(); - }); - if (['reborn', 'random', 'blue', 'darkblue', 'red', 'custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'].includes(pref.theme)) { - styleMenu.addToggleItem('Gradient', pref, 'styleGradient', () => { - if (!pref.themeSandbox) pref.savedStyleGradient = pref.styleGradient; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('gradient', pref.styleGradient); - updateStyle(); - }, pref.styleRebornWhite); - } - if (['reborn', 'random', 'blue', 'darkblue', 'red', 'custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'].includes(pref.theme)) { - styleMenu.addToggleItem('Gradient 2', pref, 'styleGradient2', () => { - if (!pref.themeSandbox) pref.savedStyleGradient2 = pref.styleGradient2; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('gradient2', pref.styleGradient2); - updateStyle(); - }, pref.styleRebornWhite); - } - styleMenu.addSeparator(); - - // * STYLES - GROUP TWO * // - styleMenu.addToggleItem('Alternative', pref, 'styleAlternative', () => { - if (!pref.themeSandbox) pref.savedStyleAlternative = pref.styleAlternative; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('alternative', pref.styleAlternative); - updateStyle(); - }); - styleMenu.addToggleItem('Alternative 2', pref, 'styleAlternative2', () => { - if (!pref.themeSandbox) pref.savedStyleAlternative2 = pref.styleAlternative2; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('alternative2', pref.styleAlternative2); - updateStyle(); - }); - if (pref.theme === 'white') { - styleMenu.addToggleItem('Black and white', pref, 'styleBlackAndWhite', () => { - if (!pref.themeSandbox) pref.savedStyleBlackAndWhite = pref.styleBlackAndWhite; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('blackAndWhite', pref.styleBlackAndWhite); - updateStyle(); - }, pref.styleBlackAndWhiteReborn); - styleMenu.addToggleItem('Black and white 2', pref, 'styleBlackAndWhite2', () => { - if (!pref.themeSandbox) pref.savedStyleBlackAndWhite2 = pref.styleBlackAndWhite2; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('blackAndWhite2', pref.styleBlackAndWhite2); - updateStyle(); - }, pref.styleBlackAndWhiteReborn); - styleMenu.addToggleItem('Black and white reborn', pref, 'styleBlackAndWhiteReborn', () => { - if (!pref.themeSandbox) pref.savedStyleBlackAndWhiteReborn = pref.styleBlackAndWhiteReborn; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('blackAndWhiteReborn', pref.styleBlackAndWhiteReborn); - updateStyle(); + themeMenu.addSeparator(); + themeMenu.addRadioItems(['Blue', 'Dark blue', 'Red', 'Cream'], grSet.theme, ['blue', 'darkblue', 'red', 'cream'], (theme) => { + if (!grSet.themeSandbox) grSet.savedTheme = grSet.theme = theme; else grSet.theme = theme; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.resetTheme(); + grm.ui.initTheme(); + grm.preset.initThemePresetState(); }); - } - if (pref.theme === 'black') { - styleMenu.addToggleItem('Black reborn', pref, 'styleBlackReborn', () => { - if (!pref.themeSandbox) pref.savedStyleBlackReborn = pref.styleBlackReborn; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('blackReborn', pref.styleBlackReborn); - updateStyle(); + themeMenu.addSeparator(); + themeMenu.addRadioItems(['Neon blue', 'Neon green', 'Neon red', 'Neon gold'], grSet.theme, ['nblue', 'ngreen', 'nred', 'ngold'], (theme) => { + if (!grSet.themeSandbox) grSet.savedTheme = grSet.theme = theme; else grSet.theme = theme; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.resetTheme(); + grm.ui.initTheme(); + grm.preset.initThemePresetState(); }); - } - if (pref.theme === 'reborn') { - styleMenu.addToggleItem('Reborn white', pref, 'styleRebornWhite', () => { - if (!pref.themeSandbox) pref.savedStyleRebornWhite = pref.styleRebornWhite; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - if (pref.styleNighttime) pref.styleNighttime = false; - setStyle('rebornWhite', pref.styleRebornWhite); - updateStyle(); - }); - styleMenu.addToggleItem('Reborn black', pref, 'styleRebornBlack', () => { - if (!pref.themeSandbox) pref.savedStyleRebornBlack = pref.styleRebornBlack; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - if (pref.styleNighttime) pref.styleNighttime = false; - setStyle('rebornBlack', pref.styleRebornBlack); - updateStyle(); - }); - styleMenu.addToggleItem('Reborn fusion', pref, 'styleRebornFusion', () => { - if (!pref.themeSandbox) pref.savedStyleRebornFusion = pref.styleRebornFusion; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('rebornFusion', pref.styleRebornFusion); - updateStyle(); - }); - styleMenu.addToggleItem('Reborn fusion 2', pref, 'styleRebornFusion2', () => { - if (!pref.themeSandbox) pref.savedStyleRebornFusion2 = pref.styleRebornFusion2; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('rebornFusion2', pref.styleRebornFusion2); - updateStyle(); - }); - styleMenu.addToggleItem('Reborn fusion accent', pref, 'styleRebornFusionAccent', () => { - if (!pref.themeSandbox) pref.savedStyleRebornFusionAccent = pref.styleRebornFusionAccent; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('rebornFusionAccent', pref.styleRebornFusionAccent); - updateStyle(); + themeMenu.addSeparator(); + + // * CUSTOM THEME * // + const customThemeMenu = new Menu('Custom'); + const customThemeName = [ + grCfg.customTheme01.name || 'Theme 01', + grCfg.customTheme02.name || 'Theme 02', + grCfg.customTheme03.name || 'Theme 03', + grCfg.customTheme04.name || 'Theme 04', + grCfg.customTheme05.name || 'Theme 05', + grCfg.customTheme06.name || 'Theme 06', + grCfg.customTheme07.name || 'Theme 07', + grCfg.customTheme08.name || 'Theme 08', + grCfg.customTheme09.name || 'Theme 09', + grCfg.customTheme10.name || 'Theme 10' + ]; + + customThemeMenu.addRadioItems(customThemeName, grSet.theme, ['custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'], (theme) => { + if (!grSet.themeSandbox) grSet.savedTheme = grSet.theme = theme; else grSet.theme = theme; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.resetTheme(); + grm.ui.initCustomTheme(); + grm.ui.initTheme(); + grm.cthMenu.initCustomThemeMenu('pl_bg'); + grm.preset.initThemePresetState(); }); - } - if (pref.theme === 'random') { - styleMenu.addToggleItem('Random pastel', pref, 'styleRandomPastel', () => { - if (!pref.themeSandbox) pref.savedStyleRandomPastel = pref.styleRandomPastel; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('randomPastel', pref.styleRandomPastel); - updateStyle(); - }); - styleMenu.addToggleItem('Random dark', pref, 'styleRandomDark', () => { - if (!pref.themeSandbox) pref.savedStyleRandomDark = pref.styleRandomDark; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - setStyle('randomDark', pref.styleRandomDark); - updateStyle(); + customThemeMenu.addSeparator(); + + customThemeMenu.addItem('Edit custom theme', false, () => { + if (grSet.layout === 'default') { + grm.ui.displayCustomThemeMenu = !grm.ui.displayCustomThemeMenu; + grm.ui.initCustomTheme(); + if (grm.ui.displayDetails || grm.ui.displayLibrary || grm.ui.displayBiography || grm.ui.displayLyrics) { + grm.ui.displayPlaylist = true; + grm.ui.displayDetails = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + } + grm.cthMenu.initCustomThemeMenu('pl_bg'); + RepaintWindow(); + } else { + fb.ShowPopupMessage(`Custom theme can only be live edited in default layout:\nOptions > Layout > Default\n\nYou could manually edit your config file while reloading to take effect:\n${grCfg.configPathCustom}\n`, 'Custom theme live editing'); + } }); - } - styleMenu.addSeparator(); - - // * STYLES - RANDOM AUTO COLOR * // - if (pref.theme === 'random') { - const styleAutoColorMenu = new Menu('Auto color'); - styleAutoColorMenu.addRadioItems(['Off', '5 sec', '10 sec', '15 sec', '30 sec', '45 sec', '1 min', '2 min', '3 min', '4 min', '5 min', 'New track'], pref.styleRandomAutoColor, - ['off', 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, 'track'], (timer) => { - if (!pref.themeSandbox) pref.savedStyleRandomAutoColor = pref.styleRandomAutoColor = timer; else pref.styleRandomAutoColor = timer; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - getRandomThemeAutoColor(); - }); - styleAutoColorMenu.appendTo(styleMenu); - styleMenu.addSeparator(); - } - - // * STYLES - BUTTONS * // - const styleButtonsMenu = new Menu('Buttons'); - const styleTopButtonsMenu = new Menu('Top menu'); - styleTopButtonsMenu.addRadioItems(['Default', 'Filled', 'Bevel', 'Inner', 'Emboss', 'Minimal'], pref.styleTopMenuButtons, ['default', 'filled', 'bevel', 'inner', 'emboss', 'minimal'], (style) => { - if (!pref.themeSandbox) pref.savedStyleTopMenuButtons = pref.styleTopMenuButtons = style; else pref.styleTopMenuButtons = style; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleTopButtonsMenu.appendTo(styleButtonsMenu); - const styleTransportButtonsMenu = new Menu('Transport'); - styleTransportButtonsMenu.addRadioItems(['Default', 'Bevel', 'Inner', 'Emboss', 'Minimal'], pref.styleTransportButtons, ['default', 'bevel', 'inner', 'emboss', 'minimal'], (style) => { - if (!pref.themeSandbox) pref.savedStyleTransportButtons = pref.styleTransportButtons = style; else pref.styleTransportButtons = style; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleTransportButtonsMenu.appendTo(styleButtonsMenu); - styleButtonsMenu.appendTo(styleMenu); - - // * STYLES - PROGRESS BAR * // - const styleProgressBarMenu = new Menu('Progress bar'); - styleProgressBarMenu.createRadioSubMenu('Design', ['Default', 'Rounded', 'Lines', 'Blocks', 'Dots', 'Thin'], pref.styleProgressBarDesign, ['default', 'rounded', 'lines', 'blocks', 'dots', 'thin'], (design) => { - if (!pref.themeSandbox) pref.savedStyleProgressBarDesign = pref.styleProgressBarDesign = design; else pref.styleProgressBarDesign = design; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleProgressBarMenu.createRadioSubMenu('Background', ['Default', 'Bevel', 'Inner'], pref.styleProgressBar, ['default', 'bevel', 'inner'], (style) => { - if (!pref.themeSandbox) pref.savedStyleProgressBar = pref.styleProgressBar = style; else pref.styleProgressBar = style; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleProgressBarMenu.createRadioSubMenu('Progress fill', ['Default', 'Bevel', 'Inner', 'Blend'], pref.styleProgressBarFill, ['default', 'bevel', 'inner', 'blend'], (style) => { - if (!pref.themeSandbox) pref.savedStyleProgressBarFill = pref.styleProgressBarFill = style; else pref.styleProgressBarFill = style; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleProgressBarMenu.appendTo(styleMenu); - - // * STYLES - VOLUME BAR * // - const styleVolumeBarMenu = new Menu('Volume bar'); - styleVolumeBarMenu.createRadioSubMenu('Design', ['Default', 'Rounded'], pref.styleVolumeBarDesign, ['default', 'rounded'], (design) => { - if (!pref.themeSandbox) pref.savedStyleVolumeBarDesign = pref.styleVolumeBarDesign = design; else pref.styleVolumeBarDesign = design; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleVolumeBarMenu.createRadioSubMenu('Background', ['Default', 'Bevel', 'Inner'], pref.styleVolumeBar, ['default', 'bevel', 'inner'], (style) => { - if (!pref.themeSandbox) pref.savedStyleVolumeBar = pref.styleVolumeBar = style; else pref.styleVolumeBar = style; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleVolumeBarMenu.createRadioSubMenu('Volume fill', ['Default', 'Bevel', 'Inner'], pref.styleVolumeBarFill, ['default', 'bevel', 'inner'], (style) => { - if (!pref.themeSandbox) pref.savedStyleVolumeBarFill = pref.styleVolumeBarFill = style; else pref.styleVolumeBarFill = style; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - updateStyle(); - }); - styleVolumeBarMenu.appendTo(styleMenu); - - styleMenu.appendTo(menu, pref.presetSelectMode === 'harmonic'); -} + customThemeMenu.addItem('Rename custom theme', false, () => { grm.inputBox.renameCustomTheme(); }); -//////////////////////// -// * PRESET OPTIONS * // -//////////////////////// -/** - * Top menu > Options > Preset. - * @param {Menu} menu Creates the Preset menu via a new Menu instance. - */ -function presetOptions(menu) { - const themePresetsMenu = new Menu('Preset'); - const themePresetSelectModeMenu = new Menu('Select mode'); - - const presetSelectMode = () => { - themePresetSelectModeMenu.addRadioItems(['Default', 'Harmonic', 'Theme'], pref.presetSelectMode, ['default', 'harmonic', 'theme'], (mode) => { - pref.presetSelectMode = mode; - if (mode === 'default') { - const msg = 'Do you want to activate the -Default- preset select mode?\n\nThe default select mode will automatically choose\na random pick of 88 theme presets.\n\nDouble-click on the lower bar to choose\nanother random theme preset.\n\nWhen random mode is activated,\nall themes and style options will be available.\n\nContinue?\n\n\n'; - const msgFb = 'Default preset select mode activated:\n\nThe default preset select mode will automatically choose a random pick of 88 theme presets.\n\nDouble-click on the lower bar to choose another random theme preset.\n\nWhen random mode is activated,\nall themes and style options will be available.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) { - pref.presetSelectMode = 'default'; - return; - } - pref.presetAutoRandomMode = 'dblclick'; - setThemePresetSelection(true); // * Reactivate all - resetStyle('all'); - resetTheme(); - restoreThemeStylePreset(); - if (pref.savedPreset !== false) setThemePreset(pref.savedPreset); - updateStyle(); - }); - } - else if (mode === 'harmonic') { - const msg = 'Do you want to activate the -Harmonic- preset select mode?\n\nThe harmonic preset select mode will automatically\nchoose the best visual experience of themes and styles\nbased on album art.\n\nYou can also double-click on the lower bar\nto choose another random harmonic preset.\n\nWhen harmonic preset select mode is activated,\nall themes and almost all style options will be disabled.\n\nContinue?\n\n\n'; - const msgFb = 'Harmonic preset select mode activated:\n\nThe harmonic preset select mode will automatically choose the best visual experience of themes and styles based on album art.\n\nYou can also double-click on the lower bar to choose another random harmonic preset.\n\nWhen harmonic preset select mode is activated,\nall themes and almost all style options will be disabled.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) { - pref.presetSelectMode = 'default'; - return; - } - pref.presetAutoRandomMode = 'dblclick'; - setThemePresetSelection(true); // * Reactivate all - getRandomThemePreset(); - }); - } - else if (mode === 'theme') { - const msg = 'Do you want to activate the -Theme- preset select mode?\n\nThe theme preset select mode will automatically choose\na random theme preset based on current active theme.\n\nYou can also double-click on the lower bar\nto choose another random theme preset.\n\nWhen theme preset select mode is activated,\nall themes and style options will be available.\n\nContinue?\n\n\n'; - const msgFb = 'Theme preset select mode activated:\n\nThe theme preset select mode will automatically choose a random theme preset based on current active theme.\n\nYou can also double-click on the lower bar to choose another random theme preset.\n\nWhen theme preset select mode is activated,\nall themes and style options will be available'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) { - pref.presetSelectMode = 'default'; - return; - } - pref.presetAutoRandomMode = 'dblclick'; - setThemePresetSelection(false, true); - getRandomThemePreset(); - }); - } + customThemeMenu.createRadioSubMenu('Save current colors', customThemeName, '', ['custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'], (theme) => { + const msg = `Do you want to save current used colors\nto the selected custom theme slot?\n\nThis will overwrite all colors in the selected\ncustom theme slot.\n\nIt is recommended to make a backup\nof your custom config file:\n${grCfg.configPathCustom}\n\nSaved color changes will take effect on next reload.\n\nContinue?\n\n\n`; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) grm.color.setCurrentColorsToCustomTheme(theme); + }); }); - }; - - // * HARMONIC MODE EXCLUSIVE MENU * // only show these options when harmonic mode is active - if (pref.presetSelectMode === 'harmonic') { - presetSelectMode(); - themePresetSelectModeMenu.appendTo(themePresetsMenu); - const themePresetSelectMenu = new Menu('Select presets'); - themePresetSelectMenu.addToggleItem('Neon blue', pref, 'presetSelectNblue'); - themePresetSelectMenu.addToggleItem('Neon green', pref, 'presetSelectNgreen'); - themePresetSelectMenu.addToggleItem('Neon red', pref, 'presetSelectNred'); - themePresetSelectMenu.addToggleItem('Neon gold', pref, 'presetSelectNgold'); - themePresetSelectMenu.appendTo(themePresetsMenu); - themePresetsMenu.addToggleItem('Indicator', pref, 'presetIndicator'); - themePresetsMenu.appendTo(menu); - return; + customThemeMenu.appendTo(themeMenu); + themeMenu.appendTo(menu, grSet.presetSelectMode === 'harmonic'); } - // * THEME PRESETS MENUS * // - const applyThemePreset = (preset) => { - if (!pref.themeSandbox) pref.savedPreset = pref.preset = preset; else pref.preset = preset; - setThemePreset(preset); // After applying the preset, synchronize the daytime/nighttime theme preset if necessary - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - }; - - // * WHITE THEME PRESETS * // - const themePresetsWhiteMenu = new Menu('White'); - themePresetsWhiteMenu.addRadioItems(['Beveled', 'Black and white', 'Black and white blended', 'Black and white 2', 'Black and white 2 blended', 'Black and white reborn', 'Black and white reborn blended', 'Minimalized'], pref.preset, - ['whiteP01', 'whiteP02', 'whiteP03', 'whiteP04', 'whiteP05', 'whiteP06', 'whiteP07', 'whiteP08'], (preset) => { - applyThemePreset(preset); - }); - themePresetsWhiteMenu.appendTo(themePresetsMenu); - - // * BLACK THEME PRESETS * // - const themePresetsBlackMenu = new Menu('Black'); - themePresetsBlackMenu.addRadioItems(['Beveled', 'Blended', 'Blended alternative', 'Blended alternative 2', 'Black reborn', 'Black reborn blended', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], pref.preset, - ['blackP01', 'blackP02', 'blackP03', 'blackP04', 'blackP05', 'blackP06', 'blackP07', 'blackP08', 'blackP09', 'blackP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsBlackMenu.appendTo(themePresetsMenu); - - // * REBORN THEME PRESETS * // - const themePresetsRebornMenu = new Menu('Reborn'); - themePresetsRebornMenu.addRadioItems(['Beveled', 'Blended', 'Blended 2', 'Gradiented', 'Gradiented 2', 'Minimalized', 'Minimalized blended'], pref.preset, - ['rebornP01', 'rebornP02', 'rebornP03', 'rebornP04', 'rebornP05', 'rebornP06', 'rebornP07'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRebornMenu.addSeparator(); - themePresetsRebornMenu.addRadioItems(['Reborn white beveled', 'Reborn white blended', 'Reborn white blended 2'], pref.preset, - ['rebornP08', 'rebornP09', 'rebornP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRebornMenu.addSeparator(); - themePresetsRebornMenu.addRadioItems(['Reborn black beveled', 'Reborn black blended', 'Reborn black blended 2', 'Reborn black gradiented', 'Reborn black gradiented 2'], pref.preset, - ['rebornP11', 'rebornP12', 'rebornP13', 'rebornP14', 'rebornP15'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRebornMenu.addSeparator(); - themePresetsRebornMenu.addRadioItems(['Reborn fusion beveled', 'Reborn fusion blended', 'Reborn fusion blended 2', 'Reborn fusion gradiented', 'Reborn fusion gradiented 2'], pref.preset, - ['rebornP16', 'rebornP17', 'rebornP18', 'rebornP19', 'rebornP20'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRebornMenu.addSeparator(); - themePresetsRebornMenu.addRadioItems(['Reborn fusion 2 beveled', 'Reborn fusion 2 blended', 'Reborn fusion 2 blended 2', 'Reborn fusion 2 gradiented', 'Reborn fusion 2 gradiented 2'], pref.preset, - ['rebornP21', 'rebornP22', 'rebornP23', 'rebornP24', 'rebornP25'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRebornMenu.addSeparator(); - themePresetsRebornMenu.addRadioItems(['Reborn fusion accent beveled', 'Reborn fusion accent blended', 'Reborn fusion accent blended 2', 'Reborn fusion accent gradiented', 'Reborn fusion accent gradiented 2'], pref.preset, - ['rebornP26', 'rebornP27', 'rebornP28', 'rebornP29', 'rebornP30'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRebornMenu.appendTo(themePresetsMenu); - - // * RANDOM THEME PRESETS * // - const themePresetsRandomMenu = new Menu('Random'); - themePresetsRandomMenu.addRadioItems(['Beveled blended alternative', 'Beveled blended pastel', 'Beveled blended dark', 'Beveled blended auto dark', 'Beveled auto dark', 'Beveled dark', 'Gradiented', 'Gradiented 2', 'Minimalized', 'Minimalized blended'], pref.preset, - ['randomP01', 'randomP02', 'randomP03', 'randomP04', 'randomP05', 'randomP06', 'randomP07', 'randomP08', 'randomP09', 'randomP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRandomMenu.appendTo(themePresetsMenu); - themePresetsMenu.addSeparator(); - - // * BLUE THEME PRESETS * // - const themePresetsBlueMenu = new Menu('Blue'); - themePresetsBlueMenu.addRadioItems(['Beveled', 'Beveled 2', 'Gradiented', 'Gradiented 2', 'Minimalized'], pref.preset, - ['blueP01', 'blueP02', 'blueP03', 'blueP04', 'blueP05'], (preset) => { - applyThemePreset(preset); - }); - themePresetsBlueMenu.appendTo(themePresetsMenu); - - // * DARK BLUE THEME PRESETS * // - const themePresetsDarkblueMenu = new Menu('Dark blue'); - themePresetsDarkblueMenu.addRadioItems(['Beveled', 'Beveled 2', 'Gradiented', 'Gradiented 2', 'Minimalized'], pref.preset, - ['darkblueP01', 'darkblueP02', 'darkblueP03', 'darkblueP04', 'darkblueP05'], (preset) => { - applyThemePreset(preset); - }); - themePresetsDarkblueMenu.appendTo(themePresetsMenu); - - // * RED THEME PRESETS * // - const themePresetsRedMenu = new Menu('Red'); - themePresetsRedMenu.addRadioItems(['Beveled', 'Beveled 2', 'Gradiented', 'Gradiented 2', 'Minimalized'], pref.preset, - ['redP01', 'redP02', 'redP03', 'redP04', 'redP05'], (preset) => { - applyThemePreset(preset); - }); - themePresetsRedMenu.appendTo(themePresetsMenu); - - // * CREAM THEME PRESETS * // - const themePresetsCreamMenu = new Menu('Cream'); - themePresetsCreamMenu.addRadioItems(['Beveled', 'Beveled 2', 'Alternative', 'Alternative 2', 'Minimalized'], pref.preset, - ['creamP01', 'creamP02', 'creamP03', 'creamP04', 'creamP05'], (preset) => { - applyThemePreset(preset); - }); - themePresetsCreamMenu.appendTo(themePresetsMenu); - themePresetsMenu.addSeparator(); - - // * NEON BLUE THEME PRESETS * // - const themePresetsNblueMenu = new Menu('Neon blue'); - themePresetsNblueMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], pref.preset, - ['nblueP01', 'nblueP02', 'nblueP03', 'nblueP04', 'nblueP05', 'nblueP06', 'nblueP07', 'nblueP08', 'nblueP09', 'nblueP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsNblueMenu.appendTo(themePresetsMenu); - - // * NEON GREEN THEME PRESETS * // - const themePresetsNgreenMenu = new Menu('Neon green'); - themePresetsNgreenMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], pref.preset, - ['ngreenP01', 'ngreenP02', 'ngreenP03', 'ngreenP04', 'ngreenP05', 'ngreenP06', 'ngreenP07', 'ngreenP08', 'ngreenP09', 'ngreenP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsNgreenMenu.appendTo(themePresetsMenu); - - // * NEON RED THEME PRESETS * // - const themePresetsNredMenu = new Menu('Neon red'); - themePresetsNredMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], pref.preset, - ['nredP01', 'nredP02', 'nredP03', 'nredP04', 'nredP05', 'nredP06', 'nredP07', 'nredP08', 'nredP09', 'nredP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsNredMenu.appendTo(themePresetsMenu); - - // * NEON GOLD THEME PRESETS * // - const themePresetsNgoldMenu = new Menu('Neon gold'); - themePresetsNgoldMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], pref.preset, - ['ngoldP01', 'ngoldP02', 'ngoldP03', 'ngoldP04', 'ngoldP05', 'ngoldP06', 'ngoldP07', 'ngoldP08', 'ngoldP09', 'ngoldP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsNgoldMenu.appendTo(themePresetsMenu); - themePresetsMenu.addSeparator(); - - // * CUSTOM THEME PRESETS * // - const themePresetsCustomMenu = new Menu('Custom'); - themePresetsCustomMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Gradiented', 'Gradiented 2', 'Alternative', 'Alternative 2', 'Minimalized', 'Minimalized blended'], pref.preset, - ['customP01', 'customP02', 'customP03', 'customP04', 'customP05', 'customP06', 'customP07', 'customP08', 'customP09', 'customP10'], (preset) => { - applyThemePreset(preset); - }); - themePresetsCustomMenu.appendTo(themePresetsMenu); - themePresetsMenu.addSeparator(); - - // * CUSTOM USER THEME PRESET * // - const themePresetUserMenu = new Menu('User preset'); - themePresetUserMenu.addRadioItems(['User settings'], pref.preset, ['user'], (preset) => { - if (!pref.themeSandbox) pref.savedPreset = pref.preset = preset; else pref.preset = preset; - resetStyle('all'); - resetTheme(); - pref.theme = customStylePreset.theme; - pref.styleNighttime = customStylePreset.styleNighttime; - pref.styleBevel = customStylePreset.styleBevel; - pref.styleBlend = customStylePreset.styleBlend; - pref.styleBlend2 = customStylePreset.styleBlend2; - pref.styleGradient = customStylePreset.styleGradient; - pref.styleGradient2 = customStylePreset.styleGradient2; - pref.styleAlternative = customStylePreset.styleAlternative; - pref.styleAlternative2 = customStylePreset.styleAlternative2; - pref.styleBlackAndWhite = customStylePreset.styleBlackAndWhite; - pref.styleBlackAndWhite2 = customStylePreset.styleBlackAndWhite2; - pref.styleBlackAndWhiteReborn = customStylePreset.styleBlackAndWhiteReborn; - pref.styleBlackReborn = customStylePreset.styleBlackReborn; - pref.styleRebornWhite = customStylePreset.styleRebornWhite; - pref.styleRebornBlack = customStylePreset.styleRebornBlack; - pref.styleRebornFusion = customStylePreset.styleRebornFusion; - pref.styleRebornFusion2 = customStylePreset.styleRebornFusion2; - pref.styleRebornFusionAccent = customStylePreset.styleRebornFusionAccent; - pref.styleRandomPastel = customStylePreset.styleRandomPastel; - pref.styleRandomDark = customStylePreset.styleRandomDark; - pref.styleRandomAutoColor = customStylePreset.styleRandomAutoColor; - pref.styleTopMenuButtons = customStylePreset.styleTopMenuButtons; - pref.styleTransportButtons = customStylePreset.styleTransportButtons; - pref.styleProgressBarDesign = customStylePreset.styleProgressBarDesign; - pref.styleProgressBar = customStylePreset.styleProgressBar; - pref.styleProgressBarFill = customStylePreset.styleProgressBarFill; - pref.styleVolumeBarDesign = customStylePreset.styleVolumeBarDesign; - pref.styleVolumeBar = customStylePreset.styleVolumeBar; - pref.styleVolumeBarFill = customStylePreset.styleVolumeBarFill; - pref.themeBrightness = customStylePreset.themeBrightness; - updateStyle(); - // After applying the preset, synchronize the daytime/nighttime theme preset if necessary - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - }); - themePresetUserMenu.appendTo(themePresetsMenu); - themePresetsMenu.addSeparator(); - - // * THEME PRESET SELECT MODE * // - presetSelectMode(); - themePresetSelectModeMenu.appendTo(themePresetsMenu); - - // * THEME PRESET SELECTION * // - const themePresetSelectMenu = new Menu('Select presets'); - themePresetSelectMenu.addItem('Activate all', false, () => { - setThemePresetSelection(true); - }); - themePresetSelectMenu.addSeparator(); - themePresetSelectMenu.addItem('Deactivate all', false, () => { - setThemePresetSelection(false); - }); - themePresetSelectMenu.addSeparator(); - themePresetSelectMenu.addToggleItem('White', pref, 'presetSelectWhite'); - themePresetSelectMenu.addToggleItem('Black', pref, 'presetSelectBlack'); - themePresetSelectMenu.addToggleItem('Reborn', pref, 'presetSelectReborn'); - themePresetSelectMenu.addToggleItem('Random', pref, 'presetSelectRandom'); - themePresetSelectMenu.addSeparator(); - themePresetSelectMenu.addToggleItem('Blue', pref, 'presetSelectBlue'); - themePresetSelectMenu.addToggleItem('Dark blue', pref, 'presetSelectDarkblue'); - themePresetSelectMenu.addToggleItem('Red', pref, 'presetSelectRed'); - themePresetSelectMenu.addToggleItem('Cream', pref, 'presetSelectCream'); - themePresetSelectMenu.addSeparator(); - themePresetSelectMenu.addToggleItem('Neon blue', pref, 'presetSelectNblue'); - themePresetSelectMenu.addToggleItem('Neon green', pref, 'presetSelectNgreen'); - themePresetSelectMenu.addToggleItem('Neon red', pref, 'presetSelectNred'); - themePresetSelectMenu.addToggleItem('Neon gold', pref, 'presetSelectNgold'); - themePresetSelectMenu.addSeparator(); - themePresetSelectMenu.addToggleItem('Custom', pref, 'presetSelectCustom'); - themePresetSelectMenu.appendTo(themePresetsMenu, pref.presetSelectMode === 'theme'); - - const themePresetAutoRandomModeMenu = new Menu('Auto random'); - themePresetAutoRandomModeMenu.addRadioItems(['Off', '5 sec', '10 sec', '15 sec', '30 sec', '1 min', '5 min', '10 min', '15 min', '30 min', '60 min', 'New track', 'New album', 'Double-click'], - pref.presetAutoRandomMode, ['off', 5000, 10000, 15000, 30000, 60000, 300000, 600000, 900000, 1800000, 3600000, 'track', 'album', 'dblclick'], (timer) => { - pref.presetAutoRandomMode = timer; - if (!['off', 'track', 'album', 'dblclick'].includes(timer)) { - getRandomThemePreset(); - } else { - clearInterval(presetAutoRandomModeTimer); - presetAutoRandomModeTimer = null; - } - }, pref.presetSelectMode === 'harmonic'); - themePresetAutoRandomModeMenu.appendTo(themePresetsMenu); - themePresetsMenu.addSeparator(); - themePresetsMenu.addToggleItem('Indicator', pref, 'presetIndicator'); - - themePresetsMenu.appendTo(menu); -} - - -///////////////////////////// -// * PLAYER SIZE OPTIONS * // -///////////////////////////// -/** - * Top menu > Options > Player size. - * @param {Menu} menu Creates the Player size menu via a new Menu instance. - */ -function playerSizeOptions(menu) { - menu.createRadioSubMenu('Player size', ['Small', 'Normal', 'Large'], pref.playerSize, ['small', 'normal', 'large'], (size) => { - pref.playerSize = size; - resetPlayerSize(); - if (size === 'small') { - if (!RES_4K && !RES_QHD) { - pref.playerSize_HD_small = true; - display.playerSize_HD_small(); - } else if (RES_QHD) { - pref.playerSize_QHD_small = true; - display.playerSize_QHD_small(); - } else if (RES_4K) { - pref.playerSize_4K_small = true; - display.playerSize_4K_small(); - } - } - if (size === 'normal') { - if (!RES_4K && !RES_QHD) { - pref.playerSize_HD_normal = true; - display.playerSize_HD_normal(); - } else if (RES_QHD) { - pref.playerSize_QHD_normal = true; - display.playerSize_QHD_normal(); - } else if (RES_4K) { - pref.playerSize_4K_normal = true; - display.playerSize_4K_normal(); - } - } - if (size === 'large') { - if (!RES_4K && !RES_QHD) { - pref.playerSize_HD_large = true; - display.playerSize_HD_large(); - } else if (RES_QHD) { - pref.playerSize_QHD_large = true; - display.playerSize_QHD_large(); - } else if (RES_4K) { - pref.playerSize_4K_large = true; - display.playerSize_4K_large(); + /** + * Top menu > Options > Style. + * @param {Menu} menu - Creates the Style menu via a new Menu instance. + * @protected + */ + styleOptions(menu) { + const styleMenu = new Menu('Style'); + grm.ui.themePresetIndicator = true; + grm.ui.themePresetMatchMode = true; + + // * STYLES * // + styleMenu.addToggleItem('Default', grSet, 'styleDefault', () => { + if (grSet.themeSandbox) { + const msg = 'Theme style reset was canceled:\n\nActive theme sandbox needs to be deactivated first\nin order to reset theme styles.\n\n\n'; + ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + grSet.styleDefault = false; + return; } + grSet.preset = false; + grm.ui.resetStyle('all'); + grm.ui.resetStyle('all_theme_day_night'); + grm.ui.restoreThemeStylePreset(true); + grm.ui.resetTheme(); + grm.ui.initTheme(); + }); + styleMenu.addSeparator(); + if (grSet.theme === 'reborn' || grSet.theme === 'random' || grSet.theme.startsWith('custom')) { + styleMenu.addToggleItem('Night', grSet, 'styleNighttime', () => { + if (!grSet.themeSandbox) grSet.savedStyleNighttime = grSet.styleNighttime; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + if (grSet.styleRebornWhite) grSet.styleRebornWhite = false; + if (grSet.styleRebornBlack) grSet.styleRebornBlack = false; + grm.ui.updateStyle(); + }); + styleMenu.addSeparator(); } - RepaintWindow(); - }, pref.lockPlayerSize); -} - + styleMenu.addToggleItem('Bevel', grSet, 'styleBevel', () => { + if (!grSet.themeSandbox) grSet.savedStyleBevel = grSet.styleBevel; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleMenu.addSeparator(); -//////////////////////// -// * LAYOUT OPTIONS * // -//////////////////////// -/** - * Top menu > Options > Layout. - * @param {Menu} menu Creates the Layout menu via a new Menu instance. - */ -function layoutOptions(menu) { - menu.createRadioSubMenu('Layout', ['Default', 'Artwork', 'Compact'], pref.layout, ['default', 'artwork', 'compact'], (layout) => { - pref.layout = layout; - if (pref.layout === 'default') { - displayPlaylist = pref.showPanelOnStartup === 'playlist'; // Switch back to Playlist from Artwork layout to Default layout - displayPlaylistArtwork = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - display.layoutDefault(); - } - if (pref.layout === 'artwork') { - displayPlaylist = false; - displayLibrary = false; - displayBiography = false; - display.layoutArtwork(); - } - if (pref.layout === 'compact') { - displayPlaylist = true; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - display.layoutCompact(); + // * STYLES - GROUP ONE * // + styleMenu.addToggleItem('Blend', grSet, 'styleBlend', () => { + if (!grSet.themeSandbox) grSet.savedStyleBlend = grSet.styleBlend; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('blend', grSet.styleBlend); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Blend 2', grSet, 'styleBlend2', () => { + if (!grSet.themeSandbox) grSet.savedStyleBlend2 = grSet.styleBlend2; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('blend2', grSet.styleBlend2); + grm.ui.updateStyle(); + }); + if (['reborn', 'random', 'blue', 'darkblue', 'red', 'custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'].includes(grSet.theme)) { + styleMenu.addToggleItem('Gradient', grSet, 'styleGradient', () => { + if (!grSet.themeSandbox) grSet.savedStyleGradient = grSet.styleGradient; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('gradient', grSet.styleGradient); + grm.ui.updateStyle(); + }, grSet.styleRebornWhite); + } + if (['reborn', 'random', 'blue', 'darkblue', 'red', 'custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10'].includes(grSet.theme)) { + styleMenu.addToggleItem('Gradient 2', grSet, 'styleGradient2', () => { + if (!grSet.themeSandbox) grSet.savedStyleGradient2 = grSet.styleGradient2; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('gradient2', grSet.styleGradient2); + grm.ui.updateStyle(); + }, grSet.styleRebornWhite); } - initPanels(); - }, pref.lockPlayerSize); -} - + styleMenu.addSeparator(); -///////////////////////// -// * DISPLAY OPTIONS * // -///////////////////////// -/** - * Top menu > Options > Display. - * @param {Menu} menu Creates the Display menu via a new Menu instance. - */ -function displayOptions(menu) { - const displayResMenu = new Menu('Display'); - - displayResMenu.addItem('Auto-detect', false, () => { display.autoDetectRes(); }); - displayResMenu.addSeparator(); - displayResMenu.addRadioItems(['4K', 'QHD', 'HD'], pref.displayRes, ['4K', 'QHD', 'HD'], (res) => { - pref.displayRes = res; - if (pref.layout === 'default') { - display.layoutDefault(); + // * STYLES - GROUP TWO * // + styleMenu.addToggleItem('Alternative', grSet, 'styleAlternative', () => { + if (!grSet.themeSandbox) grSet.savedStyleAlternative = grSet.styleAlternative; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('alternative', grSet.styleAlternative); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Alternative 2', grSet, 'styleAlternative2', () => { + if (!grSet.themeSandbox) grSet.savedStyleAlternative2 = grSet.styleAlternative2; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('alternative2', grSet.styleAlternative2); + grm.ui.updateStyle(); + }); + if (grSet.theme === 'white') { + styleMenu.addToggleItem('Black and white', grSet, 'styleBlackAndWhite', () => { + if (!grSet.themeSandbox) grSet.savedStyleBlackAndWhite = grSet.styleBlackAndWhite; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('blackAndWhite', grSet.styleBlackAndWhite); + grm.ui.updateStyle(); + }, grSet.styleBlackAndWhiteReborn); + styleMenu.addToggleItem('Black and white 2', grSet, 'styleBlackAndWhite2', () => { + if (!grSet.themeSandbox) grSet.savedStyleBlackAndWhite2 = grSet.styleBlackAndWhite2; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('blackAndWhite2', grSet.styleBlackAndWhite2); + grm.ui.updateStyle(); + }, grSet.styleBlackAndWhiteReborn); + styleMenu.addToggleItem('Black and white reborn', grSet, 'styleBlackAndWhiteReborn', () => { + if (!grSet.themeSandbox) grSet.savedStyleBlackAndWhiteReborn = grSet.styleBlackAndWhiteReborn; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('blackAndWhiteReborn', grSet.styleBlackAndWhiteReborn); + grm.ui.updateStyle(); + }); } - else if (pref.layout === 'artwork') { - display.layoutArtwork(); + if (grSet.theme === 'black') { + styleMenu.addToggleItem('Black reborn', grSet, 'styleBlackReborn', () => { + if (!grSet.themeSandbox) grSet.savedStyleBlackReborn = grSet.styleBlackReborn; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('blackReborn', grSet.styleBlackReborn); + grm.ui.updateStyle(); + }); } - else if (pref.layout === 'compact') { - display.layoutCompact(); + if (grSet.theme === 'reborn') { + styleMenu.addToggleItem('Reborn white', grSet, 'styleRebornWhite', () => { + if (!grSet.themeSandbox) grSet.savedStyleRebornWhite = grSet.styleRebornWhite; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + if (grSet.styleNighttime) grSet.styleNighttime = false; + grm.ui.setStyle('rebornWhite', grSet.styleRebornWhite); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Reborn black', grSet, 'styleRebornBlack', () => { + if (!grSet.themeSandbox) grSet.savedStyleRebornBlack = grSet.styleRebornBlack; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + if (grSet.styleNighttime) grSet.styleNighttime = false; + grm.ui.setStyle('rebornBlack', grSet.styleRebornBlack); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Reborn fusion', grSet, 'styleRebornFusion', () => { + if (!grSet.themeSandbox) grSet.savedStyleRebornFusion = grSet.styleRebornFusion; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('rebornFusion', grSet.styleRebornFusion); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Reborn fusion 2', grSet, 'styleRebornFusion2', () => { + if (!grSet.themeSandbox) grSet.savedStyleRebornFusion2 = grSet.styleRebornFusion2; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('rebornFusion2', grSet.styleRebornFusion2); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Reborn fusion accent', grSet, 'styleRebornFusionAccent', () => { + if (!grSet.themeSandbox) grSet.savedStyleRebornFusionAccent = grSet.styleRebornFusionAccent; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('rebornFusionAccent', grSet.styleRebornFusionAccent); + grm.ui.updateStyle(); + }); } - if (pref.displayRes === '4K' || pref.displayRes === 'HD') { - display.setSizesFor4KorHD(); - } else if (pref.displayRes === 'QHD') { - display.setSizesForQHD(); + if (grSet.theme === 'random') { + styleMenu.addToggleItem('Random pastel', grSet, 'styleRandomPastel', () => { + if (!grSet.themeSandbox) grSet.savedStyleRandomPastel = grSet.styleRandomPastel; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('randomPastel', grSet.styleRandomPastel); + grm.ui.updateStyle(); + }); + styleMenu.addToggleItem('Random dark', grSet, 'styleRandomDark', () => { + if (!grSet.themeSandbox) grSet.savedStyleRandomDark = grSet.styleRandomDark; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.setStyle('randomDark', grSet.styleRandomDark); + grm.ui.updateStyle(); + }); } - initPanels(); - }); - - displayResMenu.appendTo(menu); -} + styleMenu.addSeparator(); + // * STYLES - RANDOM AUTO COLOR * // + if (grSet.theme === 'random') { + const styleAutoColorMenu = new Menu('Auto color'); + styleAutoColorMenu.addRadioItems(['Off', '5 sec', '10 sec', '15 sec', '30 sec', '45 sec', '1 min', '2 min', '3 min', '4 min', '5 min', 'New track'], grSet.styleRandomAutoColor, + ['off', 5000, 10000, 15000, 30000, 45000, 60000, 120000, 180000, 240000, 300000, 'track'], (timer) => { + if (!grSet.themeSandbox) grSet.savedStyleRandomAutoColor = grSet.styleRandomAutoColor = timer; else grSet.styleRandomAutoColor = timer; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.color.getRandomThemeAutoColor(); + }); + styleAutoColorMenu.appendTo(styleMenu); + styleMenu.addSeparator(); + } + + // * STYLES - BUTTONS * // + const styleButtonsMenu = new Menu('Buttons'); + const styleTopButtonsMenu = new Menu('Top menu'); + styleTopButtonsMenu.addRadioItems(['Default', 'Filled', 'Bevel', 'Inner', 'Emboss', 'Minimal'], grSet.styleTopMenuButtons, ['default', 'filled', 'bevel', 'inner', 'emboss', 'minimal'], (style) => { + if (!grSet.themeSandbox) grSet.savedStyleTopMenuButtons = grSet.styleTopMenuButtons = style; else grSet.styleTopMenuButtons = style; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleTopButtonsMenu.appendTo(styleButtonsMenu); + const styleTransportButtonsMenu = new Menu('Transport'); + styleTransportButtonsMenu.addRadioItems(['Default', 'Bevel', 'Inner', 'Emboss', 'Minimal'], grSet.styleTransportButtons, ['default', 'bevel', 'inner', 'emboss', 'minimal'], (style) => { + if (!grSet.themeSandbox) grSet.savedStyleTransportButtons = grSet.styleTransportButtons = style; else grSet.styleTransportButtons = style; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleTransportButtonsMenu.appendTo(styleButtonsMenu); + styleButtonsMenu.appendTo(styleMenu); + + // * STYLES - PROGRESS BAR * // + const styleProgressBarMenu = new Menu('Progress bar'); + styleProgressBarMenu.createRadioSubMenu('Design', ['Default', 'Rounded', 'Lines', 'Blocks', 'Dots', 'Thin'], grSet.styleProgressBarDesign, ['default', 'rounded', 'lines', 'blocks', 'dots', 'thin'], (design) => { + if (!grSet.themeSandbox) grSet.savedStyleProgressBarDesign = grSet.styleProgressBarDesign = design; else grSet.styleProgressBarDesign = design; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleProgressBarMenu.createRadioSubMenu('Background', ['Default', 'Bevel', 'Inner'], grSet.styleProgressBar, ['default', 'bevel', 'inner'], (style) => { + if (!grSet.themeSandbox) grSet.savedStyleProgressBar = grSet.styleProgressBar = style; else grSet.styleProgressBar = style; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleProgressBarMenu.createRadioSubMenu('Progress fill', ['Default', 'Bevel', 'Inner', 'Blend'], grSet.styleProgressBarFill, ['default', 'bevel', 'inner', 'blend'], (style) => { + if (!grSet.themeSandbox) grSet.savedStyleProgressBarFill = grSet.styleProgressBarFill = style; else grSet.styleProgressBarFill = style; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleProgressBarMenu.appendTo(styleMenu); + + // * STYLES - VOLUME BAR * // + const styleVolumeBarMenu = new Menu('Volume bar'); + styleVolumeBarMenu.createRadioSubMenu('Design', ['Default', 'Rounded'], grSet.styleVolumeBarDesign, ['default', 'rounded'], (design) => { + if (!grSet.themeSandbox) grSet.savedStyleVolumeBarDesign = grSet.styleVolumeBarDesign = design; else grSet.styleVolumeBarDesign = design; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleVolumeBarMenu.createRadioSubMenu('Background', ['Default', 'Bevel', 'Inner'], grSet.styleVolumeBar, ['default', 'bevel', 'inner'], (style) => { + if (!grSet.themeSandbox) grSet.savedStyleVolumeBar = grSet.styleVolumeBar = style; else grSet.styleVolumeBar = style; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleVolumeBarMenu.createRadioSubMenu('Volume fill', ['Default', 'Bevel', 'Inner'], grSet.styleVolumeBarFill, ['default', 'bevel', 'inner'], (style) => { + if (!grSet.themeSandbox) grSet.savedStyleVolumeBarFill = grSet.styleVolumeBarFill = style; else grSet.styleVolumeBarFill = style; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.updateStyle(); + }); + styleVolumeBarMenu.appendTo(styleMenu); -//////////////////////////// -// * BRIGHTNESS OPTIONS * // -//////////////////////////// -/** - * Top menu > Options > Brightness. - * @param {Menu} menu Creates the Brightness menu via a new Menu instance. - */ -function brightnessOptions(menu) { - menu.createRadioSubMenu('Brightness', [' -50%', ' -40%', ' -30%', ' -25%', ' -20%', ' -15%', ' -10%', ' -5%', 'Default', ' +5%', ' +10%', ' +15%', ' +20%', ' +25%', ' +30%', ' +40%', ' +50%'], - pref.themeBrightness, [-50, -40, -30, -25, -20, -15, -10, -5, 'default', 5, 10, 15, 20, 25, 30, 40, 50], (percent) => { - if (!pref.themeSandbox) pref.savedThemeBrightness = pref.themeBrightness = percent; else pref.themeBrightness = percent; - if (pref.themeSetupDay || pref.themeSetupNight) setThemeDayNightStyle(); - if (pref.themeDayNightMode) ShowThemeDayNightModePopup(); - initThemeFull = true; - initTheme(); - }, pref.presetSelectMode === 'harmonic'); -} + styleMenu.appendTo(menu, grSet.presetSelectMode === 'harmonic'); + } + /** + * Top menu > Options > Preset. + * @param {Menu} menu - Creates the Preset menu via a new Menu instance. + * @protected + */ + presetOptions(menu) { + const themePresetsMenu = new Menu('Preset'); + const themePresetSelectModeMenu = new Menu('Select mode'); + + const presetSelectMode = () => { + themePresetSelectModeMenu.addRadioItems(['Default', 'Harmonic', 'Theme'], grSet.presetSelectMode, ['default', 'harmonic', 'theme'], (mode) => { + grSet.presetSelectMode = mode; + if (mode === 'default') { + const msg = 'Do you want to activate the -Default- preset select mode?\n\nThe default select mode will automatically choose\na random pick of 88 theme presets.\n\nDouble-click on the lower bar to choose\nanother random theme preset.\n\nWhen random mode is activated,\nall themes and style options will be available.\n\nContinue?\n\n\n'; + const msgFb = 'Default preset select mode activated:\n\nThe default preset select mode will automatically choose a random pick of 88 theme presets.\n\nDouble-click on the lower bar to choose another random theme preset.\n\nWhen random mode is activated,\nall themes and style options will be available.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + grSet.presetSelectMode = 'default'; + return; + } + grSet.presetAutoRandomMode = 'dblclick'; + grm.ui.setThemePresetSelection(true); // * Reactivate all + grm.ui.resetStyle('all'); + grm.ui.resetTheme(); + grm.ui.restoreThemeStylePreset(); + if (grSet.savedPreset !== false) grm.preset.setThemePreset(grSet.savedPreset); + grm.ui.updateStyle(); + }); + } + else if (mode === 'harmonic') { + const msg = 'Do you want to activate the -Harmonic- preset select mode?\n\nThe harmonic preset select mode will automatically\nchoose the best visual experience of themes and styles\nbased on album art.\n\nYou can also double-click on the lower bar\nto choose another random harmonic preset.\n\nWhen harmonic preset select mode is activated,\nall themes and almost all style options will be disabled.\n\nContinue?\n\n\n'; + const msgFb = 'Harmonic preset select mode activated:\n\nThe harmonic preset select mode will automatically choose the best visual experience of themes and styles based on album art.\n\nYou can also double-click on the lower bar to choose another random harmonic preset.\n\nWhen harmonic preset select mode is activated,\nall themes and almost all style options will be disabled.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + grSet.presetSelectMode = 'default'; + return; + } + grSet.presetAutoRandomMode = 'dblclick'; + grm.ui.setThemePresetSelection(true); // * Reactivate all + grm.preset.getRandomThemePreset(); + }); + } + else if (mode === 'theme') { + const msg = 'Do you want to activate the -Theme- preset select mode?\n\nThe theme preset select mode will automatically choose\na random theme preset based on current active theme.\n\nYou can also double-click on the lower bar\nto choose another random theme preset.\n\nWhen theme preset select mode is activated,\nall themes and style options will be available.\n\nContinue?\n\n\n'; + const msgFb = 'Theme preset select mode activated:\n\nThe theme preset select mode will automatically choose a random theme preset based on current active theme.\n\nYou can also double-click on the lower bar to choose another random theme preset.\n\nWhen theme preset select mode is activated,\nall themes and style options will be available'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + grSet.presetSelectMode = 'default'; + return; + } + grSet.presetAutoRandomMode = 'dblclick'; + grm.ui.setThemePresetSelection(false, true); + grm.preset.getRandomThemePreset(); + }); + } + }); + }; -/////////////////////////// -// * FONT SIZE OPTIONS * // -/////////////////////////// -/** - * Top menu > Options > Font size. - * @param {Menu} menu Creates the Font size menu via a new Menu instance. - */ -function fontSizeOptions(menu) { - const menuFontSize = pref[`menuFontSize_${pref.layout}`]; - const lowerBarFontSize = pref[`lowerBarFontSize_${pref.layout}`]; - const notificationFontSize = pref[`notificationFontSize_${pref.layout}`]; - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const tooltipFontSize = pref[`tooltipFontSize_${pref.layout}`]; - - const gridArtistFontSize = pref[`gridArtistFontSize_${pref.layout}`]; - const gridTrackNumFontSize = pref[`gridTrackNumFontSize_${pref.layout}`]; - const gridTitleFontSize = pref[`gridTitleFontSize_${pref.layout}`]; - const gridAlbumFontSize = pref[`gridAlbumFontSize_${pref.layout}`]; - const gridKeyFontSize = pref[`gridKeyFontSize_${pref.layout}`]; - const gridValueFontSize = pref[`gridValueFontSize_${pref.layout}`]; - - const playlistHeaderFontSize = pref[`playlistHeaderFontSize_${pref.layout}`]; - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`]; - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`]; - const lyricsFontSize = pref[`lyricsFontSize_${pref.layout}`]; - - const changeFontSizeMenu = new Menu('Font size'); - const mainFontSizeMenu = new Menu('Main'); - - // * MAIN - TOP MENU * // - mainFontSizeMenu.createRadioSubMenu('Top menu', [' 8px', '10px', '11px', RES_QHD ? '12px' : '12px (default)', '13px', RES_QHD ? '14px (default)' : '14px', '16px'], menuFontSize, [8, 10, 11, 12, 13, 14, 16], (size) => { - if (pref.layout === 'default') { - pref.menuFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.menuFontSize_artwork = size; - } - else if (pref.layout === 'compact') { - pref.menuFontSize_compact = size; - } - createFonts(); - createButtonImages(); - createButtonObjects(ww, wh); - RepaintWindow(); - }); - - // * MAIN - LOWER BAR * // - mainFontSizeMenu.createRadioSubMenu('Lower bar', pref.layout !== 'default' ? ['10px', '12px', '14px', RES_QHD ? '16px' : '16px (default)', RES_QHD ? '18px (default)' : '18px', '20px', '22px', '24px', '26px'] : - ['10px', '12px', '14px', '16px', RES_QHD ? '18px' : '18px (default)', RES_QHD ? '20px (default)' : '20px', '22px', '24px', '26px'], lowerBarFontSize, [10, 12, 14, 16, 18, 20, 22, 24, 26], (size) => { - if (pref.layout === 'default') { - pref.lowerBarFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.lowerBarFontSize_artwork = size; - } - else if (pref.layout === 'compact') { - pref.lowerBarFontSize_compact = size; - } - createFonts(); - createButtonImages(); - createButtonObjects(ww, wh); - RepaintWindow(); - }); - mainFontSizeMenu.appendTo(changeFontSizeMenu); - - // * MAIN - NOTIFICATION * // - mainFontSizeMenu.createRadioSubMenu('Notification', ['12px', '14px', '16px', RES_QHD ? '18px' : '18px (default)', RES_QHD ? '20px (default)' : '20px', '22px', '24px'], notificationFontSize, - [12, 14, 16, 18, 20, 22, 24], (size) => { - if (pref.layout === 'default') { pref.notificationFontSize_default = size; } - else if (pref.layout === 'artwork') { pref.notificationFontSize_artwork = size; } - else if (pref.layout === 'compact') { pref.notificationFontSize_compact = size; } - - createFonts(); - RepaintWindow(); - }); - - // * MAIN - POPUP * // - mainFontSizeMenu.createRadioSubMenu('Popup', ['12px', '14px', RES_QHD ? '16px' : '16px (default)', RES_QHD ? '18px (default)' : '18px', '20px', '22px', '24px'], popupFontSize, - [12, 14, 16, 18, 20, 22, 24], (size) => { - if (pref.layout === 'default') { pref.popupFontSize_default = size; } - else if (pref.layout === 'artwork') { pref.popupFontSize_artwork = size; } - else if (pref.layout === 'compact') { pref.popupFontSize_compact = size; } - - createFonts(); - if (displayCustomThemeMenu) initCustomThemeMenu('pl_bg'); - else if (displayMetadataGridMenu) initMetadataGridMenu(); - RepaintWindow(); - }); - - // * MAIN - TOOLTIP * // - mainFontSizeMenu.createRadioSubMenu('Tooltip', ['12px', '14px', RES_QHD ? '16px' : '16px (default)', RES_QHD ? '18px (default)' : '18px', '20px', '22px', '24px'], tooltipFontSize, - [12, 14, 16, 18, 20, 22, 24], (size) => { - if (pref.layout === 'default') { pref.tooltipFontSize_default = size; } - else if (pref.layout === 'artwork') { pref.tooltipFontSize_artwork = size; } - else if (pref.layout === 'compact') { pref.tooltipFontSize_compact = size; } - - createFonts(); - RepaintWindow(); - }); - - // * DETAILS - ARTIST * // - const detailsFontSizeMenu = new Menu('Details'); - detailsFontSizeMenu.createRadioSubMenu('Artist', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', RES_QHD ? '18px' : '18px (default)', '19px', RES_QHD ? '20px (default)' : '20px', '22px', '24px'], gridArtistFontSize, - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { - if (pref.layout === 'default') { - pref.gridArtistFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.gridArtistFontSize_artwork = size; - } - createFonts(); - RepaintWindow(); - }); - - // * DETAILS - TITLE * // - detailsFontSizeMenu.createRadioSubMenu('Title', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', RES_QHD ? '18px' : '18px (default)', '19px', RES_QHD ? '20px (default)' : '20px', '22px', '24px'], gridTrackNumFontSize && gridTitleFontSize, - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { - if (pref.layout === 'default') { - pref.gridTrackNumFontSize_default = size; - pref.gridTitleFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.gridTrackNumFontSize_artwork = size; - pref.gridTitleFontSize_artwork = size; - } - createFonts(); - RepaintWindow(); - }); - - // * DETAILS - ALBUM * // - detailsFontSizeMenu.createRadioSubMenu('Album', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', RES_QHD ? '18px' : '18px (default)', '19px', RES_QHD ? '20px (default)' : '20px', '22px', '24px'], gridAlbumFontSize, - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { - if (pref.layout === 'default') { - pref.gridAlbumFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.gridAlbumFontSize_artwork = size; - } - createFonts(); - RepaintWindow(); - }); - - // * DETAILS - TAG NAME * // - detailsFontSizeMenu.createRadioSubMenu('Tag name', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', RES_QHD ? '17px' : '17px (default)', '18px', RES_QHD ? '19px (default)' : '19px', '20px', '22px', '24px'], gridKeyFontSize, - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { - if (pref.layout === 'default') { - pref.gridKeyFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.gridKeyFontSize_artwork = size; - } - createFonts(); - RepaintWindow(); - }); - - // * DETAILS - TAG VALUE * // - detailsFontSizeMenu.createRadioSubMenu('Tag value', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', RES_QHD ? '17px' : '17px (default)', '18px', RES_QHD ? '19px (default)' : '19px', '20px', '22px', '24px'], gridValueFontSize, - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { - if (pref.layout === 'default') { - pref.gridValueFontSize_default = size; - } - else if (pref.layout === 'artwork') { - pref.gridValueFontSize_artwork = size; - } - createFonts(); - RepaintWindow(); - }); - detailsFontSizeMenu.appendTo(changeFontSizeMenu); - - // * PLAYLIST * // - changeFontSizeMenu.createRadioSubMenu('Playlist', pref.layout === 'default' ? - RES_QHD ? ['-1', '10px', '12px', '13px', '14px', '15px', '16px', '17px (default)', '18px', '20px', '22px', '+1'] : ['-1', '10px', '12px', '13px', '14px', '15px (default)', '16px', '18px', '20px', '22px', '+1'] : - RES_QHD ? ['10px', '12px', '13px', '14px', '15px', '16px', '17px (default)', '18px'] : ['10px', '12px', '13px', '14px', '15px (default)', '16px', '18px'], playlistHeaderFontSize, pref.layout === 'default' ? - RES_QHD ? [-1, 10, 12, 13, 14, 15, 16, 17, 18, 20, 22, 999] : [-1, 10, 12, 13, 14, 15, 16, 18, 20, 22, 999] : - RES_QHD ? [10, 12, 13, 14, 15, 16, 17, 18] : [10, 12, 13, 14, 15, 16, 18], (size) => { - if (size === -1) { - if (pref.layout === 'default') { pref.playlistHeaderFontSize_default--; pref.playlistFontSize_default--; } - else if (pref.layout === 'artwork') { pref.playlistHeaderFontSize_artwork--; pref.playlistFontSize_artwork--; } - else if (pref.layout === 'compact') { pref.playlistHeaderFontSize_compact--; pref.playlistFontSize_compact--; } - } - else if (size === 999) { - if (pref.layout === 'default') { pref.playlistHeaderFontSize_default++; pref.playlistFontSize_default++; } - else if (pref.layout === 'artwork') { pref.playlistHeaderFontSize_artwork++; pref.playlistFontSize_artwork++; } - else if (pref.layout === 'compact') { pref.playlistHeaderFontSize_compact++; pref.playlistFontSize_compact++; } - } - else if (pref.layout === 'default') { pref.playlistHeaderFontSize_default = size; pref.playlistFontSize_default = size - (size === 15 || size === 17 ? 3 : 2); } - else if (pref.layout === 'artwork') { pref.playlistHeaderFontSize_artwork = size; pref.playlistFontSize_artwork = size - (size === 15 || size === 17 ? 3 : 2); } - else if (pref.layout === 'compact') { pref.playlistHeaderFontSize_compact = size; pref.playlistFontSize_compact = size - (size === 15 || size === 17 ? 3 : 2); } - - // * Update Playlist history buttons - createFonts(); - setGeometry(); - createButtonImages(); - createButtonObjects(ww, wh); - // * Update Playlist - rescalePlaylist(true); - Header.art_cache.clear(); - initPlaylist(); - playlist.on_size(ww, wh); - if (pref.libraryLayout === 'split') { - // * Update Library - pop.createImages(); - panel.zoomReset(); - initLibraryLayout(); - } - RepaintWindow(); - }); - - // * LIBRARY * // - changeFontSizeMenu.createRadioSubMenu('Library', ['-1', ' 8px', '10px', '11px', RES_QHD ? '12px' : '12px (default)', '13px', RES_QHD ? '14px (default)' : '14px', '16px', '18px', '+1'], libraryFontSize, - [-1, RES_4K ? 8 * 1.5 : 8, RES_4K ? 10 * 1.5 : 10, RES_4K ? 11 * 1.5 : 11, RES_4K ? 12 * 1.5 : 12, RES_4K ? 13 * 1.5 : 13, RES_4K ? 14 * 1.5 : 14, RES_4K ? 16 * 1.5 : 16, RES_4K ? 18 * 1.5 : 18, 999], (size) => { - if (size === -1) { - if (pref.layout === 'default') { ppt.baseFontSize_default--; } - else if (pref.layout === 'artwork') { ppt.baseFontSize_artwork--; } - } - else if (size === 999) { - if (pref.layout === 'default') { ppt.baseFontSize_default++; } - else if (pref.layout === 'artwork') { ppt.baseFontSize_artwork++; } - } - else if (pref.layout === 'default') { ppt.baseFontSize_default = size; } - else if (pref.layout === 'artwork') { ppt.baseFontSize_artwork = size; } - - pref.libraryFontSize_default = ppt.baseFontSize_default; - pref.libraryFontSize_artwork = ppt.baseFontSize_artwork; - - setLibrarySize(); - panel.zoomReset(); - pop.createImages(); - RepaintWindow(); - }); - - // * BIOGRAPHY * // - changeFontSizeMenu.createRadioSubMenu('Biography', ['-1', ' 8px', '10px', '11px', RES_QHD ? '12px' : '12px (default)', '13px', RES_QHD ? '14px (default)' : '14px', '16px', '18px', '+1'], biographyFontSize, - [-1, RES_4K ? 8 * 1.5 : 8, RES_4K ? 10 * 2 : 10, RES_4K ? 11 * 2 : 11, RES_4K ? 12 * 2 : 12, RES_4K ? 13 * 2 : 13, RES_4K ? 14 * 2 : 14, RES_4K ? 16 * 2 : 16, RES_4K ? 18 * 2 : 18, 999], (size) => { - if (size === -1) { - if (pref.layout === 'default') { pptBio.baseFontSizeBio_default--; } - else if (pref.layout === 'artwork') { pptBio.baseFontSizeBio_artwork--; } - } - else if (size === 999) { - if (pref.layout === 'default') { pptBio.baseFontSizeBio_default++; } - else if (pref.layout === 'artwork') { pptBio.baseFontSizeBio_artwork++; } - } - else if (pref.layout === 'default') { pptBio.baseFontSizeBio_default = size; } - else if (pref.layout === 'artwork') { pptBio.baseFontSizeBio_artwork = size; } - - pref.biographyFontSize_default = pptBio.baseFontSizeBio_default; - pref.biographyFontSize_artwork = pptBio.baseFontSizeBio_artwork; - - setBiographySize(); - butBio.resetZoom(); - butBio.createImages(); - RepaintWindow(); - }); - - // * LYRICS * // - changeFontSizeMenu.createRadioSubMenu('Lyrics', ['-1', '10px', '12px', '14px', '16px', '18px', RES_QHD ? '20px' : '20px (default)', RES_QHD ? '22px (default)' : '22px', '24px', '26px', '28px', '30px', '+1'], lyricsFontSize, - [-1, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 999], (size) => { - if (size === -1) { - if (pref.layout === 'default') { pref.lyricsFontSize_default--; } - else if (pref.layout === 'artwork') { pref.lyricsFontSize_artwork--; } - } - else if (size === 999) { - if (pref.layout === 'default') { pref.lyricsFontSize_default++; } - else if (pref.layout === 'artwork') { pref.lyricsFontSize_artwork++; } + // * HARMONIC MODE EXCLUSIVE MENU * // only show these options when harmonic mode is active + if (grSet.presetSelectMode === 'harmonic') { + presetSelectMode(); + themePresetSelectModeMenu.appendTo(themePresetsMenu); + const themePresetSelectMenu = new Menu('Select presets'); + themePresetSelectMenu.addToggleItem('Neon blue', grSet, 'presetSelectNblue'); + themePresetSelectMenu.addToggleItem('Neon green', grSet, 'presetSelectNgreen'); + themePresetSelectMenu.addToggleItem('Neon red', grSet, 'presetSelectNred'); + themePresetSelectMenu.addToggleItem('Neon gold', grSet, 'presetSelectNgold'); + themePresetSelectMenu.appendTo(themePresetsMenu); + themePresetsMenu.addToggleItem('Indicator', grSet, 'presetIndicator'); + themePresetsMenu.appendTo(menu); + return; } - else if (pref.layout === 'default') { pref.lyricsFontSize_default = size; } - else if (pref.layout === 'artwork') { pref.lyricsFontSize_artwork = size; } - - if (pref.layout === 'default') { pref.lyricsFontSize_default = Math.max(6, pref.lyricsFontSize_default); } - else if (pref.layout === 'artwork') { pref.lyricsFontSize_artwork = Math.max(6, pref.lyricsFontSize_artwork); } - createFonts(); - if (pref.displayLyrics) initLyrics(); - }); + // * THEME PRESETS MENUS * // + const applyThemePreset = (preset) => { + if (!grSet.themeSandbox) grSet.savedPreset = grSet.preset = preset; else grSet.preset = preset; + grm.preset.setThemePreset(preset); // After applying the preset, synchronize the daytime/nighttime theme preset if necessary + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + }; - changeFontSizeMenu.appendTo(menu); -} + // * WHITE THEME PRESETS * // + const themePresetsWhiteMenu = new Menu('White'); + themePresetsWhiteMenu.addRadioItems(['Beveled', 'Black and white', 'Black and white blended', 'Black and white 2', 'Black and white 2 blended', 'Black and white reborn', 'Black and white reborn blended', 'Minimalized'], grSet.preset, + ['whiteP01', 'whiteP02', 'whiteP03', 'whiteP04', 'whiteP05', 'whiteP06', 'whiteP07', 'whiteP08'], (preset) => { + applyThemePreset(preset); + }); + themePresetsWhiteMenu.appendTo(themePresetsMenu); + // * BLACK THEME PRESETS * // + const themePresetsBlackMenu = new Menu('Black'); + themePresetsBlackMenu.addRadioItems(['Beveled', 'Blended', 'Blended alternative', 'Blended alternative 2', 'Black reborn', 'Black reborn blended', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], grSet.preset, + ['blackP01', 'blackP02', 'blackP03', 'blackP04', 'blackP05', 'blackP06', 'blackP07', 'blackP08', 'blackP09', 'blackP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsBlackMenu.appendTo(themePresetsMenu); -///////////////////////////////// -// * PLAYER CONTROLS OPTIONS * // -///////////////////////////////// -/** - * Top menu > Options > Player controls. - * @param {Menu} menu Creates the Player controls menu via a new Menu instance. - */ -function playerControlsOptions(menu) { - const playerControlsMenu = new Menu('Player controls'); - - const playlistCallback = () => { - playlist.on_size(ww, wh); - RepaintWindow(); - }; - - const updateButtons = () => { - createButtonImages(); - createButtonObjects(ww, wh); - RepaintWindow(); - }; - - const updateSeekbar = () => { - setGeometry(); - resizeArtwork(true); - RepaintWindow(); - }; - - // * TOP MENU * // - const playerControlsTopMenu = new Menu('Top menu'); - const playerControlsTopMenuDefault = new Menu('Default'); - playerControlsTopMenuDefault.addToggleItem('Details', pref, 'showPanelDetails_default', () => { updateButtons(); }); - playerControlsTopMenuDefault.addToggleItem('Library', pref, 'showPanelLibrary_default', () => { updateButtons(); }); - playerControlsTopMenuDefault.addToggleItem('Biography', pref, 'showPanelBiography_default', () => { updateButtons(); }); - playerControlsTopMenuDefault.addToggleItem('Lyrics', pref, 'showPanelLyrics_default', () => { updateButtons(); }); - playerControlsTopMenuDefault.addToggleItem('Rating', pref, 'showPanelRating_default', () => { updateButtons(); }); - playerControlsTopMenuDefault.appendTo(playerControlsTopMenu); - - const playerControlsTopMenuArtwork = new Menu('Artwork'); - playerControlsTopMenuArtwork.addToggleItem('Details', pref, 'showPanelDetails_artwork', () => { updateButtons(); }); - playerControlsTopMenuArtwork.addToggleItem('Library', pref, 'showPanelLibrary_artwork', () => { updateButtons(); }); - playerControlsTopMenuArtwork.addToggleItem('Biography', pref, 'showPanelBiography_artwork', () => { updateButtons(); }); - playerControlsTopMenuArtwork.addToggleItem('Lyrics', pref, 'showPanelLyrics_artwork', () => { updateButtons(); }); - playerControlsTopMenuArtwork.addToggleItem('Rating', pref, 'showPanelRating_artwork', () => { updateButtons(); }); - playerControlsTopMenuArtwork.appendTo(playerControlsTopMenu); - playerControlsTopMenu.addSeparator(); - - playerControlsTopMenu.addRadioItems(['Align left', 'Align center'], pref.topMenuAlignment, ['left', 'center'], (align) => { - pref.topMenuAlignment = align; - updateButtons(); - }); - playerControlsTopMenu.addSeparator(); - playerControlsTopMenu.addToggleItem('Compact top menu', pref, 'topMenuCompact', () => { - pref.showTopMenuCompact = pref.topMenuCompact; - updateButtons(); - }); - playerControlsTopMenu.appendTo(playerControlsMenu); - - // * ALBUM ART * // - if (pref.layout !== 'compact') { - const playerControlsAlbumArtMenu = new Menu('Album art'); - const playerControlsAlbumArtNotPropMenu = new Menu('When player size is not proportional'); - if (pref.layout === 'default') { - playerControlsAlbumArtNotPropMenu.addRadioItems(['Align album art left', 'Align album art left (margin)', 'Align album art center', 'Align album art right'], pref.albumArtAlign, ['left', 'leftMargin', 'center', 'right'], (pos) => { - pref.albumArtAlign = pos; - loadImageFromAlbumArtList(albumArtIndex); - resizeArtwork(true); - playlist.on_size(ww, wh); - setLibrarySize(); - setBiographySize(); - RepaintWindow(); - }); - playerControlsAlbumArtNotPropMenu.addSeparator(); - } - playerControlsAlbumArtNotPropMenu.addRadioItems(['Left album art bg', 'Full album art bg', 'No album art bg'], pref.albumArtBg, ['left', 'full', 'none'], (type) => { - pref.albumArtBg = type; - RepaintWindow(); + // * REBORN THEME PRESETS * // + const themePresetsRebornMenu = new Menu('Reborn'); + themePresetsRebornMenu.addRadioItems(['Beveled', 'Blended', 'Blended 2', 'Gradiented', 'Gradiented 2', 'Minimalized', 'Minimalized blended'], grSet.preset, + ['rebornP01', 'rebornP02', 'rebornP03', 'rebornP04', 'rebornP05', 'rebornP06', 'rebornP07'], (preset) => { + applyThemePreset(preset); }); - playerControlsAlbumArtNotPropMenu.appendTo(playerControlsAlbumArtMenu); - const playerControlsAlbumArtScaleMenu = new Menu('When player size is maximized/fullscreen'); - if (pref.layout === 'default') { - playerControlsAlbumArtScaleMenu.addRadioItems(['Scale album art cropped', 'Scale album art stretched', 'Scale album art proportional'], pref.albumArtScale, ['cropped', 'stretched', 'proportional'], (scale) => { - pref.albumArtScale = scale; - loadImageFromAlbumArtList(albumArtIndex); - resizeArtwork(true); - RepaintWindow(); - }); - playerControlsAlbumArtScaleMenu.addSeparator(); - playerControlsAlbumArtScaleMenu.addRadioItems([ - 'Crop and stretch - always', - 'Crop and stretch - limit aspect ratio 1.25x', - 'Crop and stretch - limit aspect ratio 1.50x', - 'Crop and stretch - limit aspect ratio 1.75x', - 'Crop and stretch - limit aspect ratio 2.00x' - ], pref.albumArtAspectRatioLimit, [false, 1.25, 1.5, 1.75, 2], (factor) => { - pref.albumArtAspectRatioLimit = factor; - loadImageFromAlbumArtList(albumArtIndex); - resizeArtwork(true); - RepaintWindow(); - }); - playerControlsAlbumArtScaleMenu.appendTo(playerControlsAlbumArtMenu); - } - playerControlsAlbumArtMenu.addSeparator(); - playerControlsAlbumArtMenu.addToggleItem(`Cycle album artwork (${settings.artworkDisplayTime}s delay)`, pref, 'cycleArt', () => { - if (!pref.cycleArt) { - clearTimeout(albumArtTimeout); - albumArtTimeout = 0; - } else { - displayNextImage(); - } + themePresetsRebornMenu.addSeparator(); + themePresetsRebornMenu.addRadioItems(['Reborn white beveled', 'Reborn white blended', 'Reborn white blended 2'], grSet.preset, + ['rebornP08', 'rebornP09', 'rebornP10'], (preset) => { + applyThemePreset(preset); }); - playerControlsAlbumArtMenu.addToggleItem('Cycle album artwork with mouse wheel', pref, 'cycleArtMWheel'); - playerControlsAlbumArtMenu.addSeparator(); - playerControlsAlbumArtMenu.addToggleItem('Load embedded album art first', pref, 'loadEmbeddedAlbumArtFirst', () => { - const msg = 'Do you want to load embedded album art first?\n\nYou also need to set it in foobar\'s preferences.\nFile > Preferences > Advanced > Display > Album art\n\nContinue?\n\n\n'; - const msgFb = 'Embedded album art enabled:\n\nYou also need to set it in foobar\'s preferences.\nFile > Preferences > Advanced > Display > Album art.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) pref.loadEmbeddedAlbumArtFirst = false; - }); + themePresetsRebornMenu.addSeparator(); + themePresetsRebornMenu.addRadioItems(['Reborn black beveled', 'Reborn black blended', 'Reborn black blended 2', 'Reborn black gradiented', 'Reborn black gradiented 2'], grSet.preset, + ['rebornP11', 'rebornP12', 'rebornP13', 'rebornP14', 'rebornP15'], (preset) => { + applyThemePreset(preset); }); - playerControlsAlbumArtMenu.addSeparator(); - - const showHiResAudioLogoMenu = new Menu('Show hi-res audio badge on album cover'); - showHiResAudioLogoMenu.addToggleItem('Enabled', pref, 'showHiResAudioBadge', () => { RepaintWindow(); }); - showHiResAudioLogoMenu.addSeparator(); - showHiResAudioLogoMenu.addToggleItem('Round', pref, 'hiResAudioBadgeRound', () => { RepaintWindow(); }, !pref.showHiResAudioBadge); - showHiResAudioLogoMenu.addSeparator(); - showHiResAudioLogoMenu.addRadioItems(['Small', 'Normal', 'Large'], pref.hiResAudioBadgeSize, ['small', 'normal', 'large'], (size) => { - pref.hiResAudioBadgeSize = size; - RepaintWindow(); - }, !pref.showHiResAudioBadge); - showHiResAudioLogoMenu.addSeparator(); - showHiResAudioLogoMenu.addRadioItems(['Top left', 'Top right', 'Bottom left', 'Bottom right'], pref.hiResAudioBadgePos, ['topleft', 'topright', 'bottomleft', 'bottomright'], (pos) => { - pref.hiResAudioBadgePos = pos; - RepaintWindow(); - }, !pref.showHiResAudioBadge); - showHiResAudioLogoMenu.appendTo(playerControlsAlbumArtMenu); - playerControlsAlbumArtMenu.addToggleItem('Show pause on album cover', pref, 'showPause', () => { RepaintWindow(); }); - playerControlsAlbumArtMenu.appendTo(playerControlsMenu); - } - - // * JUMP SEARCH * // - const playerControlsJumpSearchMenu = new Menu('Jump search'); - playerControlsJumpSearchMenu.addToggleItem('Include library in playlist search query', pref, 'jumpSearchIncludeLibrary'); - playerControlsJumpSearchMenu.addToggleItem('Include playlist in library search query', pref, 'jumpSearchIncludePlaylist'); - playerControlsJumpSearchMenu.addSeparator(); - playerControlsJumpSearchMenu.addToggleItem('Composer only in jump search query', pref, 'jumpSearchComposerOnly'); - playerControlsJumpSearchMenu.appendTo(playerControlsMenu); - - // * SCROLLBAR * // - const playerControlsScrollbarMenu = new Menu('Scrollbar'); - const playerControlsScrollbarPlaylistMenu = new Menu('Playlist'); - const playerControlsScrollbarPlaylistStepsMenu = new Menu('Mouse wheel scroll steps'); - playerControlsScrollbarPlaylistStepsMenu.addRadioItems(['1 Step', '2 Steps', '3 Steps (default)', '4 Steps', '5 Steps', '6 Steps', '7 Steps', '8 Steps', '9 Steps', '10 Steps'], pref.playlistWheelScrollSteps, [0.5, 2, 3, 4, 5, 6, 7, 8, 9, 10], (steps) => { - pref.playlistWheelScrollSteps = steps; - playlistCallback(); - }); - playerControlsScrollbarPlaylistStepsMenu.appendTo(playerControlsScrollbarPlaylistMenu); - const playerControlsScrollbarPlaylistDurationMenu = new Menu('Mouse wheel scroll smooth duration'); - playerControlsScrollbarPlaylistDurationMenu.addRadioItems(['100ms', '200ms', '300ms (default)', '400ms', '500ms', '600ms', '700ms', '800ms', '900ms', '1000ms'], pref.playlistWheelScrollDuration, [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], (duration) => { - pref.playlistWheelScrollDuration = duration; - playlistCallback(); - }); - playerControlsScrollbarPlaylistDurationMenu.appendTo(playerControlsScrollbarPlaylistMenu); - playerControlsScrollbarPlaylistMenu.addSeparator(); - playerControlsScrollbarPlaylistMenu.addToggleItem('Auto-scroll to current playing song', pref, 'playlistAutoScrollNowPlaying'); - playerControlsScrollbarPlaylistMenu.addToggleItem('Auto-hide', pref, 'playlistAutoHideScrollbar', () => { - g_properties.show_scrollbar = !pref.playlistAutoHideScrollbar; - updatePlaylist(); - }); - playerControlsScrollbarPlaylistMenu.addToggleItem('Smooth scroll', pref, 'playlistSmoothScrolling'); - playerControlsScrollbarPlaylistMenu.appendTo(playerControlsScrollbarMenu); - - const playerControlsScrollbarLibraryMenu = new Menu('Library'); - const playerControlsScrollbarLibraryStepsMenu = new Menu('Mouse wheel scroll steps'); - playerControlsScrollbarLibraryStepsMenu.addRadioItems(['1 Step', '2 Steps', '3 Steps (default)', '4 Steps', '5 Steps', '6 Steps', '7 Steps', '8 Steps', '9 Steps', '10 Steps'], ppt.scrollStep, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (steps) => { - ppt.scrollStep = steps; - panel.updateProp(1); - }); - playerControlsScrollbarLibraryStepsMenu.appendTo(playerControlsScrollbarLibraryMenu); - const playerControlsScrollbarLibraryDurationMenu = new Menu('Mouse wheel scroll smooth duration'); - playerControlsScrollbarLibraryDurationMenu.addRadioItems(['100ms', '200ms', '300ms', '400ms', '500ms (default)', '600ms', '700ms', '800ms', '900ms', '1000ms'], ppt.durationScroll, [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], (duration) => { - ppt.durationScroll = duration; - panel.updateProp(1); - }); - playerControlsScrollbarLibraryDurationMenu.appendTo(playerControlsScrollbarLibraryMenu); - playerControlsScrollbarLibraryMenu.addSeparator(); - playerControlsScrollbarLibraryMenu.addToggleItem('Auto-scroll to current playing song', pref, 'libraryAutoScrollNowPlaying'); - playerControlsScrollbarLibraryMenu.addToggleItem('Auto-hide', pref, 'libraryAutoHideScrollbar', () => { - ppt.sbarShow = pref.libraryAutoHideScrollbar ? 1 : 2; - setLibrarySize(); - }); - playerControlsScrollbarLibraryMenu.addToggleItem('Smooth scroll', ppt, 'smooth'); - playerControlsScrollbarLibraryMenu.appendTo(playerControlsScrollbarMenu); - - const playerControlsBiographyMenu = new Menu('Biography'); - const playerControlsScrollbarBiographyStepsMenu = new Menu('Mouse wheel scroll steps'); - playerControlsScrollbarBiographyStepsMenu.addRadioItems(['1 Step', '2 Steps', '3 Steps (default)', '4 Steps', '5 Steps', '6 Steps', '7 Steps', '8 Steps', '9 Steps', '10 Steps'], pptBio.scrollStep, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (steps) => { - pptBio.scrollStep = steps; - uiBio.updateProp(1); - }); - playerControlsScrollbarBiographyStepsMenu.appendTo(playerControlsBiographyMenu); - const playerControlsScrollbarBiographyDurationMenu = new Menu('Mouse wheel scroll smooth duration'); - playerControlsScrollbarBiographyDurationMenu.addRadioItems(['100ms', '200ms', '300ms', '400ms', '500ms (default)', '600ms', '700ms', '800ms', '900ms', '1000ms'], pptBio.durationScroll, [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], (duration) => { - pptBio.durationScroll = duration; - uiBio.updateProp(1); - }); - playerControlsScrollbarBiographyDurationMenu.appendTo(playerControlsBiographyMenu); - playerControlsBiographyMenu.addSeparator(); - playerControlsBiographyMenu.addToggleItem('Auto-hide', pref, 'biographyAutoHideScrollbar', () => { - if (pref.biographyAutoHideScrollbar) { - pptBio.sbarShow = 1; - butBio.setScrollBtnsHide(); - } else { - pptBio.sbarShow = 2; - butBio.setScrollBtnsHide(false, 'both'); - } - uiBio.updateProp(1); - }); - playerControlsBiographyMenu.addToggleItem('Smooth scroll', pptBio, 'smooth'); - playerControlsBiographyMenu.appendTo(playerControlsScrollbarMenu); - - playerControlsScrollbarMenu.appendTo(playerControlsMenu); - - // * TOOLTIP * // - const playerControlsToolTipMenu = new Menu('Tooltip'); - playerControlsToolTipMenu.addToggleItem('Show tooltips only on truncated text', pref, 'showTooltipTruncated'); - playerControlsToolTipMenu.addSeparator(); - playerControlsToolTipMenu.addToggleItem('Show timeline tooltips', pref, 'showTooltipTimeline'); - playerControlsToolTipMenu.addSeparator(); - const playerControlsVolumeToolTipMenu = new Menu('Show volume tooltips'); - playerControlsVolumeToolTipMenu.addToggleItem('Enabled', pref, 'showTooltipVolume'); - playerControlsVolumeToolTipMenu.addSeparator(); - playerControlsVolumeToolTipMenu.addToggleItem('Show volume in percent', pref, 'showTooltipVolumeInPercent'); - playerControlsVolumeToolTipMenu.appendTo(playerControlsToolTipMenu); - playerControlsToolTipMenu.addSeparator(); - playerControlsToolTipMenu.addToggleItem('Show main tooltips', pref, 'showTooltipMain'); - playerControlsToolTipMenu.addToggleItem('Show library tooltips', pref, 'showTooltipLibrary', () => { - but.tooltipLib.show = pref.showTooltipLibrary || pref.showTooltipTruncated; - setLibrarySize(); - }); - playerControlsToolTipMenu.addToggleItem('Show biography tooltips', pref, 'showTooltipBiography', () => { - butBio.tooltipBio.show = pref.showTooltipBiography || pref.showTooltipTruncated; - setBiographySize(); - }); - playerControlsToolTipMenu.addSeparator(); - playerControlsToolTipMenu.addToggleItem('Show styled tooltips', pref, 'showStyledTooltips'); - playerControlsToolTipMenu.appendTo(playerControlsMenu); - - // * PANEL MENU * // - const playerControlsPanelMenu = new Menu('Panel'); - const playerControlsPanelNotPropMenu = new Menu('Width'); - playerControlsPanelNotPropMenu.addToggleItem('Use auto panel width', pref, 'panelWidthAuto', () => { - pref.albumArtAlign = pref.panelWidthAuto ? 'left' : 'right'; - resizeArtwork(true); - playlist.on_size(ww, wh); - setLibrarySize(); - setBiographySize(); - RepaintWindow(); - }); - playerControlsPanelNotPropMenu.appendTo(playerControlsPanelMenu); - playerControlsPanelMenu.addSeparator(); - if (pref.layout !== 'compact') { - const showPanelOnStartupMenu = new Menu('Show panel on startup'); - showPanelOnStartupMenu.addRadioItems(pref.layout === 'artwork' ? ['Cover', 'Playlist', 'Details', 'Library', 'Biography', 'Lyrics'] : ['Playlist', 'Details', 'Library', 'Biography', 'Lyrics'], - pref.showPanelOnStartup, pref.layout === 'artwork' ? ['cover', 'playlist', 'details', 'library', 'biography', 'lyrics'] : ['playlist', 'details', 'library', 'biography', 'lyrics'], (panel) => { - pref.showPanelOnStartup = panel; - window.Reload(); + themePresetsRebornMenu.addSeparator(); + themePresetsRebornMenu.addRadioItems(['Reborn fusion beveled', 'Reborn fusion blended', 'Reborn fusion blended 2', 'Reborn fusion gradiented', 'Reborn fusion gradiented 2'], grSet.preset, + ['rebornP16', 'rebornP17', 'rebornP18', 'rebornP19', 'rebornP20'], (preset) => { + applyThemePreset(preset); }); - showPanelOnStartupMenu.appendTo(playerControlsPanelMenu); - } - playerControlsPanelMenu.addToggleItem('Show logo on preloader', pref, 'showPreloaderLogo', () => { RepaintWindow(); }); - playerControlsPanelMenu.addSeparator(); - playerControlsPanelMenu.addToggleItem('Return to home on playback stop', pref, 'returnToHomeOnPlaybackStop'); - playerControlsPanelMenu.addSeparator(); - playerControlsPanelMenu.addToggleItem('Hide middle panel shadow', pref, 'hideMiddlePanelShadow', () => { RepaintWindow(); }); - playerControlsPanelMenu.addSeparator(); - playerControlsPanelMenu.addToggleItem('Lock player size', pref, 'lockPlayerSize', () => { UIHacks.DisableSizing = true; }); - playerControlsPanelMenu.appendTo(playerControlsMenu); - - // * LOWER BAR MENU * // - const playerControlsLowerBarMenu = new Menu('Lower bar'); - // * TRANSPORT BUTTON SIZE * // - const transportSizeMenu = new Menu('Transport button size'); - const transportSizeMenuDefault = new Menu('Default'); - transportSizeMenuDefault.addRadioItems(['28px', '30px', '32px (default)', '34px', '36px', '38px', '40px', '42px'], pref.transportButtonSize_default, [28, 30, 32, 34, 36, 38, 40, 42], (size) => { - if (size === -1) { - pref.transportButtonSize_default -= 2; - } else if (size === 999) { - pref.transportButtonSize_default += 2; - } else { - pref.transportButtonSize_default = size; - } - createFonts(); - resizeArtwork(true); - updateButtons(); - }); - transportSizeMenuDefault.appendTo(transportSizeMenu); - - const transportSizeMenuArtwork = new Menu('Artwork'); - transportSizeMenuArtwork.addRadioItems(['28px', '30px', '32px (default)', '34px', '36px'], pref.transportButtonSize_artwork, [28, 30, 32, 34, 36], (size) => { - if (size === -1) { - pref.transportButtonSize_artwork -= 2; - } else if (size === 999) { - pref.transportButtonSize_artwork += 2; - } else { - pref.transportButtonSize_artwork = size; - } - createFonts(); - resizeArtwork(true); - updateButtons(); - }); - transportSizeMenuArtwork.appendTo(transportSizeMenu); - - const transportSizeMenuCompact = new Menu('Compact'); - transportSizeMenuCompact.addRadioItems(['28px', '30px', '32px (default)', '34px', '36px'], pref.transportButtonSize_compact, [28, 30, 32, 34, 36], (size) => { - if (size === -1) { - pref.transportButtonSize_compact -= 2; - } else if (size === 999) { - pref.transportButtonSize_compact += 2; - } else { - pref.transportButtonSize_compact = size; - } - createFonts(); - resizeArtwork(true); - updateButtons(); - }); - transportSizeMenuCompact.appendTo(transportSizeMenu); - transportSizeMenu.appendTo(playerControlsLowerBarMenu); - - // * TRANSPORT BUTTON SPACING * // - const transportSpacingMenu = new Menu('Transport button spacing'); - const transportSpacingMenuDefault = new Menu('Default'); - transportSpacingMenuDefault.addRadioItems(['-2', '3px', '5px (default)', '7px', '10px', '15px', '+2'], pref.transportButtonSpacing_default, [-1, 3, 5, 7, 10, 15, 999], (size) => { - if (size === -1) { - pref.transportButtonSpacing_default -= 2; - } else if (size === 999) { - pref.transportButtonSpacing_default += 2; - } else { - pref.transportButtonSpacing_default = size; - } - updateButtons(); - }); - transportSpacingMenuDefault.appendTo(transportSpacingMenu); - - const transportSpacingMenuArtwork = new Menu('Artwork'); - transportSpacingMenuArtwork.addRadioItems(['-2', '3px', '5px (default)', '7px', '10px', '15px', '+2'], pref.transportButtonSpacing_artwork, [-1, 3, 5, 7, 10, 15, 999], (size) => { - if (size === -1) { - pref.transportButtonSpacing_artwork -= 2; - } else if (size === 999) { - pref.transportButtonSpacing_artwork += 2; - } else { - pref.transportButtonSpacing_artwork = size; - } - updateButtons(); - }); - transportSpacingMenuArtwork.appendTo(transportSpacingMenu); - - const transportSpacingMenuCompact = new Menu('Compact'); - transportSpacingMenuCompact.addRadioItems(['-2', '3px', '5px (default)', '7px', '10px', '15px', '+2'], pref.transportButtonSpacing_compact, [-1, 3, 5, 7, 10, 15, 999], (size) => { - if (size === -1) { - pref.transportButtonSpacing_compact -= 2; - } else if (size === 999) { - pref.transportButtonSpacing_compact += 2; - } else { - pref.transportButtonSpacing_compact = size; - } - updateButtons(); - }); - transportSpacingMenuCompact.appendTo(transportSpacingMenu); - transportSpacingMenu.appendTo(playerControlsLowerBarMenu); - playerControlsLowerBarMenu.addSeparator(); - - // * SHOW TRANSPORT CONTROLS * // - const transportControlsMenu = new Menu('Show transport controls'); - transportControlsMenu.addToggleItem('Default', pref, 'showTransportControls_default', () => { - resizeArtwork(true); - updateButtons(); - }); - transportControlsMenu.addToggleItem('Artwork', pref, 'showTransportControls_artwork', () => { - resizeArtwork(true); - updateButtons(); - }); - transportControlsMenu.addToggleItem('Compact', pref, 'showTransportControls_compact', () => { - resizeArtwork(true); - updateButtons(); - }); - transportControlsMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW PLAYBACK ORDER BUTTON * // - const playbackOrderBtnMenu = new Menu('Show playback order button'); - playbackOrderBtnMenu.addToggleItem('Default', pref, 'showPlaybackOrderBtn_default', () => { - updateButtons(); - }, !pref.showTransportControls_default); - playbackOrderBtnMenu.addToggleItem('Artwork', pref, 'showPlaybackOrderBtn_artwork', () => { - updateButtons(); - }, !pref.showTransportControls_artwork); - playbackOrderBtnMenu.addToggleItem('Compact', pref, 'showPlaybackOrderBtn_compact', () => { - updateButtons(); - }, !pref.showTransportControls_compact); - playbackOrderBtnMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW RELOAD BUTTON * // - const reloadBtnMenu = new Menu('Show reload button'); - reloadBtnMenu.addToggleItem('Default', pref, 'showReloadBtn_default', () => { - volumeBtn = new VolumeBtn(); // create new volume btn for new width size - updateButtons(); - }, !pref.showTransportControls_default); - reloadBtnMenu.addToggleItem('Artwork', pref, 'showReloadBtn_artwork', () => { - volumeBtn = new VolumeBtn(); // create new volume btn for new width size - updateButtons(); - }, !pref.showTransportControls_artwork); - reloadBtnMenu.addToggleItem('Compact', pref, 'showReloadBtn_compact', () => { - volumeBtn = new VolumeBtn(); // create new volume btn for new width size - updateButtons(); - }, !pref.showTransportControls_compact); - reloadBtnMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW VOLUME BUTTON * // - const volumeBtnMenu = new Menu('Show volume button'); - volumeBtnMenu.addToggleItem('Default', pref, 'showVolumeBtn_default', () => { - updateButtons(); - }, !pref.showTransportControls_default); - volumeBtnMenu.addToggleItem('Artwork', pref, 'showVolumeBtn_artwork', () => { - updateButtons(); - }, !pref.showTransportControls_artwork); - volumeBtnMenu.addToggleItem('Compact', pref, 'showVolumeBtn_compact', () => { - updateButtons(); - }, !pref.showTransportControls_compact); - volumeBtnMenu.addSeparator(); - volumeBtnMenu.addToggleItem('Auto-hide bar', pref, 'autoHideVolumeBar', () => { - volumeBtn.toggleVolumeBar(); - updateButtons(); - }); - volumeBtnMenu.appendTo(playerControlsLowerBarMenu); - playerControlsLowerBarMenu.addSeparator(); - - // * SHOW PLAYBACK TIME IN LOWER BAR * // - const playbackTimeMenu = new Menu('Show playback time'); - playbackTimeMenu.addToggleItem('Default', pref, 'showPlaybackTime_default', () => { - updateButtons(); - }); - playbackTimeMenu.addToggleItem('Artwork', pref, 'showPlaybackTime_artwork', () => { - updateButtons(); - }); - playbackTimeMenu.addToggleItem('Compact', pref, 'showPlaybackTime_compact', () => { - updateButtons(); - }); - playbackTimeMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW ARTIST IN LOWER BAR * // - const showArtistMenu = new Menu('Show artist'); - showArtistMenu.addToggleItem('Default', pref, 'showLowerBarArtist_default', () => { RepaintWindow(); }); - showArtistMenu.addToggleItem('Artwork', pref, 'showLowerBarArtist_artwork', () => { RepaintWindow(); }); - showArtistMenu.addToggleItem('Compact', pref, 'showLowerBarArtist_compact', () => { RepaintWindow(); }); - showArtistMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW TRACK NUMBER IN LOWER BAR * // - const showTrackNumberMenu = new Menu('Show track number'); - showTrackNumberMenu.addToggleItem('Default', pref, 'showLowerBarTrackNum_default', () => { on_metadb_changed(); RepaintWindow(); }); - showTrackNumberMenu.addToggleItem('Artwork', pref, 'showLowerBarTrackNum_artwork', () => { on_metadb_changed(); RepaintWindow(); }); - showTrackNumberMenu.addToggleItem('Compact', pref, 'showLowerBarTrackNum_compact', () => { on_metadb_changed(); RepaintWindow(); }); - showTrackNumberMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW SONG TITLE IN LOWER BAR * // - const showTitleMenu = new Menu('Show song title'); - showTitleMenu.addToggleItem('Default', pref, 'showLowerBarTitle_default', () => { RepaintWindow(); }); - showTitleMenu.addToggleItem('Artwork', pref, 'showLowerBarTitle_artwork', () => { RepaintWindow(); }); - showTitleMenu.addToggleItem('Compact', pref, 'showLowerBarTitle_compact', () => { RepaintWindow(); }); - showTitleMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW COMPOSER IN LOWER BAR * // - const showComposerMenu = new Menu('Show composer'); - showComposerMenu.addToggleItem('Default', pref, 'showLowerBarComposer_default', () => { RepaintWindow(); }); - showComposerMenu.addToggleItem('Artwork', pref, 'showLowerBarComposer_artwork', () => { RepaintWindow(); }); - showComposerMenu.addToggleItem('Compact', pref, 'showLowerBarComposer_compact', () => { RepaintWindow(); }); - showComposerMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW ARTIST COUNTRY FLAGS IN LOWER BAR * // - const showArtistFlagsMenu = new Menu('Show artist country flags'); - showArtistFlagsMenu.addToggleItem('Default', pref, 'showLowerBarArtistFlags_default', () => { loadCountryFlags(); RepaintWindow(); }); - showArtistFlagsMenu.addToggleItem('Artwork', pref, 'showLowerBarArtistFlags_artwork', () => { loadCountryFlags(); RepaintWindow(); }); - showArtistFlagsMenu.addToggleItem('Compact', pref, 'showLowerBarArtistFlags_compact', () => { loadCountryFlags(); RepaintWindow(); }); - showArtistFlagsMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW SOFTWARE VERSION IN LOWER BAR * // - const showSoftwareVersionMenu = new Menu('Show software version'); - showSoftwareVersionMenu.addToggleItem('Default', pref, 'showLowerBarVersion_default', () => { initMain(); }); - showSoftwareVersionMenu.addToggleItem('Artwork', pref, 'showLowerBarVersion_artwork', () => { initMain(); }); - showSoftwareVersionMenu.addToggleItem('Compact', pref, 'showLowerBarVersion_compact', () => { initMain(); }); - showSoftwareVersionMenu.appendTo(playerControlsLowerBarMenu); - playerControlsLowerBarMenu.addSeparator(); - - // * SHOW PROGRESS BAR * // - const progressBarMenu = new Menu('Show progress bar'); - progressBarMenu.addToggleItem('Default', pref, 'showProgressBar_default', () => { updateSeekbar(); }); - progressBarMenu.addToggleItem('Artwork', pref, 'showProgressBar_artwork', () => { updateSeekbar(); }); - progressBarMenu.addToggleItem('Compact', pref, 'showProgressBar_compact', () => { updateSeekbar(); }); - progressBarMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW PEAKMETER BAR * // - const peakmeterBarMenu = new Menu('Show peakmeter bar'); - peakmeterBarMenu.addToggleItem('Default', pref, 'showPeakmeterBar_default', () => { updateSeekbar(); }); - peakmeterBarMenu.addToggleItem('Artwork', pref, 'showPeakmeterBar_artwork', () => { updateSeekbar(); }); - peakmeterBarMenu.addToggleItem('Compact', pref, 'showPeakmeterBar_compact', () => { updateSeekbar(); }); - peakmeterBarMenu.appendTo(playerControlsLowerBarMenu); - - // * SHOW WAVEFORM BAR * // - const waveformBarMenu = new Menu('Show waveform bar'); - waveformBarMenu.addToggleItem('Default', pref, 'showWaveformBar_default', () => { updateSeekbar(); }); - waveformBarMenu.addToggleItem('Artwork', pref, 'showWaveformBar_artwork', () => { updateSeekbar(); }); - waveformBarMenu.addToggleItem('Compact', pref, 'showWaveformBar_compact', () => { updateSeekbar(); }); - waveformBarMenu.appendTo(playerControlsLowerBarMenu); - - playerControlsLowerBarMenu.appendTo(playerControlsMenu); - - // * SEEKBAR - PROGRESS BAR * // - const playerControlsSeekBarMenu = new Menu('Seekbar'); - playerControlsSeekBarMenu.createRadioSubMenu('Type', ['Progress bar', 'Peakmeter bar', 'Waveform bar'], pref.seekbar, ['progressbar', 'peakmeterbar', 'waveformbar'], (type) => { - pref.seekbar = type; - if (pref.seekbar === 'waveformbar') { - waveformBar.updateBar(); - } - setProgressBarRefresh(); - RepaintWindow(); - }); - const playerControlsProgressBarMenu = new Menu('Progress bar'); - playerControlsProgressBarMenu.createRadioSubMenu('Style', ['Default', 'Rounded', 'Lines', 'Blocks', 'Dots', 'Thin'], pref.styleProgressBarDesign, ['default', 'rounded', 'lines', 'blocks', 'dots', 'thin'], (style) => { - pref.styleProgressBarDesign = style; - setGeometry(); - RepaintWindow(); - }); - playerControlsProgressBarMenu.createRadioSubMenu('Mouse wheel seek speed', [' 1 sec', ' 2 sec', ' 3 sec', ' 4 sec', ' 5 sec (default)', ' 6 sec', ' 7 sec', ' 8 sec', ' 9 sec', '10 sec'], pref.progressBarWheelSeekSpeed, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (speed) => { - pref.progressBarWheelSeekSpeed = speed; - }); - playerControlsProgressBarMenu.createRadioSubMenu('Refresh rate', ['1000 ms (very slow CPU)', ' 500 ms', ' 333 ms', ' Variable (default)', ' 100 ms', ' 60 ms', ' 30 ms (very fast CPU)'], pref.progressBarRefreshRate, [1000, 500, 333, 'variable', 100, 60, 30], (rate) => { - pref.progressBarRefreshRate = rate; - setProgressBarRefresh(); - }, !pref.showProgressBar_default || !pref.showProgressBar_artwork || !pref.showProgressBar_compact); - playerControlsProgressBarMenu.appendTo(playerControlsSeekBarMenu); - - // * SEEKBAR - PEAKMETER BAR * // - const playerControlspeakmeterBarMenu = new Menu('Peakmeter bar'); - playerControlspeakmeterBarMenu.createRadioSubMenu('Style', ['Horizontal', 'Horizontal center', 'Vertical'], pref.peakmeterBarDesign, ['horizontal', 'horizontal_center', 'vertical'], (design) => { - pref.peakmeterBarDesign = design; - RepaintWindow(); - }); - if (pref.peakmeterBarDesign === 'vertical') { - playerControlspeakmeterBarMenu.createRadioSubMenu('Size', [' 0 px', ' 2 px', ' 4 px', ' 6 px', ' 8 px', '10 px', pref.layout !== 'default' ? '12 px (default)' : '12 px', '14 px', '16 px', '18 px', pref.layout !== 'default' ? '20 px' : '20 px (default)', '25 px', '30 px', '35 px', '40 px', 'Minimum'], pref.peakmeterBarVertSize, [0, 2, 4, 6, 8, 10, 20, 25, 30, 35, 40, 'min'], (size) => { - pref.peakmeterBarVertSize = size; - RepaintWindow(); + themePresetsRebornMenu.addSeparator(); + themePresetsRebornMenu.addRadioItems(['Reborn fusion 2 beveled', 'Reborn fusion 2 blended', 'Reborn fusion 2 blended 2', 'Reborn fusion 2 gradiented', 'Reborn fusion 2 gradiented 2'], grSet.preset, + ['rebornP21', 'rebornP22', 'rebornP23', 'rebornP24', 'rebornP25'], (preset) => { + applyThemePreset(preset); }); - playerControlspeakmeterBarMenu.createRadioSubMenu('Decibel range', ['2 to -20 db (default)', '2 to -15 db', '2 to -10 db', '3 to -20 db', '3 to -15 db', '3 to -10 db', '5 to -20 db', '5 to -15 db', '5 to -10 db'], pref.peakmeterBarVertDbRange, [220, 215, 210, 320, 315, 310, 520, 515, 510], (range) => { - pref.peakmeterBarVertDbRange = range; - RepaintWindow(); + themePresetsRebornMenu.addSeparator(); + themePresetsRebornMenu.addRadioItems(['Reborn fusion accent beveled', 'Reborn fusion accent blended', 'Reborn fusion accent blended 2', 'Reborn fusion accent gradiented', 'Reborn fusion accent gradiented 2'], grSet.preset, + ['rebornP26', 'rebornP27', 'rebornP28', 'rebornP29', 'rebornP30'], (preset) => { + applyThemePreset(preset); }); - } - const playerControlspeakmeterBarDisplayMenu = new Menu('Display'); - if (pref.peakmeterBarDesign === 'horizontal' || pref.peakmeterBarDesign === 'horizontal_center') { - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show over bars', pref, 'peakmeterBarOverBars', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addSeparator(); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show outer bars', pref, 'peakmeterBarOuterBars', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show outer peaks', pref, 'peakmeterBarOuterPeaks', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addSeparator(); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show main bars', pref, 'peakmeterBarMainBars', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show main peaks', pref, 'peakmeterBarMainPeaks', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addSeparator(); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show middle bars', pref, 'peakmeterBarMiddleBars', () => { RepaintWindow(); }); - } - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show progress bar', pref, 'peakmeterBarProgBar', () => { RepaintWindow(); }); - if (pref.peakmeterBarDesign === 'horizontal' || pref.peakmeterBarDesign === 'horizontal_center') { - playerControlspeakmeterBarDisplayMenu.addSeparator(); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show gaps', pref, 'peakmeterBarGaps', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show grid', pref, 'peakmeterBarGrid', () => { peakmeterBar.on_size(ww, wh); RepaintWindow(); }); - } - if (pref.peakmeterBarDesign === 'vertical') { - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show peaks', pref, 'peakmeterBarVertPeaks', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.addToggleItem('Show baseline', pref, 'peakmeterBarVertBaseline', () => { RepaintWindow(); }); - } - playerControlspeakmeterBarDisplayMenu.addToggleItem(pref.layout !== 'default' ? 'Show info (only available in Default layout)' : 'Show info', pref, 'peakmeterBarInfo', () => { RepaintWindow(); }); - playerControlspeakmeterBarDisplayMenu.appendTo(playerControlspeakmeterBarMenu); - playerControlspeakmeterBarMenu.createRadioSubMenu('Refresh rate', [' 200 ms (very slow CPU)', ' 150 ms', ' 120 ms', ' 100 ms', ' 80 ms (default)', ' 60 ms', ' 30 ms (very fast CPU)'], pref.peakmeterBarRefreshRate, [200, 150, 120, 100, 80, 60, 30], (rate) => { - pref.peakmeterBarRefreshRate = rate; - setProgressBarRefresh(); - }, !pref.showPeakmeterBar_default || !pref.showPeakmeterBar_artwork || !pref.showPeakmeterBar_compact); - playerControlspeakmeterBarMenu.appendTo(playerControlsSeekBarMenu); - - // * SEEKBAR - WAVEFORM BAR * // - const playerControlsWaveformBarMenu = new Menu('Waveform bar'); - playerControlsWaveformBarMenu.createRadioSubMenu(`Analysis${pref.waveformBarMode === 'ffprobe' ? '' : '\t (ffprobe only)'}`, - ['RMS level', 'Peak level', 'RMS peak'], pref.waveformBarAnalysis, ['rms_level', 'peak_level', 'rms_peak'], (type) => { - pref.waveformBarAnalysis = type; - waveformBar.updateConfig({ preset: { analysisMode: type } }); - waveformBar.updateBar(); - RepaintWindow(); - }, pref.waveformBarMode !== 'ffprobe'); - - playerControlsWaveformBarMenu.createRadioSubMenu('Mode', ['FFprobe', 'Audiowaveform', 'Visualizer'], pref.waveformBarMode, ['ffprobe', 'audiowaveform', 'visualizer'], (mode) => { - pref.waveformBarMode = mode; - waveformBar.updateConfig({ analysis: { binaryMode: mode } }); - waveformBar.updateBar(); - RepaintWindow(); - }); - - playerControlsWaveformBarMenu.createRadioSubMenu('Style', ['Waveform', 'Bars', 'Dots', 'Halfbars'], pref.waveformBarDesign, ['waveform', 'bars', 'dots', 'halfbars'], (design) => { - pref.waveformBarDesign = design; - waveformBar.updateConfig({ preset: { barDesign: design } }); - }); - - const playerControlsWaveformBarSizeMenu = new Menu('Size'); - playerControlsWaveformBarSizeMenu.createRadioSubMenu('Waveform', ['1', '2', '3', '4', '5'], pref.waveformBarSizeWave, [1, 2, 3, 4, 5], (size) => { - pref.waveformBarSizeWave = size; - waveformBar.updateConfig({ ui: { sizeWave: size } }); - }); - playerControlsWaveformBarSizeMenu.createRadioSubMenu('Bars', ['1', '2', '3', '4', '5'], pref.waveformBarSizeBars, [1, 2, 3, 4, 5], (size) => { - pref.waveformBarSizeBars = size; - waveformBar.updateConfig({ ui: { sizeBars: size } }); - }); - playerControlsWaveformBarSizeMenu.createRadioSubMenu('Dots', ['1', '2', '3', '4', '5'], pref.waveformBarSizeDots, [1, 2, 3, 4, 5], (size) => { - pref.waveformBarSizeDots = size; - waveformBar.updateConfig({ ui: { sizeDots: size } }); - }); - playerControlsWaveformBarSizeMenu.createRadioSubMenu('Halfbars', ['1', '2', '3', '4', '5'], pref.waveformBarSizeHalf, [1, 2, 3, 4, 5], (size) => { - pref.waveformBarSizeHalf = size; - waveformBar.updateConfig({ ui: { sizeHalf: size } }); - }); - playerControlsWaveformBarSizeMenu.addSeparator(); - playerControlsWaveformBarSizeMenu.addToggleItem('Normalize width', pref, 'waveformBarSizeNormalize', () => { - waveformBar.updateConfig({ ui: { sizeNormalizeWidth: pref.waveformBarSizeNormalize } }); - }); - playerControlsWaveformBarSizeMenu.appendTo(playerControlsWaveformBarMenu); - - const playerControlsWaveformBarDisplayMenu = new Menu(`Display${pref.waveformBarPaint === 'full' && pref.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`); - playerControlsWaveformBarDisplayMenu.addRadioItems(['Full', 'Partial'], pref.waveformBarPaint, ['full', 'partial'], (paint) => { - pref.waveformBarPaint = paint; - waveformBar.updateConfig({ preset: { paintMode: paint } }); - }); - playerControlsWaveformBarDisplayMenu.addSeparator(); - - playerControlsWaveformBarDisplayMenu.addToggleItem(`Prepaint${pref.waveformBarPaint === 'full' ? '\t(partial only)' : ''}`, pref, 'waveformBarPrepaint', () => { - waveformBar.updateConfig({ preset: { prepaint: pref.waveformBarPrepaint } }); - }, pref.waveformBarPaint === 'full'); - - const waveformBarPrepaintMenuDisabled = pref.waveformBarPaint === 'full' || pref.waveformBarMode === 'visualizer' || !pref.waveformBarPrepaint; - playerControlsWaveformBarDisplayMenu.createRadioSubMenu('Prepaint front', [' 2 secs', ' 5 secs', '10 secs', ' Full'], pref.waveformBarPrepaintFront, [2, 5, 10, Infinity], (time) => { - pref.waveformBarPrepaintFront = time; - waveformBar.updateConfig({ preset: { prepaintFront: time } }); - }, waveformBarPrepaintMenuDisabled); - playerControlsWaveformBarDisplayMenu.appendTo(playerControlsWaveformBarMenu); - playerControlsWaveformBarDisplayMenu.addSeparator(); - - playerControlsWaveformBarDisplayMenu.addToggleItem('Animate', pref, 'waveformBarAnimate', () => { - waveformBar.updateConfig({ preset: { animate: pref.waveformBarAnimate } }); - }); - - playerControlsWaveformBarDisplayMenu.addToggleItem(`Use BPM${pref.waveformBarPaint === 'full' && pref.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`, pref, 'waveformBarBPM', () => { - if (pref.waveformBarBPM) pref.waveformBarRefreshRateVar = true; - waveformBar.updateConfig({ - preset: { useBPM: pref.waveformBarBPM }, - ui: { refreshRateVar: pref.waveformBarRefreshRateVar } - }); - }, !(pref.waveformBarPaint === 'partial' && pref.waveformBarPrepaint || pref.waveformBarMode === 'visualizer')); - - playerControlsWaveformBarDisplayMenu.addToggleItem('Invert halfbars', pref, 'waveformBarInvertHalfbars', () => { - waveformBar.updateConfig({ preset: { invertHalfbars: pref.waveformBarInvertHalfbars } }); - }); - playerControlsWaveformBarDisplayMenu.addSeparator(); - - playerControlsWaveformBarDisplayMenu.addToggleItem('Show indicator', pref, 'waveformBarIndicator', () => { - waveformBar.updateConfig({ preset: { indicator: pref.waveformBarIndicator } }); - }); - - const playerControlsWaveformBarRefreshMenu = new Menu(`Refresh rate${pref.waveformBarPaint === 'full' && pref.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`); - const waveformBarRefreshMenuDisabled = pref.waveformBarPaint === 'full' || pref.waveformBarMode === 'visualizer' || !pref.waveformBarPrepaint; - playerControlsWaveformBarRefreshMenu.addRadioItems(['1000 ms (very slow CPU)', ' 500 ms', ' 200 ms', ' 100 ms (default)', ' 80 ms', ' 60 ms', ' 30 ms (very fast CPU)'], pref.waveformBarRefreshRate, [1000, 500, 200, 100, 80, 60, 30], (rate) => { - pref.waveformBarRefreshRate = rate; - waveformBar.updateConfig({ ui: { refreshRate: rate } }); - }, waveformBarRefreshMenuDisabled); - playerControlsWaveformBarRefreshMenu.addSeparator(); - playerControlsWaveformBarRefreshMenu.addToggleItem(' Variable refresh rate', pref, 'waveformBarRefreshRateVar', () => { - waveformBar.updateConfig({ ui: { refreshRateVar: pref.waveformBarRefreshRateVar } }); - }); - playerControlsWaveformBarRefreshMenu.appendTo(playerControlsWaveformBarMenu); - playerControlsWaveformBarMenu.appendTo(playerControlsSeekBarMenu); - playerControlsSeekBarMenu.appendTo(playerControlsMenu); - - playerControlsMenu.appendTo(menu); -} + themePresetsRebornMenu.appendTo(themePresetsMenu); + // * RANDOM THEME PRESETS * // + const themePresetsRandomMenu = new Menu('Random'); + themePresetsRandomMenu.addRadioItems(['Beveled blended alternative', 'Beveled blended pastel', 'Beveled blended dark', 'Beveled blended auto dark', 'Beveled auto dark', 'Beveled dark', 'Gradiented', 'Gradiented 2', 'Minimalized', 'Minimalized blended'], grSet.preset, + ['randomP01', 'randomP02', 'randomP03', 'randomP04', 'randomP05', 'randomP06', 'randomP07', 'randomP08', 'randomP09', 'randomP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsRandomMenu.appendTo(themePresetsMenu); + themePresetsMenu.addSeparator(); + + // * BLUE THEME PRESETS * // + const themePresetsBlueMenu = new Menu('Blue'); + themePresetsBlueMenu.addRadioItems(['Beveled', 'Beveled 2', 'Gradiented', 'Gradiented 2', 'Minimalized'], grSet.preset, + ['blueP01', 'blueP02', 'blueP03', 'blueP04', 'blueP05'], (preset) => { + applyThemePreset(preset); + }); + themePresetsBlueMenu.appendTo(themePresetsMenu); -////////////////////////// -// * PLAYLIST OPTIONS * // -////////////////////////// -/** - * Top menu > Options > Playlist. - * @param {Menu} menu Creates the Playlist panel menu via a new Menu instance. - * @param {boolean} context_menu Appends Playlist panel options to context menu. - */ -function playlistOptions(menu, context_menu) { - const playlistMenu = context_menu ? menu : new Menu('Playlist'); - - const playlistCallback = () => { - playlist.on_size(ww, wh); - RepaintWindow(); - }; - - // * LAYOUT * // - if (pref.layout === 'default') { - playlistMenu.createRadioSubMenu('Layout', ['Normal', 'Full'], pref.playlistLayout, ['normal', 'full'], (width) => { - pref.playlistLayout = width; - if (!displayPlaylist) displayPlaylist = true; displayLibrary = false; displayBiography = false; - if (pref.displayLyrics) pref.displayLyrics = false; - resizeArtwork(true); - playlist.on_size(ww, wh); - jumpSearch.on_size(); - initButtonState(); - RepaintWindow(); + // * DARK BLUE THEME PRESETS * // + const themePresetsDarkblueMenu = new Menu('Dark blue'); + themePresetsDarkblueMenu.addRadioItems(['Beveled', 'Beveled 2', 'Gradiented', 'Gradiented 2', 'Minimalized'], grSet.preset, + ['darkblueP01', 'darkblueP02', 'darkblueP03', 'darkblueP04', 'darkblueP05'], (preset) => { + applyThemePreset(preset); }); - } + themePresetsDarkblueMenu.appendTo(themePresetsMenu); - // * PLAYLIST MANAGER * // - const playlistManagerMenu = new Menu('Playlist manager'); - const playlistManagerShowMenu = new Menu('Show playlist manager'); - playlistManagerShowMenu.addToggleItem('Default', pref, 'showPlaylistManager_default', playlistCallback); - playlistManagerShowMenu.addToggleItem('Artwork', pref, 'showPlaylistManager_artwork', playlistCallback); - playlistManagerShowMenu.addToggleItem('Compact', pref, 'showPlaylistManager_compact', playlistCallback); - playlistManagerShowMenu.appendTo(playlistManagerMenu); - playlistManagerMenu.addToggleItem('Show playlist history', pref, 'showPlaylistHistory', () => { RepaintWindow(); }); - playlistManagerMenu.addToggleItem('Auto-hide', pref, 'autoHidePlman', () => { initTheme(); }); - playlistManagerMenu.appendTo(playlistMenu); - - // * ALBUM HEADER * // - const playlistAlbumMenu = new Menu('Album header'); - const playlistAlbumArtMenu = new Menu('Album art'); - playlistAlbumArtMenu.addToggleItem('Show', g_properties, 'show_album_art', () => { updatePlaylist(); }); - playlistAlbumArtMenu.addToggleItem('Auto-hide when no cover', g_properties, 'auto_album_art', () => { updatePlaylist(); }); - playlistAlbumArtMenu.appendTo(playlistAlbumMenu); - playlistAlbumMenu.addSeparator(); - - playlistAlbumMenu.addToggleItem('Album header', g_properties, 'show_header', () => { - updatePlaylist(); - }); - playlistAlbumMenu.addToggleItem('Compact header', g_properties, 'use_compact_header', () => { - createPlaylistFonts(); - rescalePlaylist(true); - initPlaylist(); - playlist.on_size(ww, wh); - RepaintWindow(); - }, !g_properties.show_header); - playlistAlbumMenu.addToggleItem('Auto collapse and expand', g_properties, 'auto_collapse', () => { - initPlaylist(); - playlist.on_size(ww, wh); - }); - playlistAlbumMenu.addSeparator(); - playlistAlbumMenu.addToggleItem('Ctrl+click to follow hyperlinks', pref, 'hyperlinksCtrlClick'); - playlistAlbumMenu.addSeparator(); - playlistAlbumMenu.addToggleItem('Show disc sub-header', g_properties, 'show_disc_header', () => { updatePlaylist(); }); - playlistAlbumMenu.addToggleItem('Show group info', g_properties, 'show_group_info', () => { updatePlaylist(); }); - playlistAlbumMenu.addToggleItem('Show bit depth and sample rate always', settings, 'playlistShowBitSampleAlways', () => { updatePlaylist(); }); - playlistAlbumMenu.addToggleItem('Show weblinks in context menu', pref, 'showWeblinks'); - playlistAlbumMenu.addToggleItem('Show long release date (YYYY-MM-DD)', pref, 'showPlaylistFullDate', () => { updatePlaylist(); }); - playlistAlbumMenu.addSeparator(); - playlistAlbumMenu.addToggleItem('Show rating', g_properties, 'show_rating_header', () => { updatePlaylist(); }); - playlistAlbumMenu.addToggleItem('Show PLR value', g_properties, 'show_PLR_header', () => { updatePlaylist(); }); - playlistAlbumMenu.addSeparator(); - playlistAlbumMenu.addItem('Customize header info', false, () => { inputBox('playlistCustomHeaderInfo'); updatePlaylist(); }); - playlistAlbumMenu.appendTo(playlistMenu); - - // * TRACK ROW * // - const rowsMenu = new Menu('Track row'); - rowsMenu.addToggleItem('Show row stripes', g_properties, 'show_row_stripes', playlistCallback); - rowsMenu.addSeparator(); - rowsMenu.addToggleItem('Show play count', g_properties, 'show_playcount', playlistCallback); - rowsMenu.addToggleItem('Show queue position', g_properties, 'show_queue_position', playlistCallback); - rowsMenu.addSeparator(); - rowsMenu.addToggleItem('Show rating', g_properties, 'show_rating', playlistCallback); - rowsMenu.addToggleItem('Show rating from tags', g_properties, 'use_rating_from_tags', () => { updatePlaylist(); }); - rowsMenu.addToggleItem('Show rating grid', pref, 'showPlaylistRatingGrid', playlistCallback); - rowsMenu.addSeparator(); - rowsMenu.addToggleItem('Show PLR value', g_properties, 'show_PLR', playlistCallback); - rowsMenu.addSeparator(); - rowsMenu.addToggleItem('Show track numbers', pref, 'showPlaylistTrackNumbers', () => { pref.showPlaylistIndexNumbers = false; updatePlaylist(); }); - rowsMenu.addToggleItem('Show index numbers', pref, 'showPlaylistIndexNumbers', () => { pref.showPlaylistTrackNumbers = false; updatePlaylist(); }); - rowsMenu.addSeparator(); - rowsMenu.addToggleItem('Show artist name on difference', pref, 'showDifferentArtist', () => { updatePlaylist(); }); - rowsMenu.addToggleItem('Show artist name in all rows', pref, 'showArtistPlaylistRows', () => { updatePlaylist(); }); - rowsMenu.addToggleItem('Show album title in all rows', pref, 'showAlbumPlaylistRows', () => { updatePlaylist(); }); - rowsMenu.addToggleItem('Show time remaining on playing track', pref, 'playlistTimeRemaining', () => { RepaintWindow(); }); - rowsMenu.addToggleItem('Show vinyl style numbering if available', pref, 'showVinylNums', () => { updatePlaylist(); }); - rowsMenu.addToggleItem('Show last.fm scrobbles on no local plays', pref, 'lastFmScrobblesFallback', () => { updatePlaylist(); }); - rowsMenu.addSeparator(); - rowsMenu.addToggleItem('Row mouse hover', pref, 'playlistRowHover', () => { RepaintWindow(); }); - rowsMenu.addSeparator(); - rowsMenu.addItem('Customize track row', false, () => { inputBox('playlistCustomTrackRow'); updatePlaylist(); }); - rowsMenu.appendTo(playlistMenu); - - // * SORT ORDER * // - const playlistSortOrderMenu = new Menu('Sort order'); - playlistSortOrderMenu.addToggleItem('Always auto-sort', pref, 'playlistSortOrderAuto'); - playlistSortOrderMenu.addSeparator(); - playlistSortOrderMenu.addItem('Sort by...', false, () => { fb.RunMainMenuCommand('Edit/Sort/Sort by...'); updatePlaylist(); }); - playlistSortOrderMenu.addSeparator(); - - const setSorting = () => { - setPlaylistSortOrder(); - playlist.on_size(ww, wh); - RepaintWindow(); - }; - - const sortOrderWithDirection = ['artistDate', 'albumRating', 'albumPlaycount', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country']; - - /** @type {string} Holds the current sort order preference without any direction suffix ('_asc' or '_dsc'). */ - let savedOrder = pref.playlistSortOrder; - /** @type {boolean} Indicaties if the current sort order (`savedOrder`) requires a direction. */ - let savedOrderWithDirection = sortOrderWithDirection.includes(savedOrder.slice(0, -4)); - // Remove direction from saved order for radio item checking - if (savedOrderWithDirection) savedOrder = savedOrder.slice(0, -4); - - playlistSortOrderMenu.addRadioItems(['Order by ascending', 'Order by descending'], pref.playlistSortOrderDirection, ['_asc', '_dsc'], (direction) => { - pref.playlistSortOrder = `${savedOrder}${savedOrderWithDirection ? direction : ''}`; - setSorting(); - }, !savedOrderWithDirection); - - playlistSortOrderMenu.addSeparator(); - - playlistSortOrderMenu.addRadioItems(['Default', 'Artist | date', 'Album', 'Album rating', 'Album playcount', 'Track', 'Track number', 'Track rating', 'Track playcount', 'Year', 'Genre', 'Label', 'Country', 'File path', 'Custom'], savedOrder, - ['default', 'artistDate', 'albumTitle', 'albumRating', 'albumPlaycount', 'trackTitle', 'trackNumber', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country', 'filePath', 'custom'], (order) => { - savedOrderWithDirection = sortOrderWithDirection.includes(order); - savedOrder = order; - pref.playlistSortOrder = `${order}${savedOrderWithDirection ? pref.playlistSortOrderDirection : ''}`; - if (order === 'custom') inputBox('playlistSortCustom'); - setSorting(); - }, false, !pref.playlistSortOrderAuto); - playlistSortOrderMenu.addSeparator(); - - playlistSortOrderMenu.addItem('Randomize', false, () => { fb.RunMainMenuCommand('Edit/Sort/Randomize'); updatePlaylist(); }); - playlistSortOrderMenu.addItem('Reverse', false, () => { fb.RunMainMenuCommand('Edit/Sort/Reverse'); updatePlaylist(); }); - playlistSortOrderMenu.addSeparator(); - playlistSortOrderMenu.addItem('Save', false, () => { fb.RunMainMenuCommand('File/Save playlist...'); updatePlaylist(); }); - playlistSortOrderMenu.addItem('Load', false, () => { fb.RunMainMenuCommand('File/Load playlist...'); updatePlaylist(); }); - playlistSortOrderMenu.addItem('Undo', false, () => { fb.RunMainMenuCommand('Edit/Undo'); updatePlaylist(); }); - - playlistSortOrderMenu.appendTo(playlistMenu); - - if (!context_menu) playlistMenu.appendTo(menu); -} + // * RED THEME PRESETS * // + const themePresetsRedMenu = new Menu('Red'); + themePresetsRedMenu.addRadioItems(['Beveled', 'Beveled 2', 'Gradiented', 'Gradiented 2', 'Minimalized'], grSet.preset, + ['redP01', 'redP02', 'redP03', 'redP04', 'redP05'], (preset) => { + applyThemePreset(preset); + }); + themePresetsRedMenu.appendTo(themePresetsMenu); + // * CREAM THEME PRESETS * // + const themePresetsCreamMenu = new Menu('Cream'); + themePresetsCreamMenu.addRadioItems(['Beveled', 'Beveled 2', 'Alternative', 'Alternative 2', 'Minimalized'], grSet.preset, + ['creamP01', 'creamP02', 'creamP03', 'creamP04', 'creamP05'], (preset) => { + applyThemePreset(preset); + }); + themePresetsCreamMenu.appendTo(themePresetsMenu); + themePresetsMenu.addSeparator(); + + // * NEON BLUE THEME PRESETS * // + const themePresetsNblueMenu = new Menu('Neon blue'); + themePresetsNblueMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], grSet.preset, + ['nblueP01', 'nblueP02', 'nblueP03', 'nblueP04', 'nblueP05', 'nblueP06', 'nblueP07', 'nblueP08', 'nblueP09', 'nblueP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsNblueMenu.appendTo(themePresetsMenu); -///////////////////////// -// * DETAILS OPTIONS * // -///////////////////////// -/** - * Top menu > Options > Details. - * @param {Menu} menu Creates the Details panel menu via a new Menu instance. - * @param {boolean} context_menu Appends Details panel options to context menu. - */ -function detailsOptions(menu, context_menu) { - const detailsMenu = context_menu ? menu : new Menu('Details'); + // * NEON GREEN THEME PRESETS * // + const themePresetsNgreenMenu = new Menu('Neon green'); + themePresetsNgreenMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], grSet.preset, + ['ngreenP01', 'ngreenP02', 'ngreenP03', 'ngreenP04', 'ngreenP05', 'ngreenP06', 'ngreenP07', 'ngreenP08', 'ngreenP09', 'ngreenP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsNgreenMenu.appendTo(themePresetsMenu); - if (pref.layout === 'default') { - const discArtMenu = new Menu('Disc art'); - const displayDiscArtMenu = new Menu('Disc art placeholder'); + // * NEON RED THEME PRESETS * // + const themePresetsNredMenu = new Menu('Neon red'); + themePresetsNredMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], grSet.preset, + ['nredP01', 'nredP02', 'nredP03', 'nredP04', 'nredP05', 'nredP06', 'nredP07', 'nredP08', 'nredP09', 'nredP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsNredMenu.appendTo(themePresetsMenu); - // * DISC ART PLACEHOLDER * // - displayDiscArtMenu.addToggleItem('Show placeholder if no disc art found', pref, 'showDiscArtStub', () => { - pref.noDiscArtStub = false; - fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }, !pref.displayDiscArt); - displayDiscArtMenu.addSeparator(); - displayDiscArtMenu.addToggleItem('No placeholder', pref, 'noDiscArtStub', () => { - pref.showDiscArtStub = false; - discArt = disposeDiscArt(discArt); - discArtCover = disposeDiscArt(discArtCover); - discArtArray = []; - discArtArrayCover = []; - if (!pref.noDiscArtStub) fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }, !pref.displayDiscArt); - displayDiscArtMenu.addSeparator(); - displayDiscArtMenu.addRadioItems(['CD - Album cover', 'CD - White', 'CD - Black', 'CD - Blank', 'CD - Transparent'], - pref.discArtStub, ['cdAlbumCover', 'cdWhite', 'cdBlack', 'cdBlank', 'cdTrans'], (discArt) => { - pref.discArtStub = discArt; - pref.noDiscArtStub = false; - discArtCover = disposeDiscArt(discArtCover); - discArtArrayCover = []; - fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }, !pref.displayDiscArt); - displayDiscArtMenu.addSeparator(); - displayDiscArtMenu.addRadioItems(['Vinyl - Album cover', 'Vinyl - White', 'Vinyl - Void', 'Vinyl - Cold fusion', 'Vinyl - Ring of fire', 'Vinyl - Maple', 'Vinyl - Black', 'Vinyl - Black hole', 'Vinyl - Ebony', 'Vinyl - Transparent'], - pref.discArtStub, ['vinylAlbumCover', 'vinylWhite', 'vinylVoid', 'vinylColdFusion', 'vinylRingOfFire', 'vinylMaple', 'vinylBlack', 'vinylBlackHole', 'vinylEbony', 'vinylTrans'], (discArt) => { - pref.discArtStub = discArt; - pref.noDiscArtStub = false; - discArtCover = disposeDiscArt(discArtCover); - discArtArrayCover = []; - fetchNewArtwork(fb.GetNowPlaying()); - RepaintWindow(); - }, !pref.displayDiscArt); - - // * DISC ART CUSTOM PLACEHOLDERS * // - const customDiscArtLabels = []; - const customDiscArtValues = []; - for (const key in customDiscArtStub) { - if (Object.prototype.hasOwnProperty.call(customDiscArtStub, key) && key.includes('Name')) { - const num = key.match(/\d+$/)[0]; // Extract the number from the key (e.g., "01" from "cdName01") - const cdStubKey = `cdStub${num}`; - const vinylStubKey = `vinylStub${num}`; - if (key.startsWith('cdName') && customDiscArtStub[cdStubKey]) { - customDiscArtLabels.push(customDiscArtStub[key]); - customDiscArtValues.push(customDiscArtStub[cdStubKey].replace('.png', '')); + // * NEON GOLD THEME PRESETS * // + const themePresetsNgoldMenu = new Menu('Neon gold'); + themePresetsNgoldMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Alternative', 'Alternative 2', 'Dark gray', 'Dark gray blended', 'Dark gray 2 blended', 'Minimalized'], grSet.preset, + ['ngoldP01', 'ngoldP02', 'ngoldP03', 'ngoldP04', 'ngoldP05', 'ngoldP06', 'ngoldP07', 'ngoldP08', 'ngoldP09', 'ngoldP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsNgoldMenu.appendTo(themePresetsMenu); + themePresetsMenu.addSeparator(); + + // * CUSTOM THEME PRESETS * // + const themePresetsCustomMenu = new Menu('Custom'); + themePresetsCustomMenu.addRadioItems(['Beveled', 'Beveled 2', 'Blended', 'Blended 2', 'Gradiented', 'Gradiented 2', 'Alternative', 'Alternative 2', 'Minimalized', 'Minimalized blended'], grSet.preset, + ['customP01', 'customP02', 'customP03', 'customP04', 'customP05', 'customP06', 'customP07', 'customP08', 'customP09', 'customP10'], (preset) => { + applyThemePreset(preset); + }); + themePresetsCustomMenu.appendTo(themePresetsMenu); + themePresetsMenu.addSeparator(); + + // * CUSTOM USER THEME PRESET * // + const themePresetUserMenu = new Menu('User preset'); + themePresetUserMenu.addRadioItems(['User settings'], grSet.preset, ['user'], (preset) => { + if (!grSet.themeSandbox) grSet.savedPreset = grSet.preset = preset; else grSet.preset = preset; + grm.ui.resetStyle('all'); + grm.ui.resetTheme(); + grSet.theme = grCfg.customStylePreset.theme; + grSet.styleNighttime = grCfg.customStylePreset.styleNighttime; + grSet.styleBevel = grCfg.customStylePreset.styleBevel; + grSet.styleBlend = grCfg.customStylePreset.styleBlend; + grSet.styleBlend2 = grCfg.customStylePreset.styleBlend2; + grSet.styleGradient = grCfg.customStylePreset.styleGradient; + grSet.styleGradient2 = grCfg.customStylePreset.styleGradient2; + grSet.styleAlternative = grCfg.customStylePreset.styleAlternative; + grSet.styleAlternative2 = grCfg.customStylePreset.styleAlternative2; + grSet.styleBlackAndWhite = grCfg.customStylePreset.styleBlackAndWhite; + grSet.styleBlackAndWhite2 = grCfg.customStylePreset.styleBlackAndWhite2; + grSet.styleBlackAndWhiteReborn = grCfg.customStylePreset.styleBlackAndWhiteReborn; + grSet.styleBlackReborn = grCfg.customStylePreset.styleBlackReborn; + grSet.styleRebornWhite = grCfg.customStylePreset.styleRebornWhite; + grSet.styleRebornBlack = grCfg.customStylePreset.styleRebornBlack; + grSet.styleRebornFusion = grCfg.customStylePreset.styleRebornFusion; + grSet.styleRebornFusion2 = grCfg.customStylePreset.styleRebornFusion2; + grSet.styleRebornFusionAccent = grCfg.customStylePreset.styleRebornFusionAccent; + grSet.styleRandomPastel = grCfg.customStylePreset.styleRandomPastel; + grSet.styleRandomDark = grCfg.customStylePreset.styleRandomDark; + grSet.styleRandomAutoColor = grCfg.customStylePreset.styleRandomAutoColor; + grSet.styleTopMenuButtons = grCfg.customStylePreset.styleTopMenuButtons; + grSet.styleTransportButtons = grCfg.customStylePreset.styleTransportButtons; + grSet.styleProgressBarDesign = grCfg.customStylePreset.styleProgressBarDesign; + grSet.styleProgressBar = grCfg.customStylePreset.styleProgressBar; + grSet.styleProgressBarFill = grCfg.customStylePreset.styleProgressBarFill; + grSet.styleVolumeBarDesign = grCfg.customStylePreset.styleVolumeBarDesign; + grSet.styleVolumeBar = grCfg.customStylePreset.styleVolumeBar; + grSet.styleVolumeBarFill = grCfg.customStylePreset.styleVolumeBarFill; + grSet.themeBrightness = grCfg.customStylePreset.themeBrightness; + grm.ui.updateStyle(); + // After applying the preset, synchronize the daytime/nighttime theme preset if necessary + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + }); + themePresetUserMenu.appendTo(themePresetsMenu); + themePresetsMenu.addSeparator(); + + // * THEME PRESET SELECT MODE * // + presetSelectMode(); + themePresetSelectModeMenu.appendTo(themePresetsMenu); + + // * THEME PRESET SELECTION * // + const themePresetSelectMenu = new Menu('Select presets'); + themePresetSelectMenu.addItem('Activate all', false, () => { + grm.ui.setThemePresetSelection(true); + }); + themePresetSelectMenu.addSeparator(); + themePresetSelectMenu.addItem('Deactivate all', false, () => { + grm.ui.setThemePresetSelection(false); + }); + themePresetSelectMenu.addSeparator(); + themePresetSelectMenu.addToggleItem('White', grSet, 'presetSelectWhite'); + themePresetSelectMenu.addToggleItem('Black', grSet, 'presetSelectBlack'); + themePresetSelectMenu.addToggleItem('Reborn', grSet, 'presetSelectReborn'); + themePresetSelectMenu.addToggleItem('Random', grSet, 'presetSelectRandom'); + themePresetSelectMenu.addSeparator(); + themePresetSelectMenu.addToggleItem('Blue', grSet, 'presetSelectBlue'); + themePresetSelectMenu.addToggleItem('Dark blue', grSet, 'presetSelectDarkblue'); + themePresetSelectMenu.addToggleItem('Red', grSet, 'presetSelectRed'); + themePresetSelectMenu.addToggleItem('Cream', grSet, 'presetSelectCream'); + themePresetSelectMenu.addSeparator(); + themePresetSelectMenu.addToggleItem('Neon blue', grSet, 'presetSelectNblue'); + themePresetSelectMenu.addToggleItem('Neon green', grSet, 'presetSelectNgreen'); + themePresetSelectMenu.addToggleItem('Neon red', grSet, 'presetSelectNred'); + themePresetSelectMenu.addToggleItem('Neon gold', grSet, 'presetSelectNgold'); + themePresetSelectMenu.addSeparator(); + themePresetSelectMenu.addToggleItem('Custom', grSet, 'presetSelectCustom'); + themePresetSelectMenu.appendTo(themePresetsMenu, grSet.presetSelectMode === 'theme'); + + const themePresetAutoRandomModeMenu = new Menu('Auto random'); + themePresetAutoRandomModeMenu.addRadioItems(['Off', '5 sec', '10 sec', '15 sec', '30 sec', '1 min', '5 min', '10 min', '15 min', '30 min', '60 min', 'New track', 'New album', 'Double-click'], + grSet.presetAutoRandomMode, ['off', 5000, 10000, 15000, 30000, 60000, 300000, 600000, 900000, 1800000, 3600000, 'track', 'album', 'dblclick'], (timer) => { + grSet.presetAutoRandomMode = timer; + if (!['off', 'track', 'album', 'dblclick'].includes(timer)) { + grm.preset.getRandomThemePreset(); + } else { + clearInterval(grm.ui.presetAutoRandomModeTimer); + grm.ui.presetAutoRandomModeTimer = null; + } + }, grSet.presetSelectMode === 'harmonic'); + themePresetAutoRandomModeMenu.appendTo(themePresetsMenu); + themePresetsMenu.addSeparator(); + themePresetsMenu.addToggleItem('Indicator', grSet, 'presetIndicator'); + + themePresetsMenu.appendTo(menu); + } + + /** + * Top menu > Options > Player size. + * @param {Menu} menu - Creates the Player size menu via a new Menu instance. + * @protected + */ + playerSizeOptions(menu) { + menu.createRadioSubMenu('Player size', ['Small', 'Normal', 'Large'], grSet.playerSize, ['small', 'normal', 'large'], (size) => { + grSet.playerSize = size; + grm.ui.resetPlayerSize(); + if (size === 'small') { + if (!RES._4K && !RES._QHD) { + grSet.playerSize_HD_small = true; + grm.display.playerSize_HD_small(); + } else if (RES._QHD) { + grSet.playerSize_QHD_small = true; + grm.display.playerSize_QHD_small(); + } else if (RES._4K) { + grSet.playerSize_4K_small = true; + grm.display.playerSize_4K_small(); } - else if (key.startsWith('vinylName') && customDiscArtStub[vinylStubKey]) { - customDiscArtLabels.push(customDiscArtStub[key]); - customDiscArtValues.push(customDiscArtStub[vinylStubKey].replace('.png', '')); + } + if (size === 'normal') { + if (!RES._4K && !RES._QHD) { + grSet.playerSize_HD_normal = true; + grm.display.playerSize_HD_normal(); + } else if (RES._QHD) { + grSet.playerSize_QHD_normal = true; + grm.display.playerSize_QHD_normal(); + } else if (RES._4K) { + grSet.playerSize_4K_normal = true; + grm.display.playerSize_4K_normal(); + } + } + if (size === 'large') { + if (!RES._4K && !RES._QHD) { + grSet.playerSize_HD_large = true; + grm.display.playerSize_HD_large(); + } else if (RES._QHD) { + grSet.playerSize_QHD_large = true; + grm.display.playerSize_QHD_large(); + } else if (RES._4K) { + grSet.playerSize_4K_large = true; + grm.display.playerSize_4K_large(); } } - } - if (customDiscArtValues.length) displayDiscArtMenu.addSeparator(); - displayDiscArtMenu.addRadioItems(customDiscArtLabels, pref.discArtStub, customDiscArtValues, (discArt) => { - pref.discArtStub = discArt; - pref.noDiscArtStub = false; - paths.discArtCustomStub = `${imagesPathBase}custom\\discart\\${pref.discArtStub}.png`; - discArtCover = disposeDiscArt(discArtCover); - discArtArrayCover = []; - fetchNewArtwork(fb.GetNowPlaying()); RepaintWindow(); - if (!IsFile(paths.discArtCustomStub)) { - const msg = `The custom disc art placeholder was not found in:\n${paths.discArtCustomStub}\n\nBe sure that image exist and has the correct filename\nin the "customDiscArtStub" section of the\ncustom config file:\n${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc\n\n\n`; - ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + }, grSet.lockPlayerSize); + } + + /** + * Top menu > Options > Layout. + * @param {Menu} menu - Creates the Layout menu via a new Menu instance. + * @protected + */ + layoutOptions(menu) { + menu.createRadioSubMenu('Layout', ['Default', 'Artwork', 'Compact'], grSet.layout, ['default', 'artwork', 'compact'], (layout) => { + grSet.layout = layout; + if (grSet.layout === 'default') { + grm.ui.displayPlaylist = grSet.showPanelOnStartup === 'playlist'; // Switch back to Playlist from Artwork layout to Default layout + grm.ui.displayPanelControl(); + grm.display.layoutDefault(); + } + if (grSet.layout === 'artwork') { + grSet.showPanelOnStartup = 'cover'; + grm.ui.displayPlaylist = false; + grm.ui.displayPlaylistArtwork = false; + grm.ui.displayDetails = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.display.layoutArtwork(); } - }, !pref.displayDiscArt); - displayDiscArtMenu.appendTo(discArtMenu); + if (grSet.layout === 'compact') { + grm.ui.displayPlaylist = true; + grm.ui.displayDetails = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + grm.display.layoutCompact(); + } + grm.ui.initPanels(); + }, grSet.lockPlayerSize); + } - // * DISC ART OPTIONS * // - discArtMenu.addToggleItem(`Display disc art if found (${settings.discArtBasename}.png, ${settings.discArtBasename}2.png, vinylA.png, etc.)`, pref, 'displayDiscArt', () => { - if (fb.IsPlaying) fetchNewArtwork(fb.GetNowPlaying()); - lastLeftEdge = 0; // resize labels - resizeArtwork(true); - RepaintWindow(); + /** + * Top menu > Options > Display. + * @param {Menu} menu - Creates the Display menu via a new Menu instance. + * @protected + */ + displayOptions(menu) { + const displayResMenu = new Menu('Display'); + + displayResMenu.addItem('Auto-detect', false, () => { grm.display.autoDetectRes(); }); + displayResMenu.addSeparator(); + displayResMenu.addRadioItems(['4K', 'QHD', 'HD'], grSet.displayRes, ['4K', 'QHD', 'HD'], (res) => { + grSet.displayRes = res; + if (grSet.layout === 'default') { + grm.display.layoutDefault(); + } + else if (grSet.layout === 'artwork') { + grm.display.layoutArtwork(); + } + else if (grSet.layout === 'compact') { + grm.display.layoutCompact(); + } + if (grSet.displayRes === '4K' || grSet.displayRes === 'HD') { + grm.display.setSizesFor4KorHD(); + } else if (grSet.displayRes === 'QHD') { + grm.display.setSizesForQHD(); + } + grm.ui.initPanels(); }); - discArtMenu.addToggleItem('Display disc art above cover', pref, 'discArtOnTop', () => { - pref.detailsAlbumArtDiscAreaOpacity = 255; + displayResMenu.appendTo(menu); + } + + /** + * Top menu > Options > Brightness. + * @param {Menu} menu - Creates the Brightness menu via a new Menu instance. + * @protected + */ + brightnessOptions(menu) { + menu.createRadioSubMenu('Brightness', [' -50%', ' -40%', ' -30%', ' -25%', ' -20%', ' -15%', ' -10%', ' -5%', 'Default', ' +5%', ' +10%', ' +15%', ' +20%', ' +25%', ' +30%', ' +40%', ' +50%'], + grSet.themeBrightness, [-50, -40, -30, -25, -20, -15, -10, -5, 'default', 5, 10, 15, 20, 25, 30, 40, 50], (percent) => { + if (!grSet.themeSandbox) grSet.savedThemeBrightness = grSet.themeBrightness = percent; else grSet.themeBrightness = percent; + if (grSet.themeSetupDay || grSet.themeSetupNight) setThemeDayNightStyle(); + if (grSet.themeDayNightMode) ShowThemeDayNightModePopup(); + grm.ui.initThemeFull = true; + grm.ui.initTheme(); + }, grSet.presetSelectMode === 'harmonic'); + } + + /** + * Top menu > Options > Font size. + * @param {Menu} menu - Creates the Font size menu via a new Menu instance. + * @protected + */ + fontSizeOptions(menu) { + const menuFontSize = grSet[`menuFontSize_${grSet.layout}`]; + const lowerBarFontSize = grSet[`lowerBarFontSize_${grSet.layout}`]; + const notificationFontSize = grSet[`notificationFontSize_${grSet.layout}`]; + const popupFontSize = grSet[`popupFontSize_${grSet.layout}`]; + const tooltipFontSize = grSet[`tooltipFontSize_${grSet.layout}`]; + + const gridArtistFontSize = grSet[`gridArtistFontSize_${grSet.layout}`]; + const gridTrackNumFontSize = grSet[`gridTrackNumFontSize_${grSet.layout}`]; + const gridTitleFontSize = grSet[`gridTitleFontSize_${grSet.layout}`]; + const gridAlbumFontSize = grSet[`gridAlbumFontSize_${grSet.layout}`]; + const gridKeyFontSize = grSet[`gridKeyFontSize_${grSet.layout}`]; + const gridValueFontSize = grSet[`gridValueFontSize_${grSet.layout}`]; + + const playlistHeaderFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`]; + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`]; + const lyricsFontSize = grSet[`lyricsFontSize_${grSet.layout}`]; + + const changeFontSizeMenu = new Menu('Font size'); + const mainFontSizeMenu = new Menu('Main'); + + // * MAIN - TOP MENU * // + mainFontSizeMenu.createRadioSubMenu('Top menu', [' 8px', '10px', '11px', RES._QHD ? '12px' : '12px (default)', '13px', RES._QHD ? '14px (default)' : '14px', '16px'], menuFontSize, [8, 10, 11, 12, 13, 14, 16], (size) => { + if (grSet.layout === 'default') { + grSet.menuFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.menuFontSize_artwork = size; + } + else if (grSet.layout === 'compact') { + grSet.menuFontSize_compact = size; + } + grm.ui.createFonts(); + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); RepaintWindow(); - }, !pref.displayDiscArt); - discArtMenu.addToggleItem('Filter cd/disc/vinyl .jpgs from artwork', pref, 'filterDiscJpgsFromAlbumArt'); - discArtMenu.addSeparator(); - discArtMenu.addToggleItem('Spin disc art while songs play (increases memory and CPU)', pref, 'spinDiscArt', () => { - if (pref.spinDiscArt) { - setDiscArtRotationTimer(); - } else { - clearInterval(discArtRotationTimer); - discArtArray = []; - discArtArrayCover = []; + }); + + // * MAIN - LOWER BAR * // + mainFontSizeMenu.createRadioSubMenu('Lower bar', grSet.layout !== 'default' ? ['10px', '12px', '14px', RES._QHD ? '16px' : '16px (default)', RES._QHD ? '18px (default)' : '18px', '20px', '22px', '24px', '26px'] : + ['10px', '12px', '14px', '16px', RES._QHD ? '18px' : '18px (default)', RES._QHD ? '20px (default)' : '20px', '22px', '24px', '26px'], lowerBarFontSize, [10, 12, 14, 16, 18, 20, 22, 24, 26], (size) => { + if (grSet.layout === 'default') { + grSet.lowerBarFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.lowerBarFontSize_artwork = size; } + else if (grSet.layout === 'compact') { + grSet.lowerBarFontSize_compact = size; + } + grm.ui.createFonts(); + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }); + mainFontSizeMenu.appendTo(changeFontSizeMenu); + + // * MAIN - NOTIFICATION * // + mainFontSizeMenu.createRadioSubMenu('Notification', ['12px', '14px', '16px', RES._QHD ? '18px' : '18px (default)', RES._QHD ? '20px (default)' : '20px', '22px', '24px'], notificationFontSize, + [12, 14, 16, 18, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { grSet.notificationFontSize_default = size; } + else if (grSet.layout === 'artwork') { grSet.notificationFontSize_artwork = size; } + else if (grSet.layout === 'compact') { grSet.notificationFontSize_compact = size; } + + grm.ui.createFonts(); + RepaintWindow(); + }); + + // * MAIN - POPUP * // + mainFontSizeMenu.createRadioSubMenu('Popup', ['12px', '14px', RES._QHD ? '16px' : '16px (default)', RES._QHD ? '18px (default)' : '18px', '20px', '22px', '24px'], popupFontSize, + [12, 14, 16, 18, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { grSet.popupFontSize_default = size; } + else if (grSet.layout === 'artwork') { grSet.popupFontSize_artwork = size; } + else if (grSet.layout === 'compact') { grSet.popupFontSize_compact = size; } + + grm.ui.createFonts(); + if (grm.ui.displayCustomThemeMenu) grm.cthMenu.initCustomThemeMenu('pl_bg'); + else if (grm.ui.displayMetadataGridMenu) grm.gridMenu.initMetadataGridMenu(); + RepaintWindow(); }); - discArtMenu.createRadioSubMenu('# Rotation images (memory usage/rotational speed)', [' 36 (10 degrees)', ' 45 (8 degrees)', ' 60 (6 degrees)', ' 72 (5 degrees) (default)', ' 90 (4 degrees)', '120 (3 degrees)', '180 (2 degrees)'], pref.spinDiscArtImageCount, [36, 45, 60, 72, 90, 120, 180], (count) => { - pref.spinDiscArtImageCount = count; - discArtRotationIndex = 0; - discArtRotationIndexCover = 0; - discArtArray = []; - discArtArrayCover = []; + + // * MAIN - TOOLTIP * // + mainFontSizeMenu.createRadioSubMenu('Tooltip', ['12px', '14px', RES._QHD ? '16px' : '16px (default)', RES._QHD ? '18px (default)' : '18px', '20px', '22px', '24px'], tooltipFontSize, + [12, 14, 16, 18, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { grSet.tooltipFontSize_default = size; } + else if (grSet.layout === 'artwork') { grSet.tooltipFontSize_artwork = size; } + else if (grSet.layout === 'compact') { grSet.tooltipFontSize_compact = size; } + + grm.ui.createFonts(); RepaintWindow(); - }, !pref.spinDiscArt); - discArtMenu.createRadioSubMenu('Spinning disc art redraw speed', ['250ms (very slow CPU)', '200ms', '150ms', '125ms', '100ms', ' 75ms (default)', ' 50ms', ' 40ms', ' 30ms', ' 20ms', ' 10ms (very fast CPU)'], pref.spinDiscArtRedrawInterval, [250, 200, 150, 125, 100, 75, 50, 40, 30, 20, 10], interval => { - pref.spinDiscArtRedrawInterval = interval; - setDiscArtRotationTimer(); - }, !pref.spinDiscArt); - discArtMenu.addSeparator(); - discArtMenu.addToggleItem('Rotate disc art as tracks change', pref, 'rotateDiscArt', () => { RepaintWindow(); }, !pref.displayDiscArt || pref.spinDiscArt); - discArtMenu.createRadioSubMenu('Disc art rotation amount', ['2 degrees', '3 degrees', '4 degrees', '5 degrees'], parseInt(pref.rotationAmt), [2, 3, 4, 5], (rot) => { - pref.rotationAmt = rot; - createDiscArtRotation(); + }); + + // * DETAILS - ARTIST * // + const detailsFontSizeMenu = new Menu('Details'); + detailsFontSizeMenu.createRadioSubMenu('Artist', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', RES._QHD ? '18px' : '18px (default)', '19px', RES._QHD ? '20px (default)' : '20px', '22px', '24px'], gridArtistFontSize, + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { + grSet.gridArtistFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.gridArtistFontSize_artwork = size; + } + grm.ui.createFonts(); RepaintWindow(); - }, !pref.rotateDiscArt || pref.spinDiscArt); - discArtMenu.appendTo(detailsMenu); + }); - discArtMenu.createRadioSubMenu('Disc art display amount', ['Auto (Needs enough width)', '50% (Needs enough width, default)', '45%', '40%', '35%', '30%', '25%', '20%', '15%', '10%'], pref.discArtDisplayAmount, [1, 0.5, 0.455, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1], amount => { - pref.discArtDisplayAmount = amount; - resizeArtwork(true); + // * DETAILS - TITLE * // + detailsFontSizeMenu.createRadioSubMenu('Title', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', RES._QHD ? '18px' : '18px (default)', '19px', RES._QHD ? '20px (default)' : '20px', '22px', '24px'], gridTrackNumFontSize && gridTitleFontSize, + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { + grSet.gridTrackNumFontSize_default = size; + grSet.gridTitleFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.gridTrackNumFontSize_artwork = size; + grSet.gridTitleFontSize_artwork = size; + } + grm.ui.createFonts(); RepaintWindow(); }); - // * DISC ART ALBUM ART * // - const albumArtOpacityMenu = new Menu('Album art'); - albumArtOpacityMenu.createRadioSubMenu('Full artwork opacity (fast CPU needed when disc art spinning)', ['100%', '90%', '80%', '70%', '60%', '50%', '40%', '30%', '20%', '10%'], pref.detailsAlbumArtOpacity, [255, 230, 204, 178, 153, 128, 102, 76, 51, 25], value => { - pref.detailsAlbumArtOpacity = value; - pref.detailsAlbumArtDiscAreaOpacity = 255; - pref.discArtOnTop = false; + // * DETAILS - ALBUM * // + detailsFontSizeMenu.createRadioSubMenu('Album', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', RES._QHD ? '18px' : '18px (default)', '19px', RES._QHD ? '20px (default)' : '20px', '22px', '24px'], gridAlbumFontSize, + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { + grSet.gridAlbumFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.gridAlbumFontSize_artwork = size; + } + grm.ui.createFonts(); RepaintWindow(); }); - albumArtOpacityMenu.createRadioSubMenu('Disc area opacity (very fast CPU needed when disc art spinning)', ['100%', '90%', '80%', '70%', '60%', '50%', '40%', '30%', '20%', '10%'], pref.detailsAlbumArtDiscAreaOpacity, [255, 230, 204, 178, 153, 128, 102, 76, 51, 25], value => { - pref.detailsAlbumArtDiscAreaOpacity = value; - pref.detailsAlbumArtOpacity = 255; - pref.discArtOnTop = false; + + // * DETAILS - TAG NAME * // + detailsFontSizeMenu.createRadioSubMenu('Tag name', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', RES._QHD ? '17px' : '17px (default)', '18px', RES._QHD ? '19px (default)' : '19px', '20px', '22px', '24px'], gridKeyFontSize, + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { + grSet.gridKeyFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.gridKeyFontSize_artwork = size; + } + grm.ui.createFonts(); RepaintWindow(); }); - albumArtOpacityMenu.appendTo(detailsMenu); - } - // * METADATA GRID MENU * // - const detailsMetadataGridMenu = new Menu('Metadata grid'); - const detailsShowArtistMenu = new Menu('Show artist'); - detailsShowArtistMenu.addToggleItem('Default', pref, 'showGridArtist_default', () => { - createFonts(); - RepaintWindow(); - }); - detailsShowArtistMenu.addToggleItem('Artwork', pref, 'showGridArtist_artwork', () => { - createFonts(); - RepaintWindow(); - }); - detailsShowArtistMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW TRACK NUMBER IN DETAILS * // - const detailsShowTrackNumberMenu = new Menu('Show track number'); - detailsShowTrackNumberMenu.addToggleItem('Default', pref, 'showGridTrackNum_default', () => { - createFonts(); - RepaintWindow(); - }); - detailsShowTrackNumberMenu.addToggleItem('Artwork', pref, 'showGridTrackNum_artwork', () => { - createFonts(); - RepaintWindow(); - }); - detailsShowTrackNumberMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW SONG TITLE IN DETAILS * // - const detailsShowTitleMenu = new Menu('Show song title'); - detailsShowTitleMenu.addToggleItem('Default', pref, 'showGridTitle_default', () => { - pref.showGridTrackNum_default = true; - createFonts(); - RepaintWindow(); - }); - detailsShowTitleMenu.addToggleItem('Artwork', pref, 'showGridTitle_artwork', () => { - pref.showGridTrackNum_artwork = true; - createFonts(); - RepaintWindow(); - }); - detailsShowTitleMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW PLAYING PLAYLIST IN DETAILS * // - const detailsShowPlaylingPlaylistMenu = new Menu('Show playing playlist'); - detailsShowPlaylingPlaylistMenu.addToggleItem('Enable', pref, 'showGridPlayingPlaylist', () => { - on_playback_new_track(fb.GetNowPlaying()); - createFonts(); - RepaintWindow(); - }); - detailsShowPlaylingPlaylistMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW TIMELINE IN DETAILS * // - const detailsShowTimelineMenu = new Menu('Show timeline'); - detailsShowTimelineMenu.addToggleItem('Default', pref, 'showGridTimeline_default', () => { - RepaintWindow(); - }); - detailsShowTimelineMenu.addToggleItem('Artwork', pref, 'showGridTimeline_artwork', () => { - RepaintWindow(); - }); - detailsShowTimelineMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW ARTIST COUNTRY FLAG IN DETAILS * // - const detailsShowArtistFlagsMenu = new Menu('Show artist country flags'); - detailsShowArtistFlagsMenu.addToggleItem('Default', pref, 'showGridArtistFlags_default', () => { - loadCountryFlags(); - RepaintWindow(); - }); - detailsShowArtistFlagsMenu.addToggleItem('Artwork', pref, 'showGridArtistFlags_artwork', () => { - loadCountryFlags(); - RepaintWindow(); - }); - detailsShowArtistFlagsMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW RELEASE COUNTRY FLAG IN DETAILS * // - const detailsShowReleaseFlagsMenu = new Menu('Show release country flags'); - detailsShowReleaseFlagsMenu.createRadioSubMenu('Default', ['Disabled', 'Logo', 'Text + Logo'], pref.showGridReleaseFlags_default, [false, 'logo', 'textlogo'], type => { - pref.showGridReleaseFlags_default = type; - loadReleaseCountryFlag(); - RepaintWindow(); - }); - detailsShowReleaseFlagsMenu.createRadioSubMenu('Artwork', ['Disabled', 'Logo', 'Text + Logo'], pref.showGridReleaseFlags_artwork, [false, 'logo', 'textlogo'], type => { - pref.showGridReleaseFlags_artwork = type; - loadReleaseCountryFlag(); - RepaintWindow(); - }); - detailsShowReleaseFlagsMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW CODEC LOGO IN DETAILS * // - const detailsShowCodecLogoMenu = new Menu('Show codec logo'); - detailsShowCodecLogoMenu.createRadioSubMenu('Default', ['Disabled', 'Logo', 'Text + Logo'], pref.showGridCodecLogo_default, [false, 'logo', 'textlogo'], type => { - pref.showGridCodecLogo_default = type; - RepaintWindow(); - }); - detailsShowCodecLogoMenu.createRadioSubMenu('Artwork', ['Disabled', 'Logo', 'Text + Logo'], pref.showGridCodecLogo_artwork, [false, 'logo', 'textlogo'], type => { - pref.showGridCodecLogo_artwork = type; - RepaintWindow(); - }); - detailsShowCodecLogoMenu.appendTo(detailsMetadataGridMenu); - - // * SHOW CHANNEL LOGO IN DETAILS * // - const detailsShowChannelLogoMenu = new Menu('Show channel logo'); - detailsShowChannelLogoMenu.createRadioSubMenu('Default', ['Disabled', 'Logo', 'Text + Logo'], pref.showGridChannelLogo_default, [false, 'logo', 'textlogo'], type => { - pref.showGridChannelLogo_default = type; - RepaintWindow(); - }); - detailsShowChannelLogoMenu.createRadioSubMenu('Artwork', ['Disabled', 'Logo', 'Text + Logo'], pref.showGridChannelLogo_artwork, [false, 'logo', 'textlogo'], type => { - pref.showGridChannelLogo_artwork = type; - RepaintWindow(); - }); - detailsShowChannelLogoMenu.appendTo(detailsMetadataGridMenu); - - detailsMetadataGridMenu.addSeparator(); - detailsMetadataGridMenu.addToggleItem('Auto-hide full metadata on small player', pref, 'autoHideGridMetadata', () => RepaintWindow()); - - // * EDIT METADATA GRID IN DETAILS * // - if (fb.IsPlaying) { - detailsMetadataGridMenu.addSeparator(); - detailsMetadataGridMenu.addItem('Edit metadata grid', false, () => { - if (pref.layout === 'default') { - displayMetadataGridMenu = !displayMetadataGridMenu; - if (!displayDetails) { - displayDetails = true; - displayPlaylist = false; - displayLibrary = false; - displayBiography = false; - pref.displayLyrics = false; - resizeArtwork(true); - initButtonState(); - } - initMetadataGridMenu(1); - RepaintWindow(); - } else { - const configPath = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc`; - fb.ShowPopupMessage(`Metadata grid can only be live edited in default layout:\nOptions > Layout > Default\n\nYou could manually edit your config file while reloading to take effect:\n${configPath}\n`, 'Metadata grid live editing'); + // * DETAILS - TAG VALUE * // + detailsFontSizeMenu.createRadioSubMenu('Tag value', ['10px', '11px', '12px', '13px', '14px', '15px', '16px', RES._QHD ? '17px' : '17px (default)', '18px', RES._QHD ? '19px (default)' : '19px', '20px', '22px', '24px'], gridValueFontSize, + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24], (size) => { + if (grSet.layout === 'default') { + grSet.gridValueFontSize_default = size; + } + else if (grSet.layout === 'artwork') { + grSet.gridValueFontSize_artwork = size; } + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsFontSizeMenu.appendTo(changeFontSizeMenu); + + // * PLAYLIST * // + changeFontSizeMenu.createRadioSubMenu('Playlist', grSet.layout === 'default' ? + RES._QHD ? ['-1', '10px', '12px', '13px', '14px', '15px', '16px', '17px (default)', '18px', '20px', '22px', '+1'] : ['-1', '10px', '12px', '13px', '14px', '15px (default)', '16px', '18px', '20px', '22px', '+1'] : + RES._QHD ? ['10px', '12px', '13px', '14px', '15px', '16px', '17px (default)', '18px'] : ['10px', '12px', '13px', '14px', '15px (default)', '16px', '18px'], playlistHeaderFontSize, grSet.layout === 'default' ? + RES._QHD ? [-1, 10, 12, 13, 14, 15, 16, 17, 18, 20, 22, 999] : [-1, 10, 12, 13, 14, 15, 16, 18, 20, 22, 999] : + RES._QHD ? [10, 12, 13, 14, 15, 16, 17, 18] : [10, 12, 13, 14, 15, 16, 18], (size) => { + if (size === -1) { + if (grSet.layout === 'default') { grSet.playlistHeaderFontSize_default--; grSet.playlistFontSize_default--; } + else if (grSet.layout === 'artwork') { grSet.playlistHeaderFontSize_artwork--; grSet.playlistFontSize_artwork--; } + else if (grSet.layout === 'compact') { grSet.playlistHeaderFontSize_compact--; grSet.playlistFontSize_compact--; } + } + else if (size === 999) { + if (grSet.layout === 'default') { grSet.playlistHeaderFontSize_default++; grSet.playlistFontSize_default++; } + else if (grSet.layout === 'artwork') { grSet.playlistHeaderFontSize_artwork++; grSet.playlistFontSize_artwork++; } + else if (grSet.layout === 'compact') { grSet.playlistHeaderFontSize_compact++; grSet.playlistFontSize_compact++; } + } + else if (grSet.layout === 'default') { grSet.playlistHeaderFontSize_default = size; grSet.playlistFontSize_default = size - (size === 15 || size === 17 ? 3 : 2); } + else if (grSet.layout === 'artwork') { grSet.playlistHeaderFontSize_artwork = size; grSet.playlistFontSize_artwork = size - (size === 15 || size === 17 ? 3 : 2); } + else if (grSet.layout === 'compact') { grSet.playlistHeaderFontSize_compact = size; grSet.playlistFontSize_compact = size - (size === 15 || size === 17 ? 3 : 2); } + + // * Update Playlist history buttons + grm.ui.createFonts(); + grm.ui.initMetrics(); + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + // * Update Playlist + PlaylistRescale(true); + PlaylistHeader.img_cache.clear(); + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + if (grSet.libraryLayout === 'split') { + // * Update Library + lib.pop.createImages(); + lib.panel.zoomReset(); + grm.ui.initLibraryLayout(); + } + RepaintWindow(); }); - } - detailsMetadataGridMenu.appendTo(detailsMenu); - if (pref.layout === 'default') { - const detailsBackgroundMenu = new Menu('Background'); - // * SHOW FULL BACKGROUND WHEN NO DISC ART IN DETAILS * // - detailsBackgroundMenu.addToggleItem('Show full background when no disc art', pref, 'noDiscArtBg', () => { - if (pref.labelArtOnBg) { - pref.labelArtOnBg = false; + // * LIBRARY * // + changeFontSizeMenu.createRadioSubMenu('Library', ['-1', ' 8px', '10px', '11px', RES._QHD ? '12px' : '12px (default)', '13px', RES._QHD ? '14px (default)' : '14px', '16px', '18px', '+1'], libraryFontSize, + [-1, RES._4K ? 8 * 1.5 : 8, RES._4K ? 10 * 1.5 : 10, RES._4K ? 11 * 1.5 : 11, RES._4K ? 12 * 1.5 : 12, RES._4K ? 13 * 1.5 : 13, RES._4K ? 14 * 1.5 : 14, RES._4K ? 16 * 1.5 : 16, RES._4K ? 18 * 1.5 : 18, 999], (size) => { + if (size === -1) { + if (grSet.layout === 'default') { libSet.baseFontSize_default--; } + else if (grSet.layout === 'artwork') { libSet.baseFontSize_artwork--; } + } + else if (size === 999) { + if (grSet.layout === 'default') { libSet.baseFontSize_default++; } + else if (grSet.layout === 'artwork') { libSet.baseFontSize_artwork++; } } + else if (grSet.layout === 'default') { libSet.baseFontSize_default = size; } + else if (grSet.layout === 'artwork') { libSet.baseFontSize_artwork = size; } + + grSet.libraryFontSize_default = libSet.baseFontSize_default; + grSet.libraryFontSize_artwork = libSet.baseFontSize_artwork; + + grm.ui.setLibrarySize(); + lib.panel.zoomReset(); + lib.pop.createImages(); RepaintWindow(); }); - // * SHOW LABEL ART ON BACKGROUND IN DETAILS * // - detailsBackgroundMenu.addToggleItem('Show label art on background', pref, 'labelArtOnBg', () => RepaintWindow(), pref.noDiscArtBg); - detailsBackgroundMenu.appendTo(detailsMenu); - } - if (!context_menu) detailsMenu.appendTo(menu); -} + // * BIOGRAPHY * // + changeFontSizeMenu.createRadioSubMenu('Biography', ['-1', ' 8px', '10px', '11px', RES._QHD ? '12px' : '12px (default)', '13px', RES._QHD ? '14px (default)' : '14px', '16px', '18px', '+1'], biographyFontSize, + [-1, RES._4K ? 8 * 1.5 : 8, RES._4K ? 10 * 2 : 10, RES._4K ? 11 * 2 : 11, RES._4K ? 12 * 2 : 12, RES._4K ? 13 * 2 : 13, RES._4K ? 14 * 2 : 14, RES._4K ? 16 * 2 : 16, RES._4K ? 18 * 2 : 18, 999], (size) => { + if (size === -1) { + if (grSet.layout === 'default') { bioSet.baseFontSizeBio_default--; } + else if (grSet.layout === 'artwork') { bioSet.baseFontSizeBio_artwork--; } + } + else if (size === 999) { + if (grSet.layout === 'default') { bioSet.baseFontSizeBio_default++; } + else if (grSet.layout === 'artwork') { bioSet.baseFontSizeBio_artwork++; } + } + else if (grSet.layout === 'default') { bioSet.baseFontSizeBio_default = size; } + else if (grSet.layout === 'artwork') { bioSet.baseFontSizeBio_artwork = size; } + grSet.biographyFontSize_default = bioSet.baseFontSizeBio_default; + grSet.biographyFontSize_artwork = bioSet.baseFontSizeBio_artwork; -///////////////////////// -// * LIBRARY OPTIONS * // -///////////////////////// -/** - * Top menu > Options > Library. - * @param {Menu} menu Creates the Library panel menu via a new Menu instance. - * @param {boolean} context_menu Appends Library options to context menu. - */ -function libraryOptions(menu, context_menu) { - const libraryMenu = context_menu ? menu : new Menu('Library'); - - // * LAYOUT * // - if (pref.layout === 'default') { - const libraryLayoutMenu = new Menu('Layout'); - libraryLayoutMenu.addRadioItems(['Normal', 'Full', 'Split'], pref.libraryLayout, ['normal', 'full', 'split'], (width) => { - pref.libraryLayout = width; - if (!displayLibrary) displayLibrary = true; displayPlaylist = false; displayBiography = false; - if (pref.displayLyrics) pref.displayLyrics = false; - displayPlaylist = pref.libraryLayout === 'split'; - resizeArtwork(true); - initLibraryLayout(); - initButtonState(); + grm.ui.setBiographySize(); + bio.but.resetZoom(); + bio.but.createImages(); RepaintWindow(); }); - libraryLayoutMenu.addSeparator(); - libraryLayoutMenu.addToggleItem('Use full preset', pref, 'libraryLayoutFullPreset', () => { RepaintWindow(); }); - libraryLayoutMenu.addSeparator(); - libraryLayoutMenu.addToggleItem('Use split preset (collapse)', pref, 'libraryLayoutSplitPreset', () => { - pref.libraryLayoutSplitPreset2 = false; - pref.libraryLayoutSplitPreset3 = false; - pref.libraryLayoutSplitPreset4 = false; - initLibraryLayout(); - initPlaylist(); - playlist.on_size(ww, wh); - }); - libraryLayoutMenu.addToggleItem('Use split preset (text)', pref, 'libraryLayoutSplitPreset2', () => { - pref.libraryLayoutSplitPreset = false; - pref.libraryLayoutSplitPreset3 = false; - pref.libraryLayoutSplitPreset4 = false; - initLibraryLayout(); - initPlaylist(); - playlist.on_size(ww, wh); - }); - libraryLayoutMenu.addToggleItem('Use split preset (art grid)', pref, 'libraryLayoutSplitPreset3', () => { - pref.libraryLayoutSplitPreset = false; - pref.libraryLayoutSplitPreset2 = false; - pref.libraryLayoutSplitPreset4 = false; - initLibraryLayout(); - initPlaylist(); - playlist.on_size(ww, wh); - }); - libraryLayoutMenu.addToggleItem('Use split preset (art header)', pref, 'libraryLayoutSplitPreset4', () => { - pref.libraryLayoutSplitPreset = false; - pref.libraryLayoutSplitPreset2 = false; - pref.libraryLayoutSplitPreset3 = false; - initLibraryLayout(); - initPlaylist(); - playlist.on_size(ww, wh); - }); - libraryLayoutMenu.appendTo(libraryMenu); - } - // * DESIGN * // - libraryMenu.createRadioSubMenu('Design', ['Georgia-ReBORN', 'Traditional', 'Modern', 'Ultra-modern', 'Clean', 'List view', 'Covers (labels right)', 'Covers (labels bottom)', 'Covers (labels blend)', 'Flow mode'], pref.libraryDesign, - ['reborn', 'traditional', 'modern', 'ultraModern', 'clean', 'facet', 'coversLabelsRight', 'coversLabelsBottom', 'coversLabelsBlend', 'flowMode'], (design) => { - pref.libraryDesign = design; - setLibraryDesign(); - }); - - // * THEME * // - libraryMenu.createRadioSubMenu('Theme', ['Georgia-ReBORN', 'Dark', 'Blend', 'Light', 'Random', 'Cover'], pref.libraryTheme, [0, 1, 2, 3, 4, 5], (theme) => { - ppt.theme = pref.libraryTheme = theme; - initTheme(); - library.on_colours_changed(); - panel.updateProp(1); - themeColorAdjustments(); - }); - - // * ALBUM ART * // - const libraryAlbumArtMenu = new Menu('Album art'); - const libraryThumbnailSizeMenu = new Menu('Thumbnail size'); - libraryThumbnailSizeMenu.addRadioItems(['Auto (default)', 'Playlist', 'Mini', 'Small', 'Regular', 'Medium', 'Large', 'XL', 'XXL', 'MAX'], pref.libraryThumbnailSize, ['auto', 'playlist', 0, 1, 2, 3, 4, 5, 6, 7], (thumbnailSize) => { - pref.libraryThumbnailSizeSaved = ppt.thumbNailSize = pref.libraryThumbnailSize = thumbnailSize; - setLibrarySize(); - RepaintWindow(); - }); - libraryThumbnailSizeMenu.appendTo(libraryAlbumArtMenu); - - const libraryThumbnailBorderMenu = new Menu('Thumbnail border'); - libraryThumbnailBorderMenu.addRadioItems(['None', 'Border', 'Shadow'], pref.libraryThumbnailBorder, ['none', 'border', 'shadow'], (type) => { - pref.libraryThumbnailBorder = type; - ppt.albumArtDropShadow = pref.libraryThumbnailBorder === 'shadow'; - panel.updateProp(1); - }); - libraryThumbnailBorderMenu.appendTo(libraryAlbumArtMenu); - - if (!ppt.albumArtShow) { - libraryAlbumArtMenu.addToggleItem('Activate option or change design to album art', ppt, 'albumArtShow', () => { - if (pref.libraryDesign === 'flowMode') pref.libraryLayout = 'full'; - lib.logTree(); - pop.clearTree(); - ppt.toggle('albumArtShow'); - panel.imgView = ppt.albumArtShow = true; - men.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]); - setLibrarySize(); - displayPlaylist = false; - displayLibrary = true; - btns.library.enabled = true; - btns.library.changeState(ButtonState.Down); - panel.updateProp(1); - }, ppt.albumArtShow); - libraryAlbumArtMenu.addSeparator(); + // * LYRICS * // + changeFontSizeMenu.createRadioSubMenu('Lyrics', ['-1', '10px', '12px', '14px', '16px', '18px', RES._QHD ? '20px' : '20px (default)', RES._QHD ? '22px (default)' : '22px', '24px', '26px', '28px', '30px', '+1'], lyricsFontSize, + [-1, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 999], (size) => { + if (size === -1) { + if (grSet.layout === 'default') { grSet.lyricsFontSize_default--; } + else if (grSet.layout === 'artwork') { grSet.lyricsFontSize_artwork--; } + } + else if (size === 999) { + if (grSet.layout === 'default') { grSet.lyricsFontSize_default++; } + else if (grSet.layout === 'artwork') { grSet.lyricsFontSize_artwork++; } + } + else if (grSet.layout === 'default') { grSet.lyricsFontSize_default = size; } + else if (grSet.layout === 'artwork') { grSet.lyricsFontSize_artwork = size; } + + if (grSet.layout === 'default') { grSet.lyricsFontSize_default = Math.max(6, grSet.lyricsFontSize_default); } + else if (grSet.layout === 'artwork') { grSet.lyricsFontSize_artwork = Math.max(6, grSet.lyricsFontSize_artwork); } + + grm.ui.createFonts(); + if (grm.ui.displayLyrics) grm.lyrics.initLyrics(); + }); + + changeFontSizeMenu.appendTo(menu); } - const libraryAlbumArtOverlay = new Menu('Overlay'); - libraryAlbumArtOverlay.addRadioItems(['None', 'Track count', 'Year'], ppt.itemOverlayType, [0, 1, 2], (type) => { - ppt.itemOverlayType = type; - panel.updateProp(1); - }); - libraryAlbumArtOverlay.appendTo(libraryAlbumArtMenu); - - const libraryAlbumArtIndex = new Menu('Index'); - libraryAlbumArtIndex.addToggleItem('Show on scrollbar drag', ppt, 'albumArtLetter', () => { panel.updateProp(1); }); - libraryAlbumArtIndex.addSeparator(); - libraryAlbumArtIndex.addRadioItems(['Artist', 'Alphabet'], ppt.albumArtLetterNo, [0, 1], (type) => { - ppt.albumArtLetterNo = type; - panel.updateProp(1); - }); - libraryAlbumArtIndex.appendTo(libraryAlbumArtMenu); - libraryAlbumArtMenu.appendTo(libraryMenu); - - const libraryViewMenu = new Menu('View'); - libraryViewMenu.addRadioItems(['Front (default)', 'Back', 'Disc', 'Icon', 'Artist'], ppt.artId, [0, 1, 2, 3, 4], (view) => { - ppt.artId = view; - men.setAlbumart(view); - panel.updateProp(1); - }); - libraryViewMenu.addSeparator(); - libraryViewMenu.addRadioItems(['Group: auto', 'Group: top level', 'Group: two levels'], ppt.albumArtGrpLevel, [0, 1, 2], (view) => { - ppt.albumArtGrpLevel = view; - men.setAlbumart(view - 5); - panel.updateProp(1); - }); - libraryViewMenu.appendTo(libraryAlbumArtMenu); - - const libraryImageMenu = new Menu('Image'); - libraryImageMenu.createRadioSubMenu('Front', ['Regular', 'Auto-fill (default)', 'Circular'], ppt.imgStyleFront, [0, 1, 2], (style) => { - ppt.imgStyleFront = style; - panel.updateProp(1); - }); - libraryImageMenu.createRadioSubMenu('Back', ['Regular', 'Auto-fill (default)', 'Circular'], ppt.imgStyleBack, [0, 1, 2], (style) => { - ppt.imgStyleBack = style; - panel.updateProp(1); - }); - libraryImageMenu.createRadioSubMenu('Disc', ['Regular', 'Auto-fill (default)', 'Circular'], ppt.imgStyleDisc, [0, 1, 2], (style) => { - ppt.imgStyleDisc = style; - panel.updateProp(1); - }); - libraryImageMenu.createRadioSubMenu('Icon', ['Regular', 'Auto-fill (default)', 'Circular'], ppt.imgStyleIcon, [0, 1, 2], (style) => { - ppt.imgStyleIcon = style; - panel.updateProp(1); - }); - libraryImageMenu.createRadioSubMenu('Artist', ['Regular', 'Auto-fill (default)', 'Circular'], ppt.imgStyleArtist, [0, 1, 2], (style) => { - ppt.imgStyleArtist = style; - panel.updateProp(1); - }); - libraryImageMenu.appendTo(libraryAlbumArtMenu); - - const libraryLabelsMenu = new Menu('Labels'); - libraryLabelsMenu.addRadioItems(['Bottom (default)', 'Right', 'Blend', 'Dark', 'None'], ppt.albumArtLabelType, [1, 2, 3, 4, 0], (style) => { - pref.savedAlbumArtLabelType = ppt.albumArtLabelType = style; - panel.updateProp(1); - }); - libraryLabelsMenu.addSeparator(); - libraryLabelsMenu.addToggleItem('Flip', ppt, 'albumArtFlipLabels', () => { panel.updateProp(1); }); - libraryLabelsMenu.appendTo(libraryAlbumArtMenu); - - // * CONTROLS * // - const libraryControlsMenu = new Menu('Controls'); - libraryControlsMenu.createRadioSubMenu('Action mode', ['Default', 'Browser', 'Player'], ppt.actionMode, [0, 1, 2], (mode) => { - ppt.actionMode = mode; - if (mode === 1) { - const msg = 'Do you want to enable library browser mode?\n\nThis will act like a file browser to quickly see the content of the album. It is not recommended for new users\nwho don\'t know how the library works.\n\nContinue?\n\n\n'; - const msgFb = 'Library browser mode enabled:\n\nThis will act like a file browser to quickly see the content of the album.\nIt is not recommended for new users who don\'t know how the library works.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) { - ppt.actionMode = 0; - return; - } - pref.libraryLayoutSplitPreset = false; - pref.libraryLayoutSplitPreset2 = false; - pref.libraryLayoutSplitPreset3 = false; - pref.libraryLayoutSplitPreset4 = false; - pref.libraryLayout = 'split'; - panel.imgView = ppt.albumArtShow = true; - lib.logTree(); - pop.clearTree(); - men.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]); - setLibrarySize(); - initLibraryColors(); - themeColorAdjustments(); - g_properties.show_header = true; - g_properties.auto_collapse = false; - displayPlaylist = true; - displayLibrary = true; - }); - updatePlaylist(); - RepaintWindow(); - } - else if (mode === 2) { - const msg = 'Do you want to enable library player mode?\n\nThis will act like a playlist and will not automatically add content to the playlist. It is recommended for new users\nwho don\'t know how the library works.\n\nContinue?\n\n\n'; - const msgFb = 'Library player mode enabled:\n\nThis will act like a like a playlist and will not automatically add content to the playlist.\nIt is recommended for new users who don\'t know how the library works.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) { - ppt.actionMode = 0; - } - }); + /** + * Top menu > Options > Player controls. + * @param {Menu} menu - Creates the Player controls menu via a new Menu instance. + * @protected + */ + playerControlsOptions(menu) { + const playerControlsMenu = new Menu('Player controls'); + + const playlistCallback = () => { + pl.call.on_size(grm.ui.ww, grm.ui.wh); RepaintWindow(); }; - }); - libraryControlsMenu.addSeparator(); - libraryControlsMenu.createRadioSubMenu('Single-click action', ['Select', 'Send to playlist', 'Send to playlist and play', 'Send to playlist and play (add if playing)'], ppt.clickAction, [0, 1, 2, 3], (action) => { - ppt.clickAction = action; - panel.updateProp(1); - }); - libraryControlsMenu.createRadioSubMenu('Double-click action', ['Send to playlist', 'Send to playlist and play', 'Expand/Collapse tree', 'Play only'], ppt.dblClickAction, [0, 1, 2, 3], (action) => { - ppt.dblClickAction = action; - panel.updateProp(1); - }); - libraryControlsMenu.createRadioSubMenu('Middle-click action', ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (alt + double-click removes)'], ppt.mbtnClickAction, [0, 1, 2], (action) => { - ppt.mbtnClickAction = action; - panel.updateProp(1); - }); - libraryControlsMenu.createRadioSubMenu('Alt + mouse click action', ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (alt + double-click removes)'], ppt.altClickAction, [0, 1, 2], (action) => { - ppt.altClickAction = action; - panel.updateProp(1); - }); - const libraryKeystrokeMenu = new Menu('Keystroke action'); - libraryKeystrokeMenu.addToggleItem('Play on Enter or send from menu', ppt, 'autoPlay', () => { panel.updateProp(1); }); - libraryKeystrokeMenu.addSeparator(); - libraryKeystrokeMenu.addRadioItems(['Select', 'Send to Playlist'], ppt.keyAction, [0, 1], (action) => { - ppt.keyAction = action; - panel.updateProp(1); - }); - libraryKeystrokeMenu.appendTo(libraryControlsMenu); - libraryControlsMenu.addSeparator(); - libraryControlsMenu.addToggleItem('Always remember library state', ppt, 'rememberTree', () => { panel.updateProp(1); }); - libraryControlsMenu.addToggleItem('Always load View by same as tree', ppt, 'artTreeSameView', () => { panel.updateProp(1); }); - libraryControlsMenu.addToggleItem('Always load preset with current view pattern', ppt, 'presetLoadCurView', () => { panel.updateProp(1); }); - libraryControlsMenu.addSeparator(); - libraryControlsMenu.addToggleItem('Switch to playlist when adding songs', pref, 'libraryPlaylistSwitch'); - libraryControlsMenu.addSeparator(); - libraryControlsMenu.addItem('Reset library zoom', false, () => { panel.zoomReset(); }); - libraryControlsMenu.appendTo(libraryMenu); - - // * TRACK ROW * // - const libraryTrackRowMenu = new Menu('Track row'); - libraryTrackRowMenu.createRadioSubMenu('Node root type', ['Hide', 'All Music', 'View name', 'Summary item'], ppt.rootNode, [0, 1, 2, 3], (nodeIndex) => { - ppt.rootNode = nodeIndex; - panel.updateProp(1); - }); - libraryTrackRowMenu.createRadioSubMenu('Node item counts', ['Hidden', '# Tracks', '# Sub-Items'], ppt.nodeCounts, [0, 1, 2], (nodeIndex) => { - ppt.nodeCounts = nodeIndex; - panel.updateProp(1); - }); - libraryTrackRowMenu.createRadioSubMenu('Node item counts position', ['Right', 'Left'], ppt.countsRight, [true, false], (nodeCounts) => { - ppt.countsRight = nodeCounts; - panel.updateProp(1); - }); - libraryTrackRowMenu.createRadioSubMenu('Node auto collapse', ['On', 'Off'], ppt.autoCollapse, [true, false], (nodeCollapse) => { - ppt.autoCollapse = nodeCollapse; - panel.updateProp(1); - }); - libraryTrackRowMenu.addSeparator(); - libraryTrackRowMenu.createRadioSubMenu('Statistics', ['Tracks', 'Bitrate', 'Duration', 'Total size', 'Rating', 'Popularity', 'Date', 'Playback queue', 'Playcount', 'First played', 'Last played', 'Added'], ppt.itemShowStatistics, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], (stats) => { - ppt.itemShowStatistics = stats; - panel.updateProp(1); - }); - libraryTrackRowMenu.addSeparator(); - libraryTrackRowMenu.addToggleItem('Show now playing', ppt, 'highLightNowplaying', () => { panel.updateProp(1); }); - libraryTrackRowMenu.addToggleItem('Show tracks when expanding nodes', ppt, 'showTracks', () => { pop.collapseAll(); panel.updateProp(1); }); - libraryTrackRowMenu.addToggleItem('Show row stripes', ppt, 'rowStripes', () => { panel.updateProp(1); }); - libraryTrackRowMenu.addSeparator(); - libraryTrackRowMenu.addToggleItem('Row fully clickable', ppt, 'fullLineSelection', () => { panel.updateProp(1); }); - libraryTrackRowMenu.addToggleItem('Row mouse hover', pref, 'libraryRowHover', () => { RepaintWindow(); }); - libraryTrackRowMenu.appendTo(libraryMenu); - - // * FILTER ORDER * // - const libraryFilterOrderMenu = new Menu('Filter order'); - libraryFilterOrderMenu.addRadioItems(['No filter', 'Lossless', 'Lossy', 'Missing replaygain', 'Never played', 'Played often', 'Recently added', 'Recently played', 'Top rated', 'Nowplaying artist'], ppt.filterBy, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], (order) => { - ppt.filterBy = order; - panel.set('Filter', order); - }); - libraryFilterOrderMenu.appendTo(libraryMenu); - - // * SORT ORDER * // - const librarySortOrderMenu = new Menu('Sort order'); - librarySortOrderMenu.addRadioItems(['Default', 'Ascending (hide year)', 'Ascending (show year)', 'Descending (hide year)', 'Descending (show year)'], ppt.sortOrder, [0, 1, 2, 3, 4], (order) => { - ppt.sortOrder = order; - const d = {}; - men.getSortData(d); - men.sortByDate(ppt.sortOrder, d); - }); - librarySortOrderMenu.addSeparator(); - librarySortOrderMenu.addRadioItems(['Action: year after album', 'Action: year before album'], ppt.yearBeforeAlbum, [false, true], (year) => { - ppt.yearBeforeAlbum = year; - }); - librarySortOrderMenu.appendTo(libraryMenu); - - // * VIEW ORDER * // - const libraryViewOrderMenu = new Menu('View order'); - libraryViewOrderMenu.addRadioItems(['Artist', 'Album artist', 'Album artist | album', 'Album', 'Composer', 'Country', 'Country | Genre', 'Genre', 'Label', 'Year', 'Folder structure'], panel.imgView ? ppt.albumArtViewBy : ppt.treeViewBy, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (order) => { - if (ppt.albumArtShow) { - ppt.albumArtViewBy = order; - } else { - ppt.viewBy = order; - ppt.treeViewBy = order; - } - lib.logTree(); - pop.clearTree(); - men.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]); - }); - libraryViewOrderMenu.appendTo(libraryMenu); - - // * SOURCE * // - const librarySourceMenu = new Menu('Source'); - librarySourceMenu.addRadioItems(['Library'], ppt.fixedPlaylist ? '' : ppt.libSource, [1], (src) => { - men.setSource(0); - }); - const libraryPlaylistSourceMenu = new Menu('Playlist'); - libraryPlaylistSourceMenu.addRadioItems(['Active playlist'], ppt.fixedPlaylist ? '' : ppt.libSource, [0], (src) => { - men.setSource(2); - men.setActivePlaylist(); - }); - libraryPlaylistSourceMenu.addSeparator(); - const pl_no = Math.ceil(men.pl.length / 30); - const pl_ix = ppt.fixedPlaylist ? plman.FindPlaylist(ppt.fixedPlaylistName) : -1; - for (let j = 0; j < pl_no; j++) { - const n = `# ${j * 30 + 1} - ${Math.min(men.pl.length, 30 + j * 30)}${30 + j * 30 > pl_ix && ((j * 30) - 1) < pl_ix ? ' >>>' : ''}`; - const libraryPlaylistSourceItems = new Menu(n); - - for (let i = j * 30; i < Math.min(men.pl.length, 30 + j * 30); i++) { - libraryPlaylistSourceItems.addRadioItems([men.pl[i].menuName], ppt.fixedPlaylist ? men.pl[pl_ix].menuName : '', [men.pl[i].menuName], () => { - men.setSource(2); - men.setFixedPlaylist(i); - }); - } - libraryPlaylistSourceItems.appendTo(libraryPlaylistSourceMenu); - } - libraryPlaylistSourceMenu.appendTo(librarySourceMenu); - librarySourceMenu.appendTo(libraryMenu); - if (!context_menu) libraryMenu.appendTo(menu); -} + const updateButtons = () => { + grm.ui.createButtonImages(); + grm.ui.createButtonObjects(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }; + const updateSeekbar = () => { + grm.ui.initMetrics(); + grm.ui.resizeArtwork(true); + RepaintWindow(); + }; -/////////////////////////// -// * BIOGRAPHY OPTIONS * // -/////////////////////////// -/** - * Top menu > Options > Biography. - * @param {Menu} menu Creates the Biography panel menu via a new Menu instance. - * @param {boolean} context_menu Appends Biography panel options to context menu. - */ -function biographyOptions(menu, context_menu) { - const biographyMenu = context_menu ? menu : new Menu('Biography'); - const biographyLayoutMenu = new Menu('Layout'); - - // * LAYOUT * // - if (pref.layout === 'default') { - biographyLayoutMenu.addRadioItems(['Normal', 'Full'], pref.biographyLayout, ['normal', 'full'], (width) => { - pref.biographyLayout = width; - if (!displayBiography) displayBiography = true; displayPlaylist = false; displayLibrary = false; - if (pref.displayLyrics) pref.displayLyrics = false; - displayPlaylist = !displayPlaylist; - initBiographyLayout(); - initButtonState(); + // * TOP MENU * // + const playerControlsTopMenu = new Menu('Top menu'); + const playerControlsTopMenuDefault = new Menu('Default'); + playerControlsTopMenuDefault.addToggleItem('Details', grSet, 'showPanelDetails_default', () => { updateButtons(); }); + playerControlsTopMenuDefault.addToggleItem('Library', grSet, 'showPanelLibrary_default', () => { updateButtons(); }); + playerControlsTopMenuDefault.addToggleItem('Biography', grSet, 'showPanelBiography_default', () => { updateButtons(); }); + playerControlsTopMenuDefault.addToggleItem('Lyrics', grSet, 'showPanelLyrics_default', () => { updateButtons(); }); + playerControlsTopMenuDefault.addToggleItem('Rating', grSet, 'showPanelRating_default', () => { updateButtons(); }); + playerControlsTopMenuDefault.appendTo(playerControlsTopMenu); + + const playerControlsTopMenuArtwork = new Menu('Artwork'); + playerControlsTopMenuArtwork.addToggleItem('Details', grSet, 'showPanelDetails_artwork', () => { updateButtons(); }); + playerControlsTopMenuArtwork.addToggleItem('Library', grSet, 'showPanelLibrary_artwork', () => { updateButtons(); }); + playerControlsTopMenuArtwork.addToggleItem('Biography', grSet, 'showPanelBiography_artwork', () => { updateButtons(); }); + playerControlsTopMenuArtwork.addToggleItem('Lyrics', grSet, 'showPanelLyrics_artwork', () => { updateButtons(); }); + playerControlsTopMenuArtwork.addToggleItem('Rating', grSet, 'showPanelRating_artwork', () => { updateButtons(); }); + playerControlsTopMenuArtwork.appendTo(playerControlsTopMenu); + playerControlsTopMenu.addSeparator(); + + playerControlsTopMenu.addRadioItems(['Align left', 'Align center'], grSet.topMenuAlignment, ['left', 'center'], (align) => { + grSet.topMenuAlignment = align; + updateButtons(); }); - biographyLayoutMenu.addSeparator(); - biographyLayoutMenu.addToggleItem('Use full preset', pref, 'biographyLayoutFullPreset', () => { RepaintWindow(); }); - biographyLayoutMenu.addSeparator(); - } - biographyLayoutMenu.addRadioItems(['Top (default)', 'Right', 'Bottom', 'Left', 'Full overlay', 'Part overlay'], pptBio.style, [0, 1, 2, 3, 4, 5], (layout) => { - pptBio.style = layout; - uiBio.updateProp(1); - }); - biographyLayoutMenu.addSeparator(); - const biographyFilmstripMenu = new Menu('Filmstrip'); - biographyFilmstripMenu.addRadioItems(['Top', 'Right', 'Bottom', 'Left (default)'], pptBio.filmStripPos, [0, 1, 2, 3], (pos) => { - pptBio.filmStripPos = pos; - uiBio.updateProp(1); - }); - biographyFilmstripMenu.addSeparator(); - biographyFilmstripMenu.addToggleItem('Overlay image area', pptBio, 'filmStripOverlay', () => { uiBio.updateProp(1); }); - biographyFilmstripMenu.appendTo(biographyLayoutMenu); - biographyLayoutMenu.appendTo(biographyMenu); - - // * THEME * // - const biographyThemeMenu = new Menu('Theme'); - biographyThemeMenu.addRadioItems(['Georgia-ReBORN', 'Dark', 'Blend', 'Light'], pref.biographyTheme, [0, 1, 2, 3], (theme) => { - pref.biographyTheme = theme; - pptBio.theme = pref.biographyTheme; - initTheme(); - biography.on_colours_changed(); - uiBio.updateProp(1); - themeColorAdjustments(); - }); - biographyThemeMenu.appendTo(biographyMenu); - - // * DISPLAY * // - const biographyDisplayMenu = new Menu('Display'); - biographyDisplayMenu.addRadioItems(['Image + text', 'Image', 'Text'], pref.biographyDisplay, ['Image+text', 'Image', 'Text'], (display) => { - pref.biographyDisplay = display; - setBiographyDisplay(); - uiBio.updateProp(1); - }); - biographyDisplayMenu.addSeparator(); - biographyDisplayMenu.addToggleItem('Filmstrip', pptBio, 'showFilmStrip', () => { uiBio.updateProp(1); }); - biographyDisplayMenu.addToggleItem('Seeker', pptBio, 'imgSeekerShow', () => { uiBio.updateProp(1); }); - biographyDisplayMenu.addToggleItem('Heading', pptBio, 'heading', () => { uiBio.updateProp(1); }); - biographyDisplayMenu.addToggleItem('Summary', pptBio, 'summaryShow', () => { uiBio.updateProp(1); }); - biographyDisplayMenu.addSeparator(); - biographyDisplayMenu.addToggleItem(pptBio.summaryCompact ? 'Summary expand' : 'Summary compact', pptBio, 'summaryCompact', () => { uiBio.updateProp(1); }); - biographyDisplayMenu.addSeparator(); - biographyDisplayMenu.addRadioItems(['Artist view', 'Album view'], pptBio.artistView, [true, false], (view) => { - pptBio.artistView = view; - uiBio.updateProp(1); - }); - biographyDisplayMenu.addSeparator(); - biographyDisplayMenu.addRadioItems(['Prefer now playing', 'Follow selected track (playlist)'], pptBio.focus, [false, true], (view) => { - pptBio.focus = view; - uiBio.updateProp(1); - }); - biographyDisplayMenu.appendTo(biographyMenu); - - // * SOURCES * // - const biographySourcesMenu = new Menu('Sources'); - const biographySourcesTextMenu = new Menu('Text'); - if (!pptBio.sourceAll) { - biographySourcesTextMenu.addRadioItems(['Auto-fallback', 'Static'], pptBio.lockBio, [false, true], (source) => { - pptBio.lockBio = source; - if (pptBio.sourceAll) { - pptBio.lockBio = false; + playerControlsTopMenu.addSeparator(); + playerControlsTopMenu.addToggleItem('Compact top menu', grSet, 'topMenuCompact', () => { + grSet.showTopMenuCompact = grSet.topMenuCompact; + updateButtons(); + }); + playerControlsTopMenu.appendTo(playerControlsMenu); + + // * ALBUM ART * // + if (grSet.layout !== 'compact') { + const playerControlsAlbumArtMenu = new Menu('Album art'); + const playerControlsAlbumArtNotPropMenu = new Menu('When player size is not proportional'); + if (grSet.layout === 'default') { + playerControlsAlbumArtNotPropMenu.addRadioItems(['Align album art left', 'Align album art left (margin)', 'Align album art center', 'Align album art right'], grSet.albumArtAlign, ['left', 'leftMargin', 'center', 'right'], (pos) => { + grSet.albumArtAlign = pos; + grm.ui.loadImageFromAlbumArtList(grm.ui.albumArtIndex); + grm.ui.resizeArtwork(true); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.setLibrarySize(); + grm.ui.setBiographySize(); + RepaintWindow(); + }); + playerControlsAlbumArtNotPropMenu.addSeparator(); + } + playerControlsAlbumArtNotPropMenu.addRadioItems(['Left album art bg', 'Full album art bg', 'No album art bg'], grSet.albumArtBg, ['left', 'full', 'none'], (type) => { + grSet.albumArtBg = type; + RepaintWindow(); + }); + playerControlsAlbumArtNotPropMenu.appendTo(playerControlsAlbumArtMenu); + const playerControlsAlbumArtScaleMenu = new Menu('When player size is maximized/fullscreen'); + if (grSet.layout === 'default') { + playerControlsAlbumArtScaleMenu.addRadioItems(['Scale album art cropped', 'Scale album art stretched', 'Scale album art proportional'], grSet.albumArtScale, ['cropped', 'stretched', 'proportional'], (scale) => { + grSet.albumArtScale = scale; + grm.ui.loadImageFromAlbumArtList(grm.ui.albumArtIndex); + grm.ui.resizeArtwork(true); + RepaintWindow(); + }); + playerControlsAlbumArtScaleMenu.addSeparator(); + playerControlsAlbumArtScaleMenu.addRadioItems([ + 'Crop and stretch - always', + 'Crop and stretch - limit aspect ratio 1.25x', + 'Crop and stretch - limit aspect ratio 1.50x', + 'Crop and stretch - limit aspect ratio 1.75x', + 'Crop and stretch - limit aspect ratio 2.00x' + ], grSet.albumArtAspectRatioLimit, [false, 1.25, 1.5, 1.75, 2], (factor) => { + grSet.albumArtAspectRatioLimit = factor; + grm.ui.loadImageFromAlbumArtList(grm.ui.albumArtIndex); + grm.ui.resizeArtwork(true); + RepaintWindow(); + }); + playerControlsAlbumArtScaleMenu.appendTo(playerControlsAlbumArtMenu); + } + playerControlsAlbumArtMenu.addSeparator(); + playerControlsAlbumArtMenu.addToggleItem(`Cycle album artwork (${grCfg.settings.artworkDisplayTime}s delay)`, grSet, 'cycleArt', () => { + if (!grSet.cycleArt) { + clearTimeout(grm.ui.albumArtTimeout); + grm.ui.albumArtTimeout = 0; + } else { + grm.ui.displayNextImage(); + } + }); + playerControlsAlbumArtMenu.addToggleItem('Cycle album artwork with mouse wheel', grSet, 'cycleArtMWheel'); + playerControlsAlbumArtMenu.addSeparator(); + playerControlsAlbumArtMenu.addToggleItem('Load embedded album art first', grSet, 'loadEmbeddedAlbumArtFirst', () => { + const msg = 'Do you want to load embedded album art first?\n\nYou also need to set it in foobar\'s preferences.\nFile > Preferences > Advanced > Display > Album art\n\nContinue?\n\n\n'; + const msgFb = 'Embedded album art enabled:\n\nYou also need to set it in foobar\'s preferences.\nFile > Preferences > Advanced > Display > Album art.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) grSet.loadEmbeddedAlbumArtFirst = false; + }); + }); + playerControlsAlbumArtMenu.addSeparator(); + + const showHiResAudioLogoMenu = new Menu('Show hi-res audio badge on album cover'); + showHiResAudioLogoMenu.addToggleItem('Enabled', grSet, 'showHiResAudioBadge', () => { RepaintWindow(); }); + showHiResAudioLogoMenu.addSeparator(); + showHiResAudioLogoMenu.addToggleItem('Round', grSet, 'hiResAudioBadgeRound', () => { RepaintWindow(); }, !grSet.showHiResAudioBadge); + showHiResAudioLogoMenu.addSeparator(); + showHiResAudioLogoMenu.addRadioItems(['Small', 'Normal', 'Large'], grSet.hiResAudioBadgeSize, ['small', 'normal', 'large'], (size) => { + grSet.hiResAudioBadgeSize = size; + RepaintWindow(); + }, !grSet.showHiResAudioBadge); + showHiResAudioLogoMenu.addSeparator(); + showHiResAudioLogoMenu.addRadioItems(['Top left', 'Top right', 'Bottom left', 'Bottom right'], grSet.hiResAudioBadgePos, ['topleft', 'topright', 'bottomleft', 'bottomright'], (pos) => { + grSet.hiResAudioBadgePos = pos; + RepaintWindow(); + }, !grSet.showHiResAudioBadge); + showHiResAudioLogoMenu.appendTo(playerControlsAlbumArtMenu); + playerControlsAlbumArtMenu.addToggleItem('Show pause on album cover', grSet, 'showPause', () => { RepaintWindow(); }); + playerControlsAlbumArtMenu.appendTo(playerControlsMenu); + } + + // * JUMP SEARCH * // + const playerControlsJumpSearchMenu = new Menu('Jump search'); + playerControlsJumpSearchMenu.addToggleItem('Include library in playlist search query', grSet, 'jumpSearchIncludeLibrary'); + playerControlsJumpSearchMenu.addToggleItem('Include playlist in library search query', grSet, 'jumpSearchIncludePlaylist'); + playerControlsJumpSearchMenu.addSeparator(); + playerControlsJumpSearchMenu.addToggleItem('Composer only in jump search query', grSet, 'jumpSearchComposerOnly'); + playerControlsJumpSearchMenu.appendTo(playerControlsMenu); + + // * SCROLLBAR * // + const playerControlsScrollbarMenu = new Menu('Scrollbar'); + const playerControlsScrollbarPlaylistMenu = new Menu('Playlist'); + const playerControlsScrollbarPlaylistStepsMenu = new Menu('Mouse wheel scroll steps'); + playerControlsScrollbarPlaylistStepsMenu.addRadioItems(['1 Step', '2 Steps', '3 Steps (default)', '4 Steps', '5 Steps', '6 Steps', '7 Steps', '8 Steps', '9 Steps', '10 Steps'], grSet.playlistWheelScrollSteps, [0.5, 2, 3, 4, 5, 6, 7, 8, 9, 10], (steps) => { + grSet.playlistWheelScrollSteps = steps; + playlistCallback(); + }); + playerControlsScrollbarPlaylistStepsMenu.appendTo(playerControlsScrollbarPlaylistMenu); + const playerControlsScrollbarPlaylistDurationMenu = new Menu('Mouse wheel scroll smooth duration'); + playerControlsScrollbarPlaylistDurationMenu.addRadioItems(['100ms', '200ms', '300ms (default)', '400ms', '500ms', '600ms', '700ms', '800ms', '900ms', '1000ms'], grSet.playlistWheelScrollDuration, [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], (duration) => { + grSet.playlistWheelScrollDuration = duration; + playlistCallback(); + }); + playerControlsScrollbarPlaylistDurationMenu.appendTo(playerControlsScrollbarPlaylistMenu); + playerControlsScrollbarPlaylistMenu.addSeparator(); + playerControlsScrollbarPlaylistMenu.addToggleItem('Auto-scroll to current playing song', grSet, 'playlistAutoScrollNowPlaying'); + playerControlsScrollbarPlaylistMenu.addToggleItem('Auto-hide', grSet, 'playlistAutoHideScrollbar', () => { + plSet.show_scrollbar = !grSet.playlistAutoHideScrollbar; + grm.ui.updatePlaylist(); + }); + playerControlsScrollbarPlaylistMenu.addToggleItem('Smooth scroll', grSet, 'playlistSmoothScrolling'); + playerControlsScrollbarPlaylistMenu.appendTo(playerControlsScrollbarMenu); + + const playerControlsScrollbarLibraryMenu = new Menu('Library'); + const playerControlsScrollbarLibraryStepsMenu = new Menu('Mouse wheel scroll steps'); + playerControlsScrollbarLibraryStepsMenu.addRadioItems(['1 Step', '2 Steps', '3 Steps (default)', '4 Steps', '5 Steps', '6 Steps', '7 Steps', '8 Steps', '9 Steps', '10 Steps'], libSet.scrollStep, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (steps) => { + libSet.scrollStep = steps; + lib.panel.updateProp(1); + }); + playerControlsScrollbarLibraryStepsMenu.appendTo(playerControlsScrollbarLibraryMenu); + const playerControlsScrollbarLibraryDurationMenu = new Menu('Mouse wheel scroll smooth duration'); + playerControlsScrollbarLibraryDurationMenu.addRadioItems(['100ms', '200ms', '300ms', '400ms', '500ms (default)', '600ms', '700ms', '800ms', '900ms', '1000ms'], libSet.durationScroll, [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], (duration) => { + libSet.durationScroll = duration; + lib.panel.updateProp(1); + }); + playerControlsScrollbarLibraryDurationMenu.appendTo(playerControlsScrollbarLibraryMenu); + playerControlsScrollbarLibraryMenu.addSeparator(); + playerControlsScrollbarLibraryMenu.addToggleItem('Auto-scroll to current playing song', grSet, 'libraryAutoScrollNowPlaying'); + playerControlsScrollbarLibraryMenu.addToggleItem('Auto-hide', grSet, 'libraryAutoHideScrollbar', () => { + libSet.sbarShow = grSet.libraryAutoHideScrollbar ? 1 : 2; + grm.ui.setLibrarySize(); + }); + playerControlsScrollbarLibraryMenu.addToggleItem('Smooth scroll', libSet, 'smooth'); + playerControlsScrollbarLibraryMenu.appendTo(playerControlsScrollbarMenu); + + const playerControlsBiographyMenu = new Menu('Biography'); + const playerControlsScrollbarBiographyStepsMenu = new Menu('Mouse wheel scroll steps'); + playerControlsScrollbarBiographyStepsMenu.addRadioItems(['1 Step', '2 Steps', '3 Steps (default)', '4 Steps', '5 Steps', '6 Steps', '7 Steps', '8 Steps', '9 Steps', '10 Steps'], bioSet.scrollStep, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (steps) => { + bioSet.scrollStep = steps; + bio.ui.updateProp(1); + }); + playerControlsScrollbarBiographyStepsMenu.appendTo(playerControlsBiographyMenu); + const playerControlsScrollbarBiographyDurationMenu = new Menu('Mouse wheel scroll smooth duration'); + playerControlsScrollbarBiographyDurationMenu.addRadioItems(['100ms', '200ms', '300ms', '400ms', '500ms (default)', '600ms', '700ms', '800ms', '900ms', '1000ms'], bioSet.durationScroll, [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], (duration) => { + bioSet.durationScroll = duration; + bio.ui.updateProp(1); + }); + playerControlsScrollbarBiographyDurationMenu.appendTo(playerControlsBiographyMenu); + playerControlsBiographyMenu.addSeparator(); + playerControlsBiographyMenu.addToggleItem('Auto-hide', grSet, 'biographyAutoHideScrollbar', () => { + if (grSet.biographyAutoHideScrollbar) { + bioSet.sbarShow = 1; + bio.but.setScrollBtnsHide(); } else { - pptBio.lockRev = pptBio.lockBio; + bioSet.sbarShow = 2; + bio.but.setScrollBtnsHide(false, 'both'); } - uiBio.updateProp(1); - }, !pptBio.sourceAll); - biographySourcesTextMenu.addSeparator(); - } - biographySourcesTextMenu.addToggleItem('Amalgamate', pptBio, 'sourceAll', () => { - pptBio.lockBio = false; - pptBio.lockRev = false; - uiBio.updateProp(1); - }); - biographySourcesTextMenu.addSeparator(); - biographySourcesTextMenu.addToggleItem('Prefer composition (allmusic && wikipedia review)', pptBio, 'classicalMusicMode', () => { - cfg.classicalModeEnable = !cfg.classicalModeEnable; - pptBio.classicalAlbFallback = pptBio.classicalMusicMode; - uiBio.updateProp(1); - }); - biographySourcesTextMenu.appendTo(biographySourcesMenu); - - biographySourcesMenu.createRadioSubMenu('Photo', ['Cycle from download folder', 'Cycle from custom folder [fallback to above]', 'Artist (single image [fb2k: display])'], pptBio.cycPhotoLocation, [0, 1, 2], (location) => { - pptBio.cycPhotoLocation = location; - if (location === 0) { - pptBio.cycPhoto = true; - } - else if (location === 1 && !pptBio.get('Panel Biography - System: Photo Folder Checked', false)) { - fb.ShowPopupMessage('Enter folder in options: "Server Settings"\\Photo\\Custom photo folder.', 'Biography: custom folder for photo cycling'); - pptBio.set('Panel Biography - System: Photo Folder Checked', true); - pptBio.cycPhoto = true; - imgBio.artistReset(); - } - else if (location === 2) { - pptBio.cycPhoto = false; - } - imgBio.updImages(); - }); - - const biographySourcesCoverMenu = new Menu('Cover'); - if (!pptBio.loadCovAllFb && !pptBio.loadCovFolder) { - biographySourcesCoverMenu.addRadioItems(['Front', 'Back', 'Disc', 'Icon', 'Artist'], pptBio.covType, [0, 1, 2, 3, 4], (type) => { - pptBio.covType = type; - imgBio.cov.selection = [0, -1, -1, -1, -1]; - imgBio.cov.selFiltered = [0]; - imgBio.getImages(); - }, pptBio.loadCovFolder); - } else { - biographySourcesCoverMenu.addToggleItems(['Front', 'Back', 'Disc', 'Icon', 'Artist'], imgBio.cov.selection, [0, 1, 2, 3, 4], (type) => { - !pptBio.loadCovAllFb ? pptBio.covType = type : imgBio.cov.selection[type] = imgBio.cov.selection[type] === -1 ? type : -1; - imgBio.cov.selFiltered = imgBio.cov.selection.filter(v => v !== -1); - if (!imgBio.cov.selFiltered.length) { - imgBio.cov.selection = [0, -1, -1, -1, -1]; - imgBio.cov.selFiltered = [0]; - } - pptBio.loadCovSelFb = JSON.stringify(imgBio.cov.selection); - !pptBio.loadCovAllFb ? imgBio.getImages() : imgBio.check(); - }, !pptBio.loadCovAllFb && pptBio.loadCovFolder); - } - biographySourcesCoverMenu.addSeparator(); - biographySourcesCoverMenu.addToggleItem('Cycle above', pptBio, 'loadCovAllFb', () => { - pptBio.loadCovAllFb = !pptBio.loadCovAllFb; - imgBio.toggle('loadCovAllFb'); - }); - biographySourcesCoverMenu.addToggleItem('Cycle from download folder', pptBio, 'loadCovFolder', () => { - pptBio.loadCovFolder = !pptBio.loadCovFolder; - imgBio.toggle('loadCovFolder'); - if (pptBio.loadCovFolder) { - fb.ShowPopupMessage("Enter folder in options: \"Server Settings\"\\Cover\\Covers: cycle folder.\n\nDefault: artist photo folder.\n\nImages are updated when the album changes. Any images arriving after choosing the current album aren't included.", 'Biography: load folder for cover cycling'); - } - }); - biographySourcesCoverMenu.appendTo(biographySourcesMenu); - - const biographySourcesOpenFileLocationMenu = new Menu('Open file location'); - biographySourcesOpenFileLocationMenu.addItem('Image', false, () => { - const imgInfo = imgBio.pth(); - menBio.path.img = imgInfo.imgPth; - OpenExplorer(`explorer /select, "${menBio.path.img}"`, false); - }); - biographySourcesOpenFileLocationMenu.addSeparator(); - if (txt.bio.am.length || txt.rev.am.length) { - biographySourcesOpenFileLocationMenu.addItem(pptBio.artistView ? 'Biography [allmusic]' : 'Review [allmusic]', false, () => { - menBio.path.am = pptBio.artistView ? txt.bioPth('Am') : txt.revPth('Am'); - OpenExplorer(`explorer /select, "${menBio.path.am[1]}"`, false); + bio.ui.updateProp(1); }); - } - if (txt.bio.lfm.length || txt.rev.lfm.length) { - biographySourcesOpenFileLocationMenu.addItem(pptBio.artistView ? 'Biography [last.fm]' : 'Review [last.fm]', false, () => { - menBio.path.lfm = pptBio.artistView ? txt.bioPth('Lfm') : txt.revPth('Lfm'); - OpenExplorer(`explorer /select, "${menBio.path.lfm[1]}"`, false); + playerControlsBiographyMenu.addToggleItem('Smooth scroll', bioSet, 'smooth'); + playerControlsBiographyMenu.appendTo(playerControlsScrollbarMenu); + + playerControlsScrollbarMenu.appendTo(playerControlsMenu); + + // * TOOLTIP * // + const playerControlsToolTipMenu = new Menu('Tooltip'); + playerControlsToolTipMenu.addToggleItem('Show tooltips only on truncated text', grSet, 'showTooltipTruncated'); + playerControlsToolTipMenu.addSeparator(); + playerControlsToolTipMenu.addToggleItem('Show timeline tooltips', grSet, 'showTooltipTimeline'); + playerControlsToolTipMenu.addSeparator(); + const playerControlsVolumeToolTipMenu = new Menu('Show volume tooltips'); + playerControlsVolumeToolTipMenu.addToggleItem('Enabled', grSet, 'showTooltipVolume'); + playerControlsVolumeToolTipMenu.addSeparator(); + playerControlsVolumeToolTipMenu.addToggleItem('Show volume in percent', grSet, 'showTooltipVolumeInPercent'); + playerControlsVolumeToolTipMenu.appendTo(playerControlsToolTipMenu); + playerControlsToolTipMenu.addSeparator(); + playerControlsToolTipMenu.addToggleItem('Show main tooltips', grSet, 'showTooltipMain'); + playerControlsToolTipMenu.addToggleItem('Show library tooltips', grSet, 'showTooltipLibrary', () => { + lib.but.tooltipLib.show = grSet.showTooltipLibrary || grSet.showTooltipTruncated; + grm.ui.setLibrarySize(); }); - } - if (txt.bio.wiki.length || txt.rev.wiki.length) { - biographySourcesOpenFileLocationMenu.addItem(pptBio.artistView ? 'Biography [wikipedia]' : 'Review [wikipedia]', false, () => { - menBio.path.wiki = pptBio.artistView ? txt.bioPth('Wiki') : txt.revPth('Wiki'); - OpenExplorer(`explorer /select, "${menBio.path.wiki[1]}"`, false); + playerControlsToolTipMenu.addToggleItem('Show biography tooltips', grSet, 'showTooltipBiography', () => { + bio.but.tooltipBio.show = grSet.showTooltipBiography || grSet.showTooltipTruncated; + grm.ui.setBiographySize(); }); - } - if (lyricsBio.lyrics.length) { - biographySourcesOpenFileLocationMenu.addItem('Lyrics', false, () => { - menBio.path.txt = pptBio.artistView ? txt.txtReaderPth() : txt.txtRevPth(); - OpenExplorer(`explorer /select, "${menBio.path.txt[1]}"`, false); + playerControlsToolTipMenu.addSeparator(); + playerControlsToolTipMenu.addToggleItem('Show styled tooltips', grSet, 'showStyledTooltips'); + playerControlsToolTipMenu.appendTo(playerControlsMenu); + + // * PANEL MENU * // + const playerControlsPanelMenu = new Menu('Panel'); + const playerControlsPanelNotPropMenu = new Menu('Width'); + playerControlsPanelNotPropMenu.addToggleItem('Use auto panel width', grSet, 'panelWidthAuto', () => { + grSet.albumArtAlign = grSet.panelWidthAuto ? 'left' : 'right'; + grm.ui.resizeArtwork(true); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.ui.setLibrarySize(); + grm.ui.setBiographySize(); + RepaintWindow(); }); - } - biographySourcesMenu.addSeparator(); - biographySourcesOpenFileLocationMenu.appendTo(biographySourcesMenu); - - biographySourcesMenu.addItem('Force update', false, () => { - panelBio.callServer(1, panelBio.id.focus, 'bio_forceUpdate', 0); - uiBio.updateProp(1); - }); - biographySourcesMenu.appendTo(biographyMenu); - - // * IMAGES * // - const biographyImageMenu = new Menu('Image'); - const biographyImageDefaultMenu = new Menu('Image + text'); - biographyImageDefaultMenu.createRadioSubMenu('Photo', ['Regular', 'Auto-fill (default)', 'Circular'], pptBio.artStyleDual, [0, 1, 2], (style) => { - pptBio.artStyleDual = style; - uiBio.updateProp(1); - }); - biographyImageDefaultMenu.addToggleItem('Reflection', pptBio, 'artReflDual', () => { pptBio.artShadowDual = false; uiBio.updateProp(1); }); - biographyImageDefaultMenu.addToggleItem('Shadow', pptBio, 'artShadowDual', () => { pptBio.artReflDual = false; uiBio.updateProp(1); }); - biographyImageDefaultMenu.addSeparator(); - biographyImageDefaultMenu.createRadioSubMenu('Cover', ['Regular', 'Auto-fill (default)', 'Circular'], pptBio.covStyleDual, [0, 1, 2], (style) => { - pptBio.covStyleDual = style; - uiBio.updateProp(1); - }); - biographyImageDefaultMenu.addToggleItem('Reflection', pptBio, 'covReflDual', () => { pptBio.covShadowDual = false; uiBio.updateProp(1); }); - biographyImageDefaultMenu.addToggleItem('Shadow', pptBio, 'covShadowDual', () => { pptBio.covReflDual = false; uiBio.updateProp(1); }); - - biographyImageDefaultMenu.appendTo(biographyImageMenu); - const biographyImageOnlyMenu = new Menu('Image only'); - biographyImageOnlyMenu.createRadioSubMenu('Photo', ['Regular', 'Auto-fill (default)', 'Circular'], pptBio.artStyleImgOnly, [0, 1, 2], (style) => { - pptBio.artStyleImgOnly = style; - uiBio.updateProp(1); - }); - biographyImageOnlyMenu.addToggleItem('Reflection', pptBio, 'artReflImgOnly', () => { pptBio.artShadowImgOnly = false; uiBio.updateProp(1); }); - biographyImageOnlyMenu.addToggleItem('Shadow', pptBio, 'artShadowImgOnly', () => { pptBio.artReflImgOnly = false; uiBio.updateProp(1); }); - biographyImageOnlyMenu.addSeparator(); - biographyImageOnlyMenu.createRadioSubMenu('Cover', ['Regular', 'Auto-fill (default)', 'Circular'], pptBio.covStyleImgOnly, [0, 1, 2], (style) => { - pptBio.covStyleImgOnly = style; - uiBio.updateProp(1); - }); - biographyImageOnlyMenu.addToggleItem('Reflection', pptBio, 'covReflImgOnly', () => { pptBio.covShadowImgOnly = false; uiBio.updateProp(1); }); - biographyImageOnlyMenu.addToggleItem('Shadow', pptBio, 'covShadowImgOnly', () => { pptBio.covReflImgOnly = false; uiBio.updateProp(1); }); - biographyImageOnlyMenu.appendTo(biographyImageMenu); - - const biographyImageFilmstripMenu = new Menu('Filmstrip'); - biographyImageFilmstripMenu.createRadioSubMenu('Photo', ['Regular', 'Auto-fill (default)', 'Circular'], pptBio.filmPhotoStyle, [0, 1, 2], (style) => { - pptBio.filmPhotoStyle = style; - uiBio.updateProp(1); - }); - biographyImageFilmstripMenu.createRadioSubMenu('Cover', ['Regular', 'Auto-fill (default)', 'Circular'], pptBio.filmCoverStyle, [0, 1, 2], (style) => { - pptBio.filmCoverStyle = style; - uiBio.updateProp(1); - }); - biographyImageFilmstripMenu.appendTo(biographyImageMenu); - biographyImageMenu.addSeparator(); - - const biographyImageDownloadMenu = new Menu('Downloads'); - biographyImageDownloadMenu.addRadioItems([' 5 Images', '10 Images (default)', '15 Images', '20 Images'], cfg.photoNum, [5, 10, 15, 20], (num) => { - cfg.photoNum = num; - uiBio.updateProp(1); - }); - biographyImageDownloadMenu.appendTo(biographyImageMenu); - biographyImageMenu.addSeparator(); - - const biographyImageAutoCycleMenu = new Menu('Auto cycle'); - biographyImageAutoCycleMenu.addToggleItem('Auto cycle', pptBio, 'cycPic', () => { uiBio.updateProp(1); }); - biographyImageAutoCycleMenu.addSeparator(); - biographyImageAutoCycleMenu.addToggleItem('Smooth transition', pptBio, 'imgSmoothTrans', () => { uiBio.updateProp(1); }); - biographyImageAutoCycleMenu.addSeparator(); - biographyImageAutoCycleMenu.addRadioItems([' 5 sec', '10 sec', '15 sec (default)', '30 sec', '60 sec'], pptBio.cycTimePic, [5, 10, 15, 30, 60], (dur) => { - pptBio.cycTimePic = dur; - uiBio.updateProp(1); - }); - biographyImageAutoCycleMenu.appendTo(biographyImageMenu); - - biographyImageMenu.appendTo(biographyMenu); - - if (!context_menu) biographyMenu.appendTo(menu); -} + playerControlsPanelNotPropMenu.appendTo(playerControlsPanelMenu); + playerControlsPanelMenu.addSeparator(); + if (grSet.layout !== 'compact') { + const showPanelOnStartupMenu = new Menu('Show panel on startup'); + showPanelOnStartupMenu.addRadioItems(grSet.layout === 'artwork' ? ['Cover', 'Playlist', 'Details', 'Library', 'Biography', 'Lyrics'] : ['Playlist', 'Details', 'Library', 'Biography', 'Lyrics'], + grSet.showPanelOnStartup, grSet.layout === 'artwork' ? ['cover', 'playlist', 'details', 'library', 'biography', 'lyrics'] : ['playlist', 'details', 'library', 'biography', 'lyrics'], (panel) => { + grSet.showPanelOnStartup = panel; + window.Reload(); + }); + showPanelOnStartupMenu.appendTo(playerControlsPanelMenu); + } + playerControlsPanelMenu.addToggleItem('Show logo on preloader', grSet, 'showPreloaderLogo', () => { RepaintWindow(); }); + playerControlsPanelMenu.addSeparator(); + playerControlsPanelMenu.addToggleItem('Return to home on playback stop', grSet, 'returnToHomeOnPlaybackStop'); + playerControlsPanelMenu.addToggleItem('Switch to playlist when adding songs', grSet, 'addTracksPlaylistSwitch'); + playerControlsPanelMenu.addSeparator(); + playerControlsPanelMenu.addToggleItem('Hide middle panel shadow', grSet, 'hideMiddlePanelShadow', () => { RepaintWindow(); }); + playerControlsPanelMenu.addSeparator(); + playerControlsPanelMenu.addToggleItem('Lock player size', grSet, 'lockPlayerSize', () => { UIHacks.DisableSizing = true; }); + playerControlsPanelMenu.appendTo(playerControlsMenu); + + // * LOWER BAR MENU * // + const playerControlsLowerBarMenu = new Menu('Lower bar'); + // * TRANSPORT BUTTON SIZE * // + const transportSizeMenu = new Menu('Transport button size'); + const transportSizeMenuDefault = new Menu('Default'); + transportSizeMenuDefault.addRadioItems(['28px', '30px', '32px (default)', '34px', '36px', '38px', '40px', '42px'], grSet.transportButtonSize_default, [28, 30, 32, 34, 36, 38, 40, 42], (size) => { + if (size === -1) { + grSet.transportButtonSize_default -= 2; + } else if (size === 999) { + grSet.transportButtonSize_default += 2; + } else { + grSet.transportButtonSize_default = size; + } + grm.ui.createFonts(); + grm.ui.resizeArtwork(true); + updateButtons(); + }); + transportSizeMenuDefault.appendTo(transportSizeMenu); + + const transportSizeMenuArtwork = new Menu('Artwork'); + transportSizeMenuArtwork.addRadioItems(['28px', '30px', '32px (default)', '34px', '36px'], grSet.transportButtonSize_artwork, [28, 30, 32, 34, 36], (size) => { + if (size === -1) { + grSet.transportButtonSize_artwork -= 2; + } else if (size === 999) { + grSet.transportButtonSize_artwork += 2; + } else { + grSet.transportButtonSize_artwork = size; + } + grm.ui.createFonts(); + grm.ui.resizeArtwork(true); + updateButtons(); + }); + transportSizeMenuArtwork.appendTo(transportSizeMenu); + + const transportSizeMenuCompact = new Menu('Compact'); + transportSizeMenuCompact.addRadioItems(['28px', '30px', '32px (default)', '34px', '36px'], grSet.transportButtonSize_compact, [28, 30, 32, 34, 36], (size) => { + if (size === -1) { + grSet.transportButtonSize_compact -= 2; + } else if (size === 999) { + grSet.transportButtonSize_compact += 2; + } else { + grSet.transportButtonSize_compact = size; + } + grm.ui.createFonts(); + grm.ui.resizeArtwork(true); + updateButtons(); + }); + transportSizeMenuCompact.appendTo(transportSizeMenu); + transportSizeMenu.appendTo(playerControlsLowerBarMenu); + + // * TRANSPORT BUTTON SPACING * // + const transportSpacingMenu = new Menu('Transport button spacing'); + const transportSpacingMenuDefault = new Menu('Default'); + transportSpacingMenuDefault.addRadioItems(['-2', '3px', '5px (default)', '7px', '10px', '15px', '+2'], grSet.transportButtonSpacing_default, [-1, 3, 5, 7, 10, 15, 999], (size) => { + if (size === -1) { + grSet.transportButtonSpacing_default -= 2; + } else if (size === 999) { + grSet.transportButtonSpacing_default += 2; + } else { + grSet.transportButtonSpacing_default = size; + } + updateButtons(); + }); + transportSpacingMenuDefault.appendTo(transportSpacingMenu); + + const transportSpacingMenuArtwork = new Menu('Artwork'); + transportSpacingMenuArtwork.addRadioItems(['-2', '3px', '5px (default)', '7px', '10px', '15px', '+2'], grSet.transportButtonSpacing_artwork, [-1, 3, 5, 7, 10, 15, 999], (size) => { + if (size === -1) { + grSet.transportButtonSpacing_artwork -= 2; + } else if (size === 999) { + grSet.transportButtonSpacing_artwork += 2; + } else { + grSet.transportButtonSpacing_artwork = size; + } + updateButtons(); + }); + transportSpacingMenuArtwork.appendTo(transportSpacingMenu); + + const transportSpacingMenuCompact = new Menu('Compact'); + transportSpacingMenuCompact.addRadioItems(['-2', '3px', '5px (default)', '7px', '10px', '15px', '+2'], grSet.transportButtonSpacing_compact, [-1, 3, 5, 7, 10, 15, 999], (size) => { + if (size === -1) { + grSet.transportButtonSpacing_compact -= 2; + } else if (size === 999) { + grSet.transportButtonSpacing_compact += 2; + } else { + grSet.transportButtonSpacing_compact = size; + } + updateButtons(); + }); + transportSpacingMenuCompact.appendTo(transportSpacingMenu); + transportSpacingMenu.appendTo(playerControlsLowerBarMenu); + playerControlsLowerBarMenu.addSeparator(); + + // * SHOW TRANSPORT CONTROLS * // + const transportControlsMenu = new Menu('Show transport controls'); + transportControlsMenu.addToggleItem('Default', grSet, 'showTransportControls_default', () => { + grm.ui.resizeArtwork(true); + updateButtons(); + }); + transportControlsMenu.addToggleItem('Artwork', grSet, 'showTransportControls_artwork', () => { + grm.ui.resizeArtwork(true); + updateButtons(); + }); + transportControlsMenu.addToggleItem('Compact', grSet, 'showTransportControls_compact', () => { + grm.ui.resizeArtwork(true); + updateButtons(); + }); + transportControlsMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW PLAYBACK ORDER BUTTON * // + const playbackOrderBtnMenu = new Menu('Show playback order button'); + playbackOrderBtnMenu.addToggleItem('Default', grSet, 'showPlaybackOrderBtn_default', () => { + updateButtons(); + }, !grSet.showTransportControls_default); + playbackOrderBtnMenu.addToggleItem('Artwork', grSet, 'showPlaybackOrderBtn_artwork', () => { + updateButtons(); + }, !grSet.showTransportControls_artwork); + playbackOrderBtnMenu.addToggleItem('Compact', grSet, 'showPlaybackOrderBtn_compact', () => { + updateButtons(); + }, !grSet.showTransportControls_compact); + playbackOrderBtnMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW RELOAD BUTTON * // + const reloadBtnMenu = new Menu('Show reload button'); + reloadBtnMenu.addToggleItem('Default', grSet, 'showReloadBtn_default', () => { + grm.volBtn = new VolumeButton(); + updateButtons(); + }, !grSet.showTransportControls_default); + reloadBtnMenu.addToggleItem('Artwork', grSet, 'showReloadBtn_artwork', () => { + grm.volBtn = new VolumeButton(); + updateButtons(); + }, !grSet.showTransportControls_artwork); + reloadBtnMenu.addToggleItem('Compact', grSet, 'showReloadBtn_compact', () => { + grm.volBtn = new VolumeButton(); + updateButtons(); + }, !grSet.showTransportControls_compact); + reloadBtnMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW ADD TRACKS BUTTON * // + const addTrackBtnMenu = new Menu('Show add tracks button'); + addTrackBtnMenu.addToggleItem('Default', grSet, 'showAddTracksBtn_default', () => { + grm.volBtn = new VolumeButton(); + updateButtons(); + }, !grSet.showTransportControls_default); + addTrackBtnMenu.addToggleItem('Artwork', grSet, 'showAddTracksBtn_artwork', () => { + grm.volBtn = new VolumeButton(); + updateButtons(); + }, !grSet.showTransportControls_artwork); + addTrackBtnMenu.addToggleItem('Compact', grSet, 'showAddTracksBtn_compact', () => { + grm.volBtn = new VolumeButton(); + updateButtons(); + }, !grSet.showTransportControls_compact); + addTrackBtnMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW VOLUME BUTTON * // + const volumeBtnMenu = new Menu('Show volume button'); + volumeBtnMenu.addToggleItem('Default', grSet, 'showVolumeBtn_default', () => { + updateButtons(); + }, !grSet.showTransportControls_default); + volumeBtnMenu.addToggleItem('Artwork', grSet, 'showVolumeBtn_artwork', () => { + updateButtons(); + }, !grSet.showTransportControls_artwork); + volumeBtnMenu.addToggleItem('Compact', grSet, 'showVolumeBtn_compact', () => { + updateButtons(); + }, !grSet.showTransportControls_compact); + volumeBtnMenu.addSeparator(); + volumeBtnMenu.addToggleItem('Auto-hide bar', grSet, 'autoHideVolumeBar', () => { + grm.volBtn.toggleVolumeBar(); + updateButtons(); + }); + volumeBtnMenu.appendTo(playerControlsLowerBarMenu); + playerControlsLowerBarMenu.addSeparator(); + // * SHOW PLAYBACK TIME IN LOWER BAR * // + const playbackTimeMenu = new Menu('Show playback time'); + playbackTimeMenu.addToggleItem('Default', grSet, 'showPlaybackTime_default', () => { + updateButtons(); + }); + playbackTimeMenu.addToggleItem('Artwork', grSet, 'showPlaybackTime_artwork', () => { + updateButtons(); + }); + playbackTimeMenu.addToggleItem('Compact', grSet, 'showPlaybackTime_compact', () => { + updateButtons(); + }); + playbackTimeMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW ARTIST IN LOWER BAR * // + const showArtistMenu = new Menu('Show artist'); + showArtistMenu.addToggleItem('Default', grSet, 'showLowerBarArtist_default', () => { RepaintWindow(); }); + showArtistMenu.addToggleItem('Artwork', grSet, 'showLowerBarArtist_artwork', () => { RepaintWindow(); }); + showArtistMenu.addToggleItem('Compact', grSet, 'showLowerBarArtist_compact', () => { RepaintWindow(); }); + showArtistMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW TRACK NUMBER IN LOWER BAR * // + const showTrackNumberMenu = new Menu('Show track number'); + showTrackNumberMenu.addToggleItem('Default', grSet, 'showLowerBarTrackNum_default', () => { on_metadb_changed(); RepaintWindow(); }); + showTrackNumberMenu.addToggleItem('Artwork', grSet, 'showLowerBarTrackNum_artwork', () => { on_metadb_changed(); RepaintWindow(); }); + showTrackNumberMenu.addToggleItem('Compact', grSet, 'showLowerBarTrackNum_compact', () => { on_metadb_changed(); RepaintWindow(); }); + showTrackNumberMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW SONG TITLE IN LOWER BAR * // + const showTitleMenu = new Menu('Show song title'); + showTitleMenu.addToggleItem('Default', grSet, 'showLowerBarTitle_default', () => { RepaintWindow(); }); + showTitleMenu.addToggleItem('Artwork', grSet, 'showLowerBarTitle_artwork', () => { RepaintWindow(); }); + showTitleMenu.addToggleItem('Compact', grSet, 'showLowerBarTitle_compact', () => { RepaintWindow(); }); + showTitleMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW COMPOSER IN LOWER BAR * // + const showComposerMenu = new Menu('Show composer'); + showComposerMenu.addToggleItem('Default', grSet, 'showLowerBarComposer_default', () => { RepaintWindow(); }); + showComposerMenu.addToggleItem('Artwork', grSet, 'showLowerBarComposer_artwork', () => { RepaintWindow(); }); + showComposerMenu.addToggleItem('Compact', grSet, 'showLowerBarComposer_compact', () => { RepaintWindow(); }); + showComposerMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW ARTIST COUNTRY FLAGS IN LOWER BAR * // + const showArtistFlagsMenu = new Menu('Show artist country flags'); + showArtistFlagsMenu.addToggleItem('Default', grSet, 'showLowerBarArtistFlags_default', () => { grm.ui.loadCountryFlags(); RepaintWindow(); }); + showArtistFlagsMenu.addToggleItem('Artwork', grSet, 'showLowerBarArtistFlags_artwork', () => { grm.ui.loadCountryFlags(); RepaintWindow(); }); + showArtistFlagsMenu.addToggleItem('Compact', grSet, 'showLowerBarArtistFlags_compact', () => { grm.ui.loadCountryFlags(); RepaintWindow(); }); + showArtistFlagsMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW SOFTWARE VERSION IN LOWER BAR * // + const showSoftwareVersionMenu = new Menu('Show software version'); + showSoftwareVersionMenu.addToggleItem('Default', grSet, 'showLowerBarVersion_default', () => { grm.ui.initMain(); }); + showSoftwareVersionMenu.addToggleItem('Artwork', grSet, 'showLowerBarVersion_artwork', () => { grm.ui.initMain(); }); + showSoftwareVersionMenu.addToggleItem('Compact', grSet, 'showLowerBarVersion_compact', () => { grm.ui.initMain(); }); + showSoftwareVersionMenu.appendTo(playerControlsLowerBarMenu); + playerControlsLowerBarMenu.addSeparator(); + + // * SHOW PROGRESS BAR * // + const progressBarMenu = new Menu('Show progress bar'); + progressBarMenu.addToggleItem('Default', grSet, 'showProgressBar_default', () => { updateSeekbar(); }); + progressBarMenu.addToggleItem('Artwork', grSet, 'showProgressBar_artwork', () => { updateSeekbar(); }); + progressBarMenu.addToggleItem('Compact', grSet, 'showProgressBar_compact', () => { updateSeekbar(); }); + progressBarMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW PEAKMETER BAR * // + const peakmeterBarMenu = new Menu('Show peakmeter bar'); + peakmeterBarMenu.addToggleItem('Default', grSet, 'showPeakmeterBar_default', () => { updateSeekbar(); }); + peakmeterBarMenu.addToggleItem('Artwork', grSet, 'showPeakmeterBar_artwork', () => { updateSeekbar(); }); + peakmeterBarMenu.addToggleItem('Compact', grSet, 'showPeakmeterBar_compact', () => { updateSeekbar(); }); + peakmeterBarMenu.appendTo(playerControlsLowerBarMenu); + + // * SHOW WAVEFORM BAR * // + const waveformBarMenu = new Menu('Show waveform bar'); + waveformBarMenu.addToggleItem('Default', grSet, 'showWaveformBar_default', () => { updateSeekbar(); }); + waveformBarMenu.addToggleItem('Artwork', grSet, 'showWaveformBar_artwork', () => { updateSeekbar(); }); + waveformBarMenu.addToggleItem('Compact', grSet, 'showWaveformBar_compact', () => { updateSeekbar(); }); + waveformBarMenu.appendTo(playerControlsLowerBarMenu); + playerControlsLowerBarMenu.addSeparator(); + + // * ADD TRACKS BUTTON CONTROLS * // + const addTracksBtnControlsMenu = new Menu('Add tracks button'); + addTracksBtnControlsMenu.addItem('Add tracks playlist', false, () => { grm.inputBox.addTracksPlaylist(); }); + addTracksBtnControlsMenu.appendTo(playerControlsLowerBarMenu); + + playerControlsLowerBarMenu.appendTo(playerControlsMenu); + + // * SEEKBAR - PROGRESS BAR * // + const playerControlsSeekBarMenu = new Menu('Seekbar'); + playerControlsSeekBarMenu.createRadioSubMenu('Type', ['Progress bar', 'Peakmeter bar', 'Waveform bar'], grSet.seekbar, ['progressbar', 'peakmeterbar', 'waveformbar'], (type) => { + grSet.seekbar = type; + if (grSet.seekbar === 'waveformbar') { + grm.waveBar.updateBar(); + } + grm.ui.setProgressBarRefresh(); + RepaintWindow(); + }); + const playerControlsProgressBarMenu = new Menu('Progress bar'); + playerControlsProgressBarMenu.createRadioSubMenu('Style', ['Default', 'Rounded', 'Lines', 'Blocks', 'Dots', 'Thin'], grSet.styleProgressBarDesign, ['default', 'rounded', 'lines', 'blocks', 'dots', 'thin'], (style) => { + grSet.styleProgressBarDesign = style; + grm.ui.initMetrics(); + RepaintWindow(); + }); + playerControlsProgressBarMenu.createRadioSubMenu('Mouse wheel seek speed', [' 1 sec', ' 2 sec', ' 3 sec', ' 4 sec', ' 5 sec (default)', ' 6 sec', ' 7 sec', ' 8 sec', ' 9 sec', '10 sec'], grSet.progressBarWheelSeekSpeed, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (speed) => { + grSet.progressBarWheelSeekSpeed = speed; + }); + playerControlsProgressBarMenu.createRadioSubMenu('Refresh rate', ['1000 ms (very slow CPU)', ' 500 ms', ' 333 ms', ' Variable (default)', ' 100 ms', ' 60 ms', ' 30 ms (very fast CPU)'], grSet.progressBarRefreshRate, [1000, 500, 333, 'variable', 100, 60, 30], (rate) => { + grSet.progressBarRefreshRate = rate; + grm.ui.setProgressBarRefresh(); + }, !grSet.showProgressBar_default || !grSet.showProgressBar_artwork || !grSet.showProgressBar_compact); + playerControlsProgressBarMenu.appendTo(playerControlsSeekBarMenu); + + // * SEEKBAR - PEAKMETER BAR * // + const playerControlspeakmeterBarMenu = new Menu('Peakmeter bar'); + playerControlspeakmeterBarMenu.createRadioSubMenu('Style', ['Horizontal', 'Horizontal center', 'Vertical'], grSet.peakmeterBarDesign, ['horizontal', 'horizontal_center', 'vertical'], (design) => { + grSet.peakmeterBarDesign = design; + RepaintWindow(); + }); + if (grSet.peakmeterBarDesign === 'vertical') { + playerControlspeakmeterBarMenu.createRadioSubMenu('Size', [' 0 px', ' 2 px', ' 4 px', ' 6 px', ' 8 px', '10 px', grSet.layout !== 'default' ? '12 px (default)' : '12 px', '14 px', '16 px', '18 px', grSet.layout !== 'default' ? '20 px' : '20 px (default)', '25 px', '30 px', '35 px', '40 px', 'Minimum'], grSet.peakmeterBarVertSize, [0, 2, 4, 6, 8, 10, 20, 25, 30, 35, 40, 'min'], (size) => { + grSet.peakmeterBarVertSize = size; + RepaintWindow(); + }); + playerControlspeakmeterBarMenu.createRadioSubMenu('Decibel range', ['2 to -20 db (default)', '2 to -15 db', '2 to -10 db', '3 to -20 db', '3 to -15 db', '3 to -10 db', '5 to -20 db', '5 to -15 db', '5 to -10 db'], grSet.peakmeterBarVertDbRange, [220, 215, 210, 320, 315, 310, 520, 515, 510], (range) => { + grSet.peakmeterBarVertDbRange = range; + RepaintWindow(); + }); + } + const playerControlspeakmeterBarDisplayMenu = new Menu('Display'); + if (grSet.peakmeterBarDesign === 'horizontal' || grSet.peakmeterBarDesign === 'horizontal_center') { + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show over bars', grSet, 'peakmeterBarOverBars', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addSeparator(); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show outer bars', grSet, 'peakmeterBarOuterBars', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show outer peaks', grSet, 'peakmeterBarOuterPeaks', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addSeparator(); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show main bars', grSet, 'peakmeterBarMainBars', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show main peaks', grSet, 'peakmeterBarMainPeaks', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addSeparator(); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show middle bars', grSet, 'peakmeterBarMiddleBars', () => { RepaintWindow(); }); + } + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show progress bar', grSet, 'peakmeterBarProgBar', () => { RepaintWindow(); }); + if (grSet.peakmeterBarDesign === 'horizontal' || grSet.peakmeterBarDesign === 'horizontal_center') { + playerControlspeakmeterBarDisplayMenu.addSeparator(); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show gaps', grSet, 'peakmeterBarGaps', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show grid', grSet, 'peakmeterBarGrid', () => { grm.peakBar.on_size(grm.ui.ww, grm.ui.wh); RepaintWindow(); }); + } + if (grSet.peakmeterBarDesign === 'vertical') { + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show peaks', grSet, 'peakmeterBarVertPeaks', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.addToggleItem('Show baseline', grSet, 'peakmeterBarVertBaseline', () => { RepaintWindow(); }); + } + playerControlspeakmeterBarDisplayMenu.addToggleItem(grSet.layout !== 'default' ? 'Show info (only available in Default layout)' : 'Show info', grSet, 'peakmeterBarInfo', () => { RepaintWindow(); }); + playerControlspeakmeterBarDisplayMenu.appendTo(playerControlspeakmeterBarMenu); + playerControlspeakmeterBarMenu.createRadioSubMenu('Refresh rate', [' 200 ms (very slow CPU)', ' 150 ms', ' 120 ms', ' 100 ms', ' 80 ms (default)', ' 60 ms', ' 30 ms (very fast CPU)'], grSet.peakmeterBarRefreshRate, [200, 150, 120, 100, 80, 60, 30], (rate) => { + grSet.peakmeterBarRefreshRate = rate; + grm.ui.setProgressBarRefresh(); + }, !grSet.showPeakmeterBar_default || !grSet.showPeakmeterBar_artwork || !grSet.showPeakmeterBar_compact); + playerControlspeakmeterBarMenu.appendTo(playerControlsSeekBarMenu); + + // * SEEKBAR - WAVEFORM BAR * // + const playerControlsWaveformBarMenu = new Menu('Waveform bar'); + playerControlsWaveformBarMenu.createRadioSubMenu(`Analysis${grSet.waveformBarMode === 'ffprobe' ? '' : '\t (ffprobe only)'}`, + ['RMS level', 'Peak level', 'RMS peak'], grSet.waveformBarAnalysis, ['rms_level', 'peak_level', 'rms_peak'], (type) => { + grSet.waveformBarAnalysis = type; + grm.waveBar.updateConfig({ preset: { analysisMode: type } }); + grm.waveBar.updateBar(); + RepaintWindow(); + }, grSet.waveformBarMode !== 'ffprobe'); -//////////////////////// -// * LYRICS OPTIONS * // -//////////////////////// -/** - * Top menu > Options > Lyrics. - * @param {Menu} menu Creates the Lyrics panel menu via a new Menu instance. - * @param {boolean} context_menu Appends Lyrics panel options to context menu. - */ -function lyricsOptions(menu, context_menu) { - const lyricsMenu = context_menu ? menu : new Menu('Lyrics'); - - if (pref.layout === 'default') { - lyricsMenu.createRadioSubMenu('Layout', ['Normal', 'Full'], pref.lyricsLayout, ['normal', 'full'], (width) => { - pref.lyricsLayout = width; - if (!pref.displayLyrics && pref.lyricsLayout === 'full' || noAlbumArtStub) { - displayPlaylist = true; - pref.displayLyrics = true; - displayLibrary = false; - displayBiography = false; - lyricsLayoutFullWidth = pref.lyricsLayout === 'full'; - } - if (pref.displayLyrics && pref.lyricsLayout === 'full') { - displayPlaylist = false; - pref.displayLyrics = true; - lyricsLayoutFullWidth = pref.lyricsLayout === 'full'; - } - playlist.on_size(ww, wh); - initLyrics(); - on_playback_seek(); - resizeArtwork(true); - initButtonState(); + playerControlsWaveformBarMenu.createRadioSubMenu('Mode', ['FFprobe', 'Audiowaveform', 'Visualizer'], grSet.waveformBarMode, ['ffprobe', 'audiowaveform', 'visualizer'], (mode) => { + grSet.waveformBarMode = mode; + grm.waveBar.updateConfig({ analysis: { binaryMode: mode } }); + grm.waveBar.updateBar(); RepaintWindow(); }); - } - const lyricsDisplayMenu = new Menu('Display'); - lyricsDisplayMenu.createRadioSubMenu('Show drop shadow', ['None', 'Small', 'Normal', 'Large'], pref.lyricsDropShadowLevel, [0, 1, 2, 3], (size) => { - pref.lyricsDropShadowLevel = size; - initLyrics(); - RepaintWindow(); - }); - lyricsDisplayMenu.addToggleItem('Show fade scroll', pref, 'lyricsFadeScroll', () => { - initLyrics(); - RepaintWindow(); - }); - lyricsDisplayMenu.addToggleItem('Show larger current sync', pref, 'lyricsLargerCurrentSync', () => { - pptBio.largerSyncLyricLine = pref.lyricsLargerCurrentSync; - initLyrics(); - uiBio.updateProp(1); - RepaintWindow(); - }); - lyricsDisplayMenu.addToggleItem('Show lyrics on album art', pref, 'lyricsAlbumArt', () => { - initMainColors(); - RepaintWindow(); - }); - lyricsDisplayMenu.appendTo(lyricsMenu); - - const lyricsControlsMenu = new Menu('Controls'); - lyricsControlsMenu.addToggleItem('Remember active lyrics state', pref, 'lyricsRememberActiveState', () => { - if (pref.lyricsRememberActiveState) pref.displayLyrics = false; - }); - lyricsControlsMenu.addToggleItem('Remember lyrics panel state', pref, 'lyricsRememberPanelState'); - lyricsControlsMenu.appendTo(lyricsMenu); - - const lyricsScrollSpeedMenu = new Menu('Scroll speed'); - lyricsScrollSpeedMenu.addRadioItems(['Fastest (very slow CPU)', 'Fast', 'Normal', 'Slow', 'Slowest (very fast CPU)'], pref.lyricsScrollSpeed, ['fastest', 'fast', 'normal', 'slow', 'slowest'], (speed) => { - pref.lyricsScrollSpeed = speed; - switch (speed) { - case 'fastest': - pref.lyricsScrollRateAvg = 300; - pref.lyricsScrollRateMax = 150; - break; - case 'fast': - pref.lyricsScrollRateAvg = 500; - pref.lyricsScrollRateMax = 250; - break; - case 'normal': - pref.lyricsScrollRateAvg = 750; - pref.lyricsScrollRateMax = 375; - break; - case 'slow': - pref.lyricsScrollRateAvg = 1000; - pref.lyricsScrollRateMax = 500; - break; - case 'slowest': - pref.lyricsScrollRateAvg = 1500; - pref.lyricsScrollRateMax = 725; - break; - } - initLyrics(); - RepaintWindow(); - }); - lyricsScrollSpeedMenu.appendTo(lyricsMenu); - lyricsMenu.addSeparator(); - - lyricsMenu.addItem('Lyrics information', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Lyric information'); }); - lyricsMenu.addItem('Lyrics search', false, () => { fb.RunMainMenuCommand('View/ESLyric/Search...'); }); - lyricsMenu.addSeparator(); - lyricsMenu.addItem('Next lyrics', false, () => { lyrics.nextLyrics(); }); - lyricsMenu.addItem('Edit lyrics', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Edit lyric'); }); - lyricsMenu.addItem('Delete lyrics', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Delete lyric'); }); - lyricsMenu.addSeparator(); - lyricsMenu.addItem('Save lyrics to tags', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Save Lyric To/Tags'); }); - lyricsMenu.addSeparator(); - lyricsMenu.addItem('Open containing folder', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Open containing folder'); }); - - if (!context_menu) lyricsMenu.appendTo(menu); -} + playerControlsWaveformBarMenu.createRadioSubMenu('Style', ['Waveform', 'Bars', 'Dots', 'Halfbars'], grSet.waveformBarDesign, ['waveform', 'bars', 'dots', 'halfbars'], (design) => { + grSet.waveformBarDesign = design; + grm.waveBar.updateConfig({ preset: { barDesign: design } }); + }); + const playerControlsWaveformBarSizeMenu = new Menu('Size'); + playerControlsWaveformBarSizeMenu.createRadioSubMenu('Waveform', ['1', '2', '3', '4', '5'], grSet.waveformBarSizeWave, [1, 2, 3, 4, 5], (size) => { + grSet.waveformBarSizeWave = size; + grm.waveBar.updateConfig({ ui: { sizeWave: size } }); + }); + playerControlsWaveformBarSizeMenu.createRadioSubMenu('Bars', ['1', '2', '3', '4', '5'], grSet.waveformBarSizeBars, [1, 2, 3, 4, 5], (size) => { + grSet.waveformBarSizeBars = size; + grm.waveBar.updateConfig({ ui: { sizeBars: size } }); + }); + playerControlsWaveformBarSizeMenu.createRadioSubMenu('Dots', ['1', '2', '3', '4', '5'], grSet.waveformBarSizeDots, [1, 2, 3, 4, 5], (size) => { + grSet.waveformBarSizeDots = size; + grm.waveBar.updateConfig({ ui: { sizeDots: size } }); + }); + playerControlsWaveformBarSizeMenu.createRadioSubMenu('Halfbars', ['1', '2', '3', '4', '5'], grSet.waveformBarSizeHalf, [1, 2, 3, 4, 5], (size) => { + grSet.waveformBarSizeHalf = size; + grm.waveBar.updateConfig({ ui: { sizeHalf: size } }); + }); + playerControlsWaveformBarSizeMenu.addSeparator(); + playerControlsWaveformBarSizeMenu.addToggleItem('Normalize width', grSet, 'waveformBarSizeNormalize', () => { + grm.waveBar.updateConfig({ ui: { sizeNormalizeWidth: grSet.waveformBarSizeNormalize } }); + }); + playerControlsWaveformBarSizeMenu.appendTo(playerControlsWaveformBarMenu); -////////////////////////// -// * SETTINGS OPTIONS * // -////////////////////////// -/** - * Top menu > Options > Settings. - * @param {Menu} menu Creates the Settings menu via a new Menu instance. - */ -function settingsOptions(menu) { - const settingsMenu = new Menu('Settings'); + const playerControlsWaveformBarDisplayMenu = new Menu(`Display${grSet.waveformBarPaint === 'full' && grSet.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`); + playerControlsWaveformBarDisplayMenu.addRadioItems(['Full', 'Partial'], grSet.waveformBarPaint, ['full', 'partial'], (paint) => { + grSet.waveformBarPaint = paint; + grm.waveBar.updateConfig({ preset: { paintMode: paint } }); + }); + playerControlsWaveformBarDisplayMenu.addSeparator(); + + playerControlsWaveformBarDisplayMenu.addToggleItem(`Prepaint${grSet.waveformBarPaint === 'full' ? '\t(partial only)' : ''}`, grSet, 'waveformBarPrepaint', () => { + grm.waveBar.updateConfig({ preset: { prepaint: grSet.waveformBarPrepaint } }); + }, grSet.waveformBarPaint === 'full'); + + const waveformBarPrepaintMenuDisabled = grSet.waveformBarPaint === 'full' || grSet.waveformBarMode === 'visualizer' || !grSet.waveformBarPrepaint; + playerControlsWaveformBarDisplayMenu.createRadioSubMenu('Prepaint front', [' 2 secs', ' 5 secs', '10 secs', ' Full'], grSet.waveformBarPrepaintFront, [2, 5, 10, Infinity], (time) => { + grSet.waveformBarPrepaintFront = time; + grm.waveBar.updateConfig({ preset: { prepaintFront: time } }); + }, waveformBarPrepaintMenuDisabled); + playerControlsWaveformBarDisplayMenu.appendTo(playerControlsWaveformBarMenu); + playerControlsWaveformBarDisplayMenu.addSeparator(); + + playerControlsWaveformBarDisplayMenu.addToggleItem('Animate', grSet, 'waveformBarAnimate', () => { + grm.waveBar.updateConfig({ preset: { animate: grSet.waveformBarAnimate } }); + }); + + playerControlsWaveformBarDisplayMenu.addToggleItem(`Use BPM${grSet.waveformBarPaint === 'full' && grSet.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`, grSet, 'waveformBarBPM', () => { + if (grSet.waveformBarBPM) grSet.waveformBarRefreshRateVar = true; + grm.waveBar.updateConfig({ + preset: { useBPM: grSet.waveformBarBPM }, + ui: { refreshRateVar: grSet.waveformBarRefreshRateVar } + }); + }, !(grSet.waveformBarPaint === 'partial' && grSet.waveformBarPrepaint || grSet.waveformBarMode === 'visualizer')); - // * THEME DAY/NIGHT MODE * // - const themeDayNightModeMenu = new Menu('Theme day/night mode'); + playerControlsWaveformBarDisplayMenu.addToggleItem('Invert halfbars', grSet, 'waveformBarInvertHalfbars', () => { + grm.waveBar.updateConfig({ preset: { invertHalfbars: grSet.waveformBarInvertHalfbars } }); + }); + playerControlsWaveformBarDisplayMenu.addSeparator(); - const dayNightTimeRangeDefaults = ['6-18', '7-19', '8-20', '9-21', '10-22']; - const dayNightTimeRangeLabels = dayNightTimeRangeDefaults.map(FormatThemeDayNightModeString); - dayNightTimeRangeLabels.unshift('Deactivated (default)'); + playerControlsWaveformBarDisplayMenu.addToggleItem('Show indicator', grSet, 'waveformBarIndicator', () => { + grm.waveBar.updateConfig({ preset: { indicator: grSet.waveformBarIndicator } }); + }); - const dayNightTimeRangeCustom = pref.themeDayNightMode && !dayNightTimeRangeDefaults.includes(pref.themeDayNightMode); - const dayNightTimeRangeCustomLabel = `Custom: ${FormatThemeDayNightModeString(pref.themeDayNightMode)}`; - const dayNightTimeRangeVal = dayNightTimeRangeCustom ? pref.themeDayNightMode : (pref.themeDayNightMode || false); - const dayNightTimeRangeValues = [false, ...dayNightTimeRangeDefaults]; + const playerControlsWaveformBarRefreshMenu = new Menu(`Refresh rate${grSet.waveformBarPaint === 'full' && grSet.waveformBarMode !== 'visualizer' ? '\t(partial only)' : ''}`); + const waveformBarRefreshMenuDisabled = grSet.waveformBarPaint === 'full' || grSet.waveformBarMode === 'visualizer' || !grSet.waveformBarPrepaint; + playerControlsWaveformBarRefreshMenu.addRadioItems(['1000 ms (very slow CPU)', ' 500 ms', ' 200 ms', ' 100 ms (default)', ' 80 ms', ' 60 ms', ' 30 ms (very fast CPU)'], grSet.waveformBarRefreshRate, [1000, 500, 200, 100, 80, 60, 30], (rate) => { + grSet.waveformBarRefreshRate = rate; + grm.waveBar.updateConfig({ ui: { refreshRate: rate } }); + }, waveformBarRefreshMenuDisabled); + playerControlsWaveformBarRefreshMenu.addSeparator(); + playerControlsWaveformBarRefreshMenu.addToggleItem(' Variable refresh rate', grSet, 'waveformBarRefreshRateVar', () => { + grm.waveBar.updateConfig({ ui: { refreshRateVar: grSet.waveformBarRefreshRateVar } }); + }); + playerControlsWaveformBarRefreshMenu.appendTo(playerControlsWaveformBarMenu); + playerControlsWaveformBarMenu.appendTo(playerControlsSeekBarMenu); + playerControlsSeekBarMenu.appendTo(playerControlsMenu); - if (dayNightTimeRangeCustom) { - dayNightTimeRangeLabels.push(dayNightTimeRangeCustomLabel); - dayNightTimeRangeValues.push(pref.themeDayNightMode); + playerControlsMenu.appendTo(menu); } - themeDayNightModeMenu.addItem('Set custom time range', false, () => { - inputBox('themeDayNightModeCustom'); - }); - themeDayNightModeMenu.addSeparator(); - themeDayNightModeMenu.addRadioItems(dayNightTimeRangeLabels, dayNightTimeRangeVal, dayNightTimeRangeValues, (time) => { - pref.themeDayNightMode = time; - if (!pref.themeDayNightMode) { - pref.themeDayNightMode = false; - clearInterval(themeDayNightModeTimer); - themeDayNightModeTimer = null; - return; + /** + * Top menu > Options > Playlist. + * @param {Menu} menu - Creates the Playlist panel menu via a new Menu instance. + * @param {boolean} context_menu - Appends Playlist panel options to context menu. + * @protected + */ + playlistOptions(menu, context_menu) { + const playlistMenu = context_menu ? menu : new Menu('Playlist'); + + const playlistCallback = () => { + pl.call.on_size(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }; + + // * LAYOUT * // + if (grSet.layout === 'default') { + playlistMenu.createRadioSubMenu('Layout', ['Normal', 'Full'], grSet.playlistLayout, ['normal', 'full'], (width) => { + grSet.playlistLayout = width; + if (!grm.ui.displayPlaylist) grm.ui.displayPlaylist = true; grm.ui.displayLibrary = false; grm.ui.displayBiography = false; + if (grm.ui.displayLyrics) grm.ui.displayLyrics = false; + grm.ui.resizeArtwork(true); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.jSearch.on_size(); + grm.button.initButtonState(); + RepaintWindow(); + }); } - const msg = 'Do you want to activate the theme day/night mode?\n\nThe default daytime theme is White\nand the nighttime theme is Black.\n\nYou can set up and configure\na new theme and styles for both modes\nin the theme day/night mode setup.\n\nContinue?\n\n\n'; - const msgFb = 'Theme day/night mode is active:\n\nThe default daytime theme is White\nand the nighttime theme is Black.\n\nYou can set up and configure\na new theme and styles for both modes\nin the theme day/night mode setup.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) { - resetTheme(); - initThemeDayNightMode(new Date()); - initThemeFull = true; - if (pref.theme.startsWith('custom')) initCustomTheme(); - if (!fb.IsPlaying) setThemeColors(); - initTheme(); - initStyleState(); - initThemePresetState(); - } else { - pref.themeDayNightMode = false; - if (pref.presetAutoRandomMode !== 'off') { - pref.presetAutoRandomMode = 'dblclick'; - getRandomThemePreset(); - } - } + + // * PLAYLIST MANAGER * // + const playlistManagerMenu = new Menu('Playlist manager'); + const playlistManagerShowMenu = new Menu('Show playlist manager'); + playlistManagerShowMenu.addToggleItem('Default', grSet, 'showPlaylistManager_default', playlistCallback); + playlistManagerShowMenu.addToggleItem('Artwork', grSet, 'showPlaylistManager_artwork', playlistCallback); + playlistManagerShowMenu.addToggleItem('Compact', grSet, 'showPlaylistManager_compact', playlistCallback); + playlistManagerShowMenu.appendTo(playlistManagerMenu); + playlistManagerMenu.addToggleItem('Show playlist history', grSet, 'showPlaylistHistory', () => { RepaintWindow(); }); + playlistManagerMenu.addToggleItem('Auto-hide', grSet, 'autoHidePlman', () => { grm.ui.initTheme(); }); + playlistManagerMenu.appendTo(playlistMenu); + + // * ALBUM HEADER * // + const playlistAlbumMenu = new Menu('Album header'); + const playlistAlbumArtMenu = new Menu('Album art'); + playlistAlbumArtMenu.addToggleItem('Show', plSet, 'show_album_art', () => { grm.ui.updatePlaylist(); }); + playlistAlbumArtMenu.addToggleItem('Auto-hide when no cover', plSet, 'auto_album_art', () => { grm.ui.updatePlaylist(); }); + playlistAlbumArtMenu.appendTo(playlistAlbumMenu); + playlistAlbumMenu.addSeparator(); + + playlistAlbumMenu.addToggleItem('Album header', plSet, 'show_header', () => { + grm.ui.updatePlaylist(); }); - }); - themeDayNightModeMenu.addSeparator(); - themeDayNightModeMenu.appendTo(settingsMenu); - themeDayNightModeMenu.addItem(!pref.themeSetupDay ? 'Theme setup for daytime' : 'Save and exit daytime theme setup', false, () => { - pref.themeSetupDay = !pref.themeSetupDay; - pref.themeSetupNight = false; - RepaintWindow(); - if (!pref.themeSetupDay) { - themeNotification = ''; - return; - } - const msg = '>>> Theme setup for daytime is active <<<\n\nPlease select your theme and styles for daytime usage.\nAfter configuring the theme settings, revisit this menu to save them.\n\n\n'; - ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); - resetTheme(); - setThemeDayNightTheme(true); - initThemeFull = true; - if (pref.theme.startsWith('custom')) initCustomTheme(); - if (!fb.IsPlaying) setThemeColors(); - initTheme(); - initStyleState(); - initThemePresetState(); - }); - themeDayNightModeMenu.addItem(!pref.themeSetupNight ? 'Theme setup for nighttime' : 'Save and exit nighttime theme setup', false, () => { - pref.themeSetupDay = false; - pref.themeSetupNight = !pref.themeSetupNight; - RepaintWindow(); - if (!pref.themeSetupNight) { - themeNotification = ''; - return; - } - const msg = '>>> Theme setup for nighttime is active <<<\n\nPlease select your theme and styles for nighttime usage.\nAfter configuring the theme settings, revisit this menu to save them.\n\n\n'; - ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); - resetTheme(); - setThemeDayNightTheme(false); - initThemeFull = true; - if (pref.theme.startsWith('custom')) initCustomTheme(); - if (!fb.IsPlaying) setThemeColors(); - initTheme(); - initStyleState(); - initThemePresetState(); - }); - - // * HIDE OTHER MENUS WHEN THEME DAY/NIGHT SETUP IS ACTIVE - if (pref.themeSetupDay || pref.themeSetupNight) { - settingsMenu.appendTo(menu); - return; - } + playlistAlbumMenu.addToggleItem('Compact header', plSet, 'use_compact_header', () => { + PlaylistRescale(true); + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }, !plSet.show_header); + playlistAlbumMenu.addToggleItem('Auto collapse and expand', plSet, 'auto_collapse', () => { + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + }); + playlistAlbumMenu.addSeparator(); + playlistAlbumMenu.addToggleItem('Ctrl+click to follow hyperlinks', grSet, 'hyperlinksCtrlClick'); + playlistAlbumMenu.addSeparator(); + playlistAlbumMenu.addToggleItem('Show disc sub-header', plSet, 'show_disc_header', () => { grm.ui.updatePlaylist(); }); + playlistAlbumMenu.addToggleItem('Show group info', plSet, 'show_group_info', () => { grm.ui.updatePlaylist(); }); + playlistAlbumMenu.addToggleItem('Show bit depth and sample rate always', grCfg.settings, 'playlistShowBitSampleAlways', () => { grm.ui.updatePlaylist(); }); + playlistAlbumMenu.addToggleItem('Show weblinks in context menu', grSet, 'showWeblinks'); + playlistAlbumMenu.addToggleItem('Show long release date (YYYY-MM-DD)', grSet, 'showPlaylistFullDate', () => { grm.ui.updatePlaylist(); }); + playlistAlbumMenu.addSeparator(); + playlistAlbumMenu.addToggleItem('Show rating', plSet, 'show_rating_header', () => { grm.ui.updatePlaylist(); }); + playlistAlbumMenu.addToggleItem('Show PLR value', plSet, 'show_PLR_header', () => { grm.ui.updatePlaylist(); }); + playlistAlbumMenu.addSeparator(); + playlistAlbumMenu.addItem('Customize header info', false, () => { grm.inputBox.playlistCustomHeaderInfo(); grm.ui.updatePlaylist(); }); + playlistAlbumMenu.appendTo(playlistMenu); + + // * TRACK ROW * // + const rowsMenu = new Menu('Track row'); + rowsMenu.addToggleItem('Show row stripes', plSet, 'show_row_stripes', playlistCallback); + rowsMenu.addSeparator(); + rowsMenu.addToggleItem('Show play count', plSet, 'show_playcount', playlistCallback); + rowsMenu.addToggleItem('Show queue position', plSet, 'show_queue_position', playlistCallback); + rowsMenu.addSeparator(); + rowsMenu.addToggleItem('Show rating', plSet, 'show_rating', playlistCallback); + rowsMenu.addToggleItem('Show rating from tags', plSet, 'use_rating_from_tags', () => { grm.ui.updatePlaylist(); }); + rowsMenu.addToggleItem('Show rating grid', grSet, 'showPlaylistRatingGrid', playlistCallback); + rowsMenu.addSeparator(); + rowsMenu.addToggleItem('Show PLR value', plSet, 'show_PLR', playlistCallback); + rowsMenu.addSeparator(); + rowsMenu.addToggleItem('Show track numbers', grSet, 'showPlaylistTrackNumbers', () => { grSet.showPlaylistIndexNumbers = false; grm.ui.updatePlaylist(); }); + rowsMenu.addToggleItem('Show index numbers', grSet, 'showPlaylistIndexNumbers', () => { grSet.showPlaylistTrackNumbers = false; grm.ui.updatePlaylist(); }); + rowsMenu.addSeparator(); + rowsMenu.addToggleItem('Show artist name on difference', grSet, 'showDifferentArtist', () => { grm.ui.updatePlaylist(); }); + rowsMenu.addToggleItem('Show artist name in all rows', grSet, 'showArtistPlaylistRows', () => { grm.ui.updatePlaylist(); }); + rowsMenu.addToggleItem('Show album title in all rows', grSet, 'showAlbumPlaylistRows', () => { grm.ui.updatePlaylist(); }); + rowsMenu.addToggleItem('Show time remaining on playing track', grSet, 'playlistTimeRemaining', () => { RepaintWindow(); }); + rowsMenu.addToggleItem('Show vinyl style numbering if available', grSet, 'showVinylNums', () => { grm.ui.updatePlaylist(); }); + rowsMenu.addToggleItem('Show last.fm scrobbles on no local plays', grSet, 'lastFmScrobblesFallback', () => { grm.ui.updatePlaylist(); }); + rowsMenu.addSeparator(); + rowsMenu.addToggleItem('Row mouse hover', grSet, 'playlistRowHover', () => { RepaintWindow(); }); + rowsMenu.addSeparator(); + rowsMenu.addItem('Customize track row', false, () => { grm.inputBox.playlistCustomTrackRow(); grm.ui.updatePlaylist(); }); + rowsMenu.appendTo(playlistMenu); + + // * SORT ORDER * // + const playlistSortOrderMenu = new Menu('Sort order'); + playlistSortOrderMenu.addToggleItem('Always auto-sort', grSet, 'playlistSortOrderAuto'); + playlistSortOrderMenu.addSeparator(); + playlistSortOrderMenu.addItem('Sort by...', false, () => { fb.RunMainMenuCommand('Edit/Sort/Sort by...'); grm.ui.updatePlaylist(); }); + playlistSortOrderMenu.addSeparator(); + + const setSorting = () => { + grm.ui.setPlaylistSortOrder(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }; - // * THEME SANDBOX * // - const themeSandboxMenu = new Menu('Theme sandbox'); - const restoreThemeStylePresetSettings = (reset) => { - pref.presetAutoRandomMode = 'dblclick'; - setThemePresetSelection(true); // * Reactivate all - resetTheme(); - if (reset) restoreThemeStylePreset(true); else restoreThemeStylePreset(); - if (pref.savedPreset !== false) setThemePreset(pref.savedPreset); - updateStyle(); + const sortOrderWithDirection = ['artistDate', 'albumRating', 'albumPlaycount', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country']; + + /** @type {string} Holds the current sort order preference without any direction suffix ('_asc' or '_dsc'). */ + let savedOrder = grSet.playlistSortOrder; + /** @type {boolean} Indicaties if the current sort order (`savedOrder`) requires a direction. */ + let savedOrderWithDirection = sortOrderWithDirection.includes(savedOrder.slice(0, -4)); + // Remove direction from saved order for radio item checking + if (savedOrderWithDirection) savedOrder = savedOrder.slice(0, -4); + + playlistSortOrderMenu.addRadioItems(['Order by ascending', 'Order by descending'], grSet.playlistSortOrderDirection, ['_asc', '_dsc'], (direction) => { + grSet.playlistSortOrder = `${savedOrder}${savedOrderWithDirection ? direction : ''}`; + setSorting(); + }, !savedOrderWithDirection); + + playlistSortOrderMenu.addSeparator(); + + playlistSortOrderMenu.addRadioItems(['Default', 'Artist | date', 'Album', 'Album rating', 'Album playcount', 'Track', 'Track number', 'Track rating', 'Track playcount', 'Year', 'Genre', 'Label', 'Country', 'File path', 'Custom'], savedOrder, + ['default', 'artistDate', 'albumTitle', 'albumRating', 'albumPlaycount', 'trackTitle', 'trackNumber', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country', 'filePath', 'custom'], (order) => { + savedOrderWithDirection = sortOrderWithDirection.includes(order); + savedOrder = order; + grSet.playlistSortOrder = `${order}${savedOrderWithDirection ? grSet.playlistSortOrderDirection : ''}`; + if (order === 'custom') grm.inputBox.playlistSortCustom(); + setSorting(); + }, false, !grSet.playlistSortOrderAuto); + playlistSortOrderMenu.addSeparator(); + + playlistSortOrderMenu.addItem('Randomize', false, () => { fb.RunMainMenuCommand('Edit/Sort/Randomize'); grm.ui.updatePlaylist(); }); + playlistSortOrderMenu.addItem('Reverse', false, () => { fb.RunMainMenuCommand('Edit/Sort/Reverse'); grm.ui.updatePlaylist(); }); + playlistSortOrderMenu.addSeparator(); + playlistSortOrderMenu.addItem('Save', false, () => { fb.RunMainMenuCommand('File/Save playlist...'); grm.ui.updatePlaylist(); }); + playlistSortOrderMenu.addItem('Load', false, () => { fb.RunMainMenuCommand('File/Load playlist...'); grm.ui.updatePlaylist(); }); + playlistSortOrderMenu.addItem('Undo', false, () => { fb.RunMainMenuCommand('Edit/Undo'); grm.ui.updatePlaylist(); }); + + playlistSortOrderMenu.appendTo(playlistMenu); + + if (!context_menu) playlistMenu.appendTo(menu); } - themeSandboxMenu.addToggleItem('Enabled', pref, 'themeSandbox', () => { - if (!pref.themeSandbox) { - const msg = 'Do you want to restore\nor keep current theme settings?\n\nThis will restore previously used\ntheme, styles, preset\nor use the current active.\n\nContinue?\n\n\n'; - const msgFb = 'Theme settings restored:\n\nTheme, styles or preset have been restored.'; - ShowPopup(true, msgFb, msg, 'Restore', 'Keep', (confirmed) => { - if (confirmed) { - restoreThemeStylePresetSettings(); - } else { - restoreThemeStylePresetSettings(true); - } - }); - return; - } - const msg = 'Do you want to activate the theme sandbox?\n\nThis mode is useful when trying out\nthemes, styles, presets or writing theme tags.\n\nAfter disabling the theme sandbox mode,\npreviously used theme settings can be restored.\n\nContinue?\n\n\n'; - const msgFb = 'Theme sandbox mode activated:\n\nThis mode is useful when trying out\nthemes, styles, presets or writing theme tags.\n\nAfter disabling the theme sandbox mode,\npreviously used theme settings will be restored.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) pref.themeSandbox = false; - }); - }); - themeSandboxMenu.addSeparator(); - themeSandboxMenu.addItem('Restore theme settings', false, () => { - const msg = 'Do you want to restore theme settings?\n\nThis will restore previously used\ntheme, styles, preset.\n\nContinue?\n\n\n'; - const msgFb = 'Theme settings restored:\n\nTheme, styles or preset have been restored.'; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) restoreThemeStylePresetSettings(); - }); - }, pref.themeSandbox); - themeSandboxMenu.appendTo(settingsMenu); - - // * THEME FONTS * // - const themeFontMenu = new Menu('Theme fonts'); - themeFontMenu.addToggleItem('Use custom theme fonts', pref, 'customThemeFonts', () => { - const msg = 'Do you want to use custom theme fonts?\n\nYou need to set your custom fonts in your config file located in\nfoobar\\profile\\georgia-reborn\\configs\\georgia-reborn-config.jsonc\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - pref.customThemeFonts = confirmed; - }); - window.Reload(); - }); - themeFontMenu.appendTo(settingsMenu); - - // * THEME IMAGES * // - const themeImagesMenu = new Menu('Theme images'); - themeImagesMenu.addToggleItem('Use custom preloader logo', pref, 'customPreloaderLogo', () => { - if (!pref.customPreloaderLogo) return window.Reload(); - const customLogoPath = `${fb.ProfilePath}georgia-reborn\\images\\custom\\logo\\_4K-custom-logo.png and _custom-logo.png`; - const msg = `The custom logo placeholder can be replaced\nwith a new logo:\n\n${customLogoPath}\n\nRecommended logo dimensions are:\n500x500 pixels for 4K\n250x250 pixels for HD\n\n\n`; - ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); - window.Reload(); - }); - themeImagesMenu.addToggleItem('Use custom theme images', pref, 'customThemeImages', () => { - if (!pref.customThemeImages) return window.Reload(); - const customImagesPath = `${fb.ProfilePath}georgia-reborn\\images\\custom\\`; - const msg = `All theme images can be safely replaced\nwith new custom ones:\n\n${customImagesPath}\n\nPlease ensure all images have the same names\nas the original ones, which are located in the\nparent directory.\n\n\n`; - ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); - window.Reload(); - }); - themeImagesMenu.appendTo(settingsMenu); - - // * THEME CACHE * // - const themeCacheMenu = new Menu('Theme cache'); - const themeCacheLibraryMenu = new Menu('Library'); - themeCacheLibraryMenu.addToggleItem('Image disk cache enabled', ppt, 'albumArtDiskCache'); - themeCacheLibraryMenu.addToggleItem('Preload images in disk cache', ppt, 'albumArtPreLoad'); - themeCacheLibraryMenu.addToggleItem('Use custom library directory', pref, 'customLibraryDir', () => { - if (pref.customLibraryDir) inputBox('customLibraryDir'); - window.Reload(); - }); - themeCacheLibraryMenu.addSeparator(); - themeCacheLibraryMenu.addItem('Open library cache directory', false, () => { - const cacheDir = pref.customLibraryDir ? globals.customLibraryDir : `${fb.ProfilePath}cache\\library\\library-tree-cache`; - try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} - OpenExplorer(`explorer /open, "${cacheDir}"`, false); - }); - themeCacheLibraryMenu.addItem('Delete library cache', false, () => { - const msg = 'Do you want to delete the library cache?\n\nThis will permanently delete cached library album art thumbnails.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) DeleteLibraryCache(); - }); - }); - themeCacheLibraryMenu.addSeparator(); - themeCacheLibraryMenu.addToggleItem('Auto-delete library cache on startup', pref, 'libraryAutoDelete', () => { - const msg = 'Do you want to set auto-delete for library cache?\n\nThis will always auto-delete cached library album art thumbnails on startup.\n\nContinue?\n\n\n'; - if (pref.libraryAutoDelete) { - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - pref.libraryAutoDelete = confirmed; + + /** + * Top menu > Options > Details. + * @param {Menu} menu - Creates the Details panel menu via a new Menu instance. + * @param {boolean} context_menu - Appends Details panel options to context menu. + * @protected + */ + detailsOptions(menu, context_menu) { + const detailsMenu = context_menu ? menu : new Menu('Details'); + + if (grSet.layout === 'default') { + const discArtMenu = new Menu('Disc art'); + const displayDiscArtMenu = new Menu('Disc art placeholder'); + + // * DISC ART PLACEHOLDER * // + displayDiscArtMenu.addToggleItem('Show placeholder if no disc art found', grSet, 'showDiscArtStub', () => { + grSet.noDiscArtStub = false; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }, !grSet.displayDiscArt); + displayDiscArtMenu.addSeparator(); + displayDiscArtMenu.addToggleItem('No placeholder', grSet, 'noDiscArtStub', () => { + grSet.showDiscArtStub = false; + grm.ui.discArt = grm.ui.disposeDiscArt(grm.ui.discArt); + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArray = []; + grm.ui.discArtArrayCover = []; + if (!grSet.noDiscArtStub) grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }, !grSet.displayDiscArt); + displayDiscArtMenu.addSeparator(); + displayDiscArtMenu.addRadioItems(['CD - Album cover', 'CD - White', 'CD - Black', 'CD - Blank', 'CD - Transparent'], + grSet.discArtStub, ['cdAlbumCover', 'cdWhite', 'cdBlack', 'cdBlank', 'cdTrans'], (discArt) => { + grSet.discArtStub = discArt; + grSet.noDiscArtStub = false; + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArrayCover = []; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }, !grSet.displayDiscArt); + displayDiscArtMenu.addSeparator(); + displayDiscArtMenu.addRadioItems(['Vinyl - Album cover', 'Vinyl - White', 'Vinyl - Void', 'Vinyl - Cold fusion', 'Vinyl - Ring of fire', 'Vinyl - Maple', 'Vinyl - Black', 'Vinyl - Black hole', 'Vinyl - Ebony', 'Vinyl - Transparent'], + grSet.discArtStub, ['vinylAlbumCover', 'vinylWhite', 'vinylVoid', 'vinylColdFusion', 'vinylRingOfFire', 'vinylMaple', 'vinylBlack', 'vinylBlackHole', 'vinylEbony', 'vinylTrans'], (discArt) => { + grSet.discArtStub = discArt; + grSet.noDiscArtStub = false; + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArrayCover = []; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + }, !grSet.displayDiscArt); + + // * DISC ART CUSTOM PLACEHOLDERS * // + const customDiscArtLabels = []; + const customDiscArtValues = []; + for (const key in grCfg.customDiscArtStub) { + if (Object.prototype.hasOwnProperty.call(grCfg.customDiscArtStub, key) && key.includes('Name')) { + const num = key.match(/\d+$/)[0]; // Extract the number from the key (e.g., "01" from "cdName01") + const cdStubKey = `cdStub${num}`; + const vinylStubKey = `vinylStub${num}`; + if (key.startsWith('cdName') && grCfg.customDiscArtStub[cdStubKey]) { + customDiscArtLabels.push(grCfg.customDiscArtStub[key]); + customDiscArtValues.push(grCfg.customDiscArtStub[cdStubKey].replace('.png', '')); + } + else if (key.startsWith('vinylName') && grCfg.customDiscArtStub[vinylStubKey]) { + customDiscArtLabels.push(grCfg.customDiscArtStub[key]); + customDiscArtValues.push(grCfg.customDiscArtStub[vinylStubKey].replace('.png', '')); + } + } + } + if (customDiscArtValues.length) displayDiscArtMenu.addSeparator(); + displayDiscArtMenu.addRadioItems(customDiscArtLabels, grSet.discArtStub, customDiscArtValues, (discArt) => { + grSet.discArtStub = discArt; + grSet.noDiscArtStub = false; + grPath.discArtCustomStub = `${fb.ProfilePath}georgia-reborn\\images\\custom\\discart\\${grSet.discArtStub}.png`; + grm.ui.discArtCover = grm.ui.disposeDiscArt(grm.ui.discArtCover); + grm.ui.discArtArrayCover = []; + grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + RepaintWindow(); + if (!IsFile(grPath.discArtCustomStub)) { + const msg = `The custom disc art placeholder was not found in:\n${grPath.discArtCustomStub}\n\nBe sure that image exist and has the correct filename\nin the "customDiscArtStub" section of the\ncustom config file:\n${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc\n\n\n`; + ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + } + }, !grSet.displayDiscArt); + displayDiscArtMenu.appendTo(discArtMenu); + + // * DISC ART OPTIONS * // + discArtMenu.addToggleItem(`Display disc art if found (${grCfg.settings.discArtBasename}.png, ${grCfg.settings.discArtBasename}2.png, vinylA.png, etc.)`, grSet, 'displayDiscArt', () => { + if (fb.IsPlaying) grm.ui.fetchNewArtwork(fb.GetNowPlaying()); + grm.ui.lastLeftEdge = 0; // resize labels + grm.ui.resizeArtwork(true); + RepaintWindow(); }); + + discArtMenu.addToggleItem('Display disc art above cover', grSet, 'discArtOnTop', () => { + grSet.detailsAlbumArtDiscAreaOpacity = 255; + RepaintWindow(); + }, !grSet.displayDiscArt); + discArtMenu.addToggleItem('Filter cd/disc/vinyl .jpgs from artwork', grSet, 'filterDiscJpgsFromAlbumArt'); + discArtMenu.addSeparator(); + discArtMenu.addToggleItem('Spin disc art while songs play (increases memory and CPU)', grSet, 'spinDiscArt', () => { + if (grSet.spinDiscArt) { + grm.ui.setDiscArtRotationTimer(); + } else { + clearInterval(grm.ui.discArtRotationTimer); + grm.ui.discArtArray = []; + grm.ui.discArtArrayCover = []; + } + }); + discArtMenu.createRadioSubMenu('# Rotation images (memory usage/rotational speed)', [' 36 (10 degrees)', ' 45 (8 degrees)', ' 60 (6 degrees)', ' 72 (5 degrees) (default)', ' 90 (4 degrees)', '120 (3 degrees)', '180 (2 degrees)'], grSet.spinDiscArtImageCount, [36, 45, 60, 72, 90, 120, 180], (count) => { + grSet.spinDiscArtImageCount = count; + grm.ui.discArtRotationIndex = 0; + grm.ui.discArtRotationIndexCover = 0; + grm.ui.discArtArray = []; + grm.ui.discArtArrayCover = []; + RepaintWindow(); + }, !grSet.spinDiscArt); + discArtMenu.createRadioSubMenu('Spinning disc art redraw speed', ['250ms (very slow CPU)', '200ms', '150ms', '125ms', '100ms', ' 75ms (default)', ' 50ms', ' 40ms', ' 30ms', ' 20ms', ' 10ms (very fast CPU)'], grSet.spinDiscArtRedrawInterval, [250, 200, 150, 125, 100, 75, 50, 40, 30, 20, 10], interval => { + grSet.spinDiscArtRedrawInterval = interval; + grm.ui.setDiscArtRotationTimer(); + }, !grSet.spinDiscArt); + discArtMenu.addSeparator(); + discArtMenu.addToggleItem('Rotate disc art as tracks change', grSet, 'rotateDiscArt', () => { RepaintWindow(); }, !grSet.displayDiscArt || grSet.spinDiscArt); + discArtMenu.createRadioSubMenu('Disc art rotation amount', ['2 degrees', '3 degrees', '4 degrees', '5 degrees'], parseInt(grSet.rotationAmt), [2, 3, 4, 5], (rot) => { + grSet.rotationAmt = rot; + grm.ui.createDiscArtRotation(); + RepaintWindow(); + }, !grSet.rotateDiscArt || grSet.spinDiscArt); + discArtMenu.appendTo(detailsMenu); + + discArtMenu.createRadioSubMenu('Disc art display amount', ['Auto (Needs enough width)', '50% (Needs enough width, default)', '45%', '40%', '35%', '30%', '25%', '20%', '15%', '10%'], grSet.discArtDisplayAmount, [1, 0.5, 0.455, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1], amount => { + grSet.discArtDisplayAmount = amount; + grm.ui.resizeArtwork(true); + RepaintWindow(); + }); + + // * DISC ART ALBUM ART * // + const albumArtOpacityMenu = new Menu('Album art'); + albumArtOpacityMenu.createRadioSubMenu('Full artwork opacity (fast CPU needed when disc art spinning)', ['100%', '90%', '80%', '70%', '60%', '50%', '40%', '30%', '20%', '10%'], grSet.detailsAlbumArtOpacity, [255, 230, 204, 178, 153, 128, 102, 76, 51, 25], value => { + grSet.detailsAlbumArtOpacity = value; + grSet.detailsAlbumArtDiscAreaOpacity = 255; + grSet.discArtOnTop = false; + RepaintWindow(); + }); + albumArtOpacityMenu.createRadioSubMenu('Disc area opacity (very fast CPU needed when disc art spinning)', ['100%', '90%', '80%', '70%', '60%', '50%', '40%', '30%', '20%', '10%'], grSet.detailsAlbumArtDiscAreaOpacity, [255, 230, 204, 178, 153, 128, 102, 76, 51, 25], value => { + grSet.detailsAlbumArtDiscAreaOpacity = value; + grSet.detailsAlbumArtOpacity = 255; + grSet.discArtOnTop = false; + RepaintWindow(); + }); + albumArtOpacityMenu.appendTo(detailsMenu); } - }); - themeCacheLibraryMenu.appendTo(themeCacheMenu); - - const themeCacheBiographyMenu = new Menu('Biography'); - themeCacheBiographyMenu.addToggleItem('Use custom biography directory', pref, 'customBiographyDir', () => { - if (pref.customBiographyDir) { - inputBox('customBiographyDir'); - const bioCfg = new SettingsBio(); - bioCfg.resetCfg(); - } - window.Reload(); - }); - themeCacheBiographyMenu.addSeparator(); - themeCacheBiographyMenu.addItem('Open biography cache directory', false, () => { - const cacheDir = pref.customBiographyDir ? globals.customBiographyDir : `${fb.ProfilePath}cache\\biography\\biography-cache`; - try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} - OpenExplorer(`explorer /open, "${cacheDir}"`, false); - }); - themeCacheBiographyMenu.addItem('Delete biography cache', false, () => { - const msg = 'Do you want to delete the biography cache?\n\nThis will permanently delete downloaded biography images and text files\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) DeleteBiographyCache(); - }); - }); - themeCacheBiographyMenu.addSeparator(); - themeCacheBiographyMenu.addToggleItem('Auto-delete biography cache on startup', pref, 'biographyAutoDelete', () => { - const msg = 'Do you want to set auto-delete for biography cache?\n\nThis will always auto-delete downloaded biography images\nand text on startup\n\nContinue?\n\n\n'; - if (pref.biographyAutoDelete) { - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - pref.biographyAutoDelete = confirmed; + + // * METADATA GRID MENU * // + const detailsMetadataGridMenu = new Menu('Metadata grid'); + const detailsShowArtistMenu = new Menu('Show artist'); + detailsShowArtistMenu.addToggleItem('Default', grSet, 'showGridArtist_default', () => { + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowArtistMenu.addToggleItem('Artwork', grSet, 'showGridArtist_artwork', () => { + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowArtistMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW TRACK NUMBER IN DETAILS * // + const detailsShowTrackNumberMenu = new Menu('Show track number'); + detailsShowTrackNumberMenu.addToggleItem('Default', grSet, 'showGridTrackNum_default', () => { + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowTrackNumberMenu.addToggleItem('Artwork', grSet, 'showGridTrackNum_artwork', () => { + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowTrackNumberMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW SONG TITLE IN DETAILS * // + const detailsShowTitleMenu = new Menu('Show song title'); + detailsShowTitleMenu.addToggleItem('Default', grSet, 'showGridTitle_default', () => { + grSet.showGridTrackNum_default = true; + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowTitleMenu.addToggleItem('Artwork', grSet, 'showGridTitle_artwork', () => { + grSet.showGridTrackNum_artwork = true; + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowTitleMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW PLAYING PLAYLIST IN DETAILS * // + const detailsShowPlaylingPlaylistMenu = new Menu('Show playing playlist'); + detailsShowPlaylingPlaylistMenu.addToggleItem('Enable', grSet, 'showGridPlayingPlaylist', () => { + on_playback_new_track(fb.GetNowPlaying()); + grm.ui.createFonts(); + RepaintWindow(); + }); + detailsShowPlaylingPlaylistMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW TIMELINE IN DETAILS * // + const detailsShowTimelineMenu = new Menu('Show timeline'); + detailsShowTimelineMenu.addToggleItem('Default', grSet, 'showGridTimeline_default', () => { + RepaintWindow(); + }); + detailsShowTimelineMenu.addToggleItem('Artwork', grSet, 'showGridTimeline_artwork', () => { + RepaintWindow(); + }); + detailsShowTimelineMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW ARTIST COUNTRY FLAG IN DETAILS * // + const detailsShowArtistFlagsMenu = new Menu('Show artist country flags'); + detailsShowArtistFlagsMenu.addToggleItem('Default', grSet, 'showGridArtistFlags_default', () => { + grm.ui.loadCountryFlags(); + RepaintWindow(); + }); + detailsShowArtistFlagsMenu.addToggleItem('Artwork', grSet, 'showGridArtistFlags_artwork', () => { + grm.ui.loadCountryFlags(); + RepaintWindow(); + }); + detailsShowArtistFlagsMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW RELEASE COUNTRY FLAG IN DETAILS * // + const detailsShowReleaseFlagsMenu = new Menu('Show release country flags'); + detailsShowReleaseFlagsMenu.createRadioSubMenu('Default', ['Disabled', 'Logo', 'Text + Logo'], grSet.showGridReleaseFlags_default, [false, 'logo', 'textlogo'], type => { + grSet.showGridReleaseFlags_default = type; + grm.ui.loadReleaseCountryFlag(); + RepaintWindow(); + }); + detailsShowReleaseFlagsMenu.createRadioSubMenu('Artwork', ['Disabled', 'Logo', 'Text + Logo'], grSet.showGridReleaseFlags_artwork, [false, 'logo', 'textlogo'], type => { + grSet.showGridReleaseFlags_artwork = type; + grm.ui.loadReleaseCountryFlag(); + RepaintWindow(); + }); + detailsShowReleaseFlagsMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW CODEC LOGO IN DETAILS * // + const detailsShowCodecLogoMenu = new Menu('Show codec logo'); + detailsShowCodecLogoMenu.createRadioSubMenu('Default', ['Disabled', 'Logo', 'Text + Logo'], grSet.showGridCodecLogo_default, [false, 'logo', 'textlogo'], type => { + grSet.showGridCodecLogo_default = type; + RepaintWindow(); + }); + detailsShowCodecLogoMenu.createRadioSubMenu('Artwork', ['Disabled', 'Logo', 'Text + Logo'], grSet.showGridCodecLogo_artwork, [false, 'logo', 'textlogo'], type => { + grSet.showGridCodecLogo_artwork = type; + RepaintWindow(); + }); + detailsShowCodecLogoMenu.appendTo(detailsMetadataGridMenu); + + // * SHOW CHANNEL LOGO IN DETAILS * // + const detailsShowChannelLogoMenu = new Menu('Show channel logo'); + detailsShowChannelLogoMenu.createRadioSubMenu('Default', ['Disabled', 'Logo', 'Text + Logo'], grSet.showGridChannelLogo_default, [false, 'logo', 'textlogo'], type => { + grSet.showGridChannelLogo_default = type; + RepaintWindow(); + }); + detailsShowChannelLogoMenu.createRadioSubMenu('Artwork', ['Disabled', 'Logo', 'Text + Logo'], grSet.showGridChannelLogo_artwork, [false, 'logo', 'textlogo'], type => { + grSet.showGridChannelLogo_artwork = type; + RepaintWindow(); + }); + detailsShowChannelLogoMenu.appendTo(detailsMetadataGridMenu); + + detailsMetadataGridMenu.addSeparator(); + detailsMetadataGridMenu.addToggleItem('Auto-hide full metadata on small player', grSet, 'autoHideGridMetadata', () => RepaintWindow()); + + // * EDIT METADATA GRID IN DETAILS * // + if (fb.IsPlaying) { + detailsMetadataGridMenu.addSeparator(); + detailsMetadataGridMenu.addItem('Edit metadata grid', false, () => { + if (grSet.layout === 'default') { + grm.ui.displayMetadataGridMenu = !grm.ui.displayMetadataGridMenu; + if (!grm.ui.displayDetails) { + grm.ui.displayDetails = true; + grm.ui.displayPlaylist = false; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + } + grm.gridMenu.initMetadataGridMenu(1); + RepaintWindow(); + } else { + fb.ShowPopupMessage(`Metadata grid can only be live edited in default layout:\nOptions > Layout > Default\n\nYou could manually edit your config file while reloading to take effect:\n${grCfg.configPath}\n`, 'Metadata grid live editing'); + } }); } - }); - themeCacheBiographyMenu.appendTo(themeCacheMenu); - - const themeCacheLyricsMenu = new Menu('Lyrics'); - themeCacheLyricsMenu.addToggleItem('Use custom lyrics directory', pref, 'customLyricsDir', () => { - if (pref.customLyricsDir) inputBox('customLyricsDir'); - window.Reload(); - }); - themeCacheLyricsMenu.addSeparator(); - themeCacheLyricsMenu.addItem('Open lyrics directory', false, () => { - const cacheDir = pref.customLyricsDir ? globals.customLyricsDir : `${fb.ProfilePath}cache\\lyrics`; - try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} - OpenExplorer(`explorer /open, "${cacheDir}"`, false); - }); - themeCacheLyricsMenu.addItem('Delete lyrics', false, () => { - const msg = 'Do you want to delete all lyrics?\n\nThis will permanently delete downloaded lyrics.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) DeleteLyrics(); - }); - }); - themeCacheLyricsMenu.addSeparator(); - themeCacheLyricsMenu.addToggleItem('Auto-delete lyrics on startup', pref, 'lyricsAutoDelete', () => { - const msg = 'Do you want to set auto-delete for lyrics?\n\nThis will always auto-delete downloaded lyrics on startup.\n\nContinue?\n\n\n'; - if (pref.lyricsAutoDelete) { - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - pref.lyricsAutoDelete = confirmed; + detailsMetadataGridMenu.appendTo(detailsMenu); + + if (grSet.layout === 'default') { + const detailsBackgroundMenu = new Menu('Background'); + // * SHOW FULL BACKGROUND WHEN NO DISC ART IN DETAILS * // + detailsBackgroundMenu.addToggleItem('Show full background when no disc art', grSet, 'noDiscArtBg', () => { + if (grSet.labelArtOnBg) { + grSet.labelArtOnBg = false; + } + RepaintWindow(); }); + // * SHOW LABEL ART ON BACKGROUND IN DETAILS * // + detailsBackgroundMenu.addToggleItem('Show label art on background', grSet, 'labelArtOnBg', () => RepaintWindow(), grSet.noDiscArtBg); + detailsBackgroundMenu.appendTo(detailsMenu); } - }); - themeCacheLyricsMenu.appendTo(themeCacheMenu); - - const themeCacheWaveformBarMenu = new Menu('Waveform bar'); - themeCacheWaveformBarMenu.addToggleItem('Use custom waveform bar directory', pref, 'customWaveformBarDir', () => { - if (pref.customWaveformBarDir) inputBox('customWaveformBarDir'); - window.Reload(); - }); - themeCacheWaveformBarMenu.addSeparator(); - themeCacheWaveformBarMenu.addItem('Open waveform bar cache directory', false, () => { - const cacheDir = pref.customWaveformBarDir ? globals.customWaveformBarDir : `${fb.ProfilePath}cache\\waveform`; - try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} - OpenExplorer(`explorer /open, "${cacheDir}"`, false); - }); - themeCacheWaveformBarMenu.addItem('Delete waveform bar cache', false, () => { - const msg = 'Do you want to delete all waveform bar cache?\n\nThis will permanently delete analyzed files.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) DeleteWaveformBarCache(); - }); - }); - - themeCacheWaveformBarMenu.addToggleItem('Auto-delete waveform bar cache on startup', pref, 'waveformBarAutoDelete', () => { - const msg = 'Do you want to set auto-delete for waveform bar?\n\nThis will always auto-delete waveform bar cache on startup.\n\nContinue?\n\n\n'; - if (pref.waveformBarAutoDelete) { - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - pref.waveformBarAutoDelete = confirmed; + + if (!context_menu) detailsMenu.appendTo(menu); + } + + /** + * Top menu > Options > Library. + * @param {Menu} menu - Creates the Library panel menu via a new Menu instance. + * @param {boolean} context_menu - Appends Library options to context menu. + * @protected + */ + libraryOptions(menu, context_menu) { + const libraryMenu = context_menu ? menu : new Menu('Library'); + + // * LAYOUT * // + if (grSet.layout === 'default') { + const libraryLayoutMenu = new Menu('Layout'); + libraryLayoutMenu.addRadioItems(['Normal', 'Full', 'Split'], grSet.libraryLayout, ['normal', 'full', 'split'], (width) => { + grSet.libraryLayout = width; + if (!grm.ui.displayLibrary) grm.ui.displayLibrary = true; grm.ui.displayPlaylist = false; grm.ui.displayBiography = false; + if (grm.ui.displayLyrics) grm.ui.displayLyrics = false; + grm.ui.displayPlaylist = grSet.libraryLayout === 'split'; + grm.ui.resizeArtwork(true); + grm.ui.initLibraryLayout(); + grm.button.initButtonState(); + RepaintWindow(); + }); + libraryLayoutMenu.addSeparator(); + libraryLayoutMenu.addToggleItem('Use full preset', grSet, 'libraryLayoutFullPreset', () => { RepaintWindow(); }); + libraryLayoutMenu.addSeparator(); + libraryLayoutMenu.addToggleItem('Use split preset (collapse)', grSet, 'libraryLayoutSplitPreset', () => { + grSet.libraryLayoutSplitPreset2 = false; + grSet.libraryLayoutSplitPreset3 = false; + grSet.libraryLayoutSplitPreset4 = false; + grm.ui.initLibraryLayout(); + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + }); + libraryLayoutMenu.addToggleItem('Use split preset (text)', grSet, 'libraryLayoutSplitPreset2', () => { + grSet.libraryLayoutSplitPreset = false; + grSet.libraryLayoutSplitPreset3 = false; + grSet.libraryLayoutSplitPreset4 = false; + grm.ui.initLibraryLayout(); + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + }); + libraryLayoutMenu.addToggleItem('Use split preset (art grid)', grSet, 'libraryLayoutSplitPreset3', () => { + grSet.libraryLayoutSplitPreset = false; + grSet.libraryLayoutSplitPreset2 = false; + grSet.libraryLayoutSplitPreset4 = false; + grm.ui.initLibraryLayout(); + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + }); + libraryLayoutMenu.addToggleItem('Use split preset (art header)', grSet, 'libraryLayoutSplitPreset4', () => { + grSet.libraryLayoutSplitPreset = false; + grSet.libraryLayoutSplitPreset2 = false; + grSet.libraryLayoutSplitPreset3 = false; + grm.ui.initLibraryLayout(); + grm.ui.initPlaylist(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); }); + libraryLayoutMenu.appendTo(libraryMenu); } - }); - themeCacheWaveformBarMenu.appendTo(themeCacheMenu); - themeCacheMenu.appendTo(settingsMenu); - - // * THEME BACKUP * // - const themeBackupMenu = new Menu('Theme backup'); - themeBackupMenu.addItem('Make backup', false, () => { - const msg = `Do you want to make a backup of the theme?\n\nThis will create a backup in ${fb.ProfilePath}backup\n\nOn new fb2k installation, you can copy/paste and replace it with ${fb.ProfilePath}\n\nIf a backup already exist, you can use\nOptions > Settings > Theme backup > Restore backup\n\nContinue?\n\n\n`; - const msgFb = `You can find the Georgia-ReBORN theme backup in ${fb.ProfilePath}backup\n\nOn new fb2k installation, you can copy/paste and replace it with ${fb.ProfilePath}\n\nIf a backup already exist, you can use\nOptions > Settings > Theme backup > Restore backup`; - ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) ManageBackup(true); - }); - }); - - themeBackupMenu.addItem('Restore backup', false, () => { - const msg = `Do you want to restore your backup of the theme?\n\n>>> WARNING <<<\n\nThis will restore your backup from ${fb.ProfilePath}\n\nChanges and modifications since your last backup\n(new theme settings, new playlists and play statistics)\nwill be lost!\n\nIt is recommended to make a new backup\nbefore you restore.\n\nContinue?\n\n\n`; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (confirmed) ManageBackup(false, true); - }); - }); - themeBackupMenu.appendTo(settingsMenu); - - // * THEME CONFIGURATION * // - const themeConfigMenu = new Menu('Theme configuration'); - themeConfigMenu.addItem('Save settings to config file', false, () => { - const msg = 'Do you want to save all current theme settings?\n\nThis will overwrite all settings from the top menu "Options"\nin the georgia-reborn-config.jsonc file.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - const start = async () => { - await setThemeSettings(true); - await window.Reload(); - }; - start(); - console.log(`\n>>> Georgia-ReBORN theme settings have been successfully saved in ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc <<<\n\n`); - }); - }); - themeConfigMenu.addItem('Load settings from config file', false, () => { - const msg = 'Do you want to load all theme settings\nfrom the georgia-reborn-config.jsonc file?\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - const start = async () => { - pref.customThemeSettings = true; - await setThemeSettings(); - await window.Reload(); - }; - start(); - console.log(`\n>>> Georgia-ReBORN theme settings have been successfully loaded from ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc <<<\n\n`); - }); - }); - themeConfigMenu.addSeparator(); - themeConfigMenu.addItem('Load default settings', false, () => { - const msg = 'Do you want to load default theme settings?\n\nThis will not overwrite the georgia-reborn-config.jsonc file,\nbut you should probably first save your settings.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - const start = async () => { - pref.customThemeSettings = false; - await setThemeSettings(); - await display.autoDetectRes(); - await window.Reload(); + + // * DESIGN * // + libraryMenu.createRadioSubMenu('Design', ['Georgia-ReBORN', 'Traditional', 'Modern', 'Ultra-modern', 'Clean', 'List view', 'Covers (labels right)', 'Covers (labels bottom)', 'Covers (labels blend)', 'Flow mode'], grSet.libraryDesign, + ['reborn', 'traditional', 'modern', 'ultraModern', 'clean', 'facet', 'coversLabelsRight', 'coversLabelsBottom', 'coversLabelsBlend', 'flowMode'], (design) => { + grSet.libraryDesign = design; + grm.ui.setLibraryDesign(); + }); + + // * THEME * // + libraryMenu.createRadioSubMenu('Theme', ['Georgia-ReBORN', 'Dark', 'Blend', 'Light', 'Random', 'Cover'], grSet.libraryTheme, [0, 1, 2, 3, 4, 5], (theme) => { + libSet.theme = grSet.libraryTheme = theme; + grm.ui.initTheme(); + lib.call.on_colours_changed(); + lib.panel.updateProp(1); + grm.theme.themeColorAdjustments(); + }); + + // * ALBUM ART * // + const libraryAlbumArtMenu = new Menu('Album art'); + const libraryThumbnailSizeMenu = new Menu('Thumbnail size'); + libraryThumbnailSizeMenu.addRadioItems(['Auto (default)', 'Playlist', 'Mini', 'Small', 'Regular', 'Medium', 'Large', 'XL', 'XXL', 'MAX'], grSet.libraryThumbnailSize, ['auto', 'playlist', 0, 1, 2, 3, 4, 5, 6, 7], (thumbnailSize) => { + grSet.libraryThumbnailSizeSaved = libSet.thumbNailSize = grSet.libraryThumbnailSize = thumbnailSize; + grm.ui.setLibrarySize(); + RepaintWindow(); + }); + libraryThumbnailSizeMenu.appendTo(libraryAlbumArtMenu); + + const libraryThumbnailBorderMenu = new Menu('Thumbnail border'); + libraryThumbnailBorderMenu.addRadioItems(['None', 'Border', 'Shadow'], grSet.libraryThumbnailBorder, ['none', 'border', 'shadow'], (type) => { + grSet.libraryThumbnailBorder = type; + libSet.albumArtDropShadow = grSet.libraryThumbnailBorder === 'shadow'; + lib.panel.updateProp(1); + }); + libraryThumbnailBorderMenu.appendTo(libraryAlbumArtMenu); + + if (!libSet.albumArtShow) { + libraryAlbumArtMenu.addToggleItem('Activate option or change design to album art', libSet, 'albumArtShow', () => { + if (grSet.libraryDesign === 'flowMode') grSet.libraryLayout = 'full'; + lib.lib.logTree(); + lib.pop.clearTree(); + libSet.toggle('albumArtShow'); + lib.panel.imgView = libSet.albumArtShow = true; + lib.men.loadView(false, !lib.panel.imgView ? (libSet.artTreeSameView ? libSet.viewBy : libSet.treeViewBy) : (libSet.artTreeSameView ? libSet.viewBy : libSet.albumArtViewBy), lib.pop.sel_items[0]); + grm.ui.setLibrarySize(); + grm.ui.displayPlaylist = false; + grm.ui.displayLibrary = true; + grm.ui.btn.library.enabled = true; + grm.ui.btn.library.changeState(ButtonState.Down); + lib.panel.updateProp(1); + }, libSet.albumArtShow); + libraryAlbumArtMenu.addSeparator(); + } + + const libraryAlbumArtOverlay = new Menu('Overlay'); + libraryAlbumArtOverlay.addRadioItems(['None', 'Track count', 'Year'], libSet.itemOverlayType, [0, 1, 2], (type) => { + libSet.itemOverlayType = type; + lib.panel.updateProp(1); + }); + libraryAlbumArtOverlay.appendTo(libraryAlbumArtMenu); + + const libraryAlbumArtIndex = new Menu('Index'); + libraryAlbumArtIndex.addToggleItem('Show on scrollbar drag', libSet, 'albumArtLetter', () => { lib.panel.updateProp(1); }); + libraryAlbumArtIndex.addSeparator(); + libraryAlbumArtIndex.addRadioItems(['Artist', 'Alphabet'], libSet.albumArtLetterNo, [0, 1], (type) => { + libSet.albumArtLetterNo = type; + lib.panel.updateProp(1); + }); + libraryAlbumArtIndex.appendTo(libraryAlbumArtMenu); + libraryAlbumArtMenu.appendTo(libraryMenu); + + const libraryViewMenu = new Menu('View'); + libraryViewMenu.addRadioItems(['Front (default)', 'Back', 'Disc', 'Icon', 'Artist'], libSet.artId, [0, 1, 2, 3, 4], (view) => { + libSet.artId = view; + lib.men.setAlbumart(view); + lib.panel.updateProp(1); + }); + libraryViewMenu.addSeparator(); + libraryViewMenu.addRadioItems(['Group: auto', 'Group: top level', 'Group: two levels'], libSet.albumArtGrpLevel, [0, 1, 2], (view) => { + libSet.albumArtGrpLevel = view; + lib.men.setAlbumart(view - 5); + lib.panel.updateProp(1); + }); + libraryViewMenu.appendTo(libraryAlbumArtMenu); + + const libraryImageMenu = new Menu('Image'); + libraryImageMenu.createRadioSubMenu('Front', ['Regular', 'Auto-fill (default)', 'Circular'], libSet.imgStyleFront, [0, 1, 2], (style) => { + libSet.imgStyleFront = style; + lib.panel.updateProp(1); + }); + libraryImageMenu.createRadioSubMenu('Back', ['Regular', 'Auto-fill (default)', 'Circular'], libSet.imgStyleBack, [0, 1, 2], (style) => { + libSet.imgStyleBack = style; + lib.panel.updateProp(1); + }); + libraryImageMenu.createRadioSubMenu('Disc', ['Regular', 'Auto-fill (default)', 'Circular'], libSet.imgStyleDisc, [0, 1, 2], (style) => { + libSet.imgStyleDisc = style; + lib.panel.updateProp(1); + }); + libraryImageMenu.createRadioSubMenu('Icon', ['Regular', 'Auto-fill (default)', 'Circular'], libSet.imgStyleIcon, [0, 1, 2], (style) => { + libSet.imgStyleIcon = style; + lib.panel.updateProp(1); + }); + libraryImageMenu.createRadioSubMenu('Artist', ['Regular', 'Auto-fill (default)', 'Circular'], libSet.imgStyleArtist, [0, 1, 2], (style) => { + libSet.imgStyleArtist = style; + lib.panel.updateProp(1); + }); + libraryImageMenu.appendTo(libraryAlbumArtMenu); + + const libraryLabelsMenu = new Menu('Labels'); + libraryLabelsMenu.addRadioItems(['Bottom (default)', 'Right', 'Blend', 'Dark', 'None'], libSet.albumArtLabelType, [1, 2, 3, 4, 0], (style) => { + grSet.savedAlbumArtLabelType = libSet.albumArtLabelType = style; + lib.panel.updateProp(1); + }); + libraryLabelsMenu.addSeparator(); + libraryLabelsMenu.addToggleItem('Flip', libSet, 'albumArtFlipLabels', () => { lib.panel.updateProp(1); }); + libraryLabelsMenu.appendTo(libraryAlbumArtMenu); + + // * CONTROLS * // + const libraryControlsMenu = new Menu('Controls'); + libraryControlsMenu.createRadioSubMenu('Action mode', ['Default', 'Browser', 'Player'], libSet.actionMode, [0, 1, 2], (mode) => { + libSet.actionMode = mode; + if (mode === 1) { + const msg = 'Do you want to enable library browser mode?\n\nThis will act like a file browser to quickly see the content of the album. It is not recommended for new users\nwho don\'t know how the library works.\n\nContinue?\n\n\n'; + const msgFb = 'Library browser mode enabled:\n\nThis will act like a file browser to quickly see the content of the album.\nIt is not recommended for new users who don\'t know how the library works.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + libSet.actionMode = 0; + return; + } + grSet.libraryLayoutSplitPreset = false; + grSet.libraryLayoutSplitPreset2 = false; + grSet.libraryLayoutSplitPreset3 = false; + grSet.libraryLayoutSplitPreset4 = false; + grSet.libraryLayout = 'split'; + lib.panel.imgView = libSet.albumArtShow = true; + lib.lib.logTree(); + lib.pop.clearTree(); + lib.men.loadView(false, !lib.panel.imgView ? (libSet.artTreeSameView ? libSet.viewBy : libSet.treeViewBy) : (libSet.artTreeSameView ? libSet.viewBy : libSet.albumArtViewBy), lib.pop.sel_items[0]); + grm.ui.setLibrarySize(); + grm.theme.initLibraryColors(); + grm.theme.themeColorAdjustments(); + plSet.show_header = true; + plSet.auto_collapse = false; + grm.ui.displayPlaylist = true; + grm.ui.displayLibrary = true; + }); + grm.ui.updatePlaylist(); + RepaintWindow(); + } + else if (mode === 2) { + const msg = 'Do you want to enable library player mode?\n\nThis will act like a playlist and will not automatically add content to the playlist. It is recommended for new users\nwho don\'t know how the library works.\n\nContinue?\n\n\n'; + const msgFb = 'Library player mode enabled:\n\nThis will act like a like a playlist and will not automatically add content to the playlist.\nIt is recommended for new users who don\'t know how the library works.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + libSet.actionMode = 0; + } + }); + RepaintWindow(); }; - start(); - console.log('\n>>> Default Georgia-ReBORN theme settings have been successfully loaded <<<\n\n'); - }); - }); - themeConfigMenu.addSeparator(); - themeConfigMenu.addItem('Edit main configuration file', false, () => { - try { - OpenFile(`${config.getPath()}`); - } catch (e) { - OpenExplorer(`explorer /select, "${config.getPath()}"`, false); - } - }); - themeConfigMenu.addItem('Edit custom configuration file', false, () => { - try { - OpenFile(`${configCustom.getPath()}`); - } catch (e) { - OpenExplorer(`explorer /select, "${configCustom.getPath()}"`, false); - } - }); - themeConfigMenu.addSeparator(); - themeConfigMenu.addItem('Reset main configuration file', false, () => { - const msg = 'Do you want to reset the config file to default?\n\n!!! WARNING !!!\n\nThis will set all settings to default.\nYou should probably make a backup first.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - try { // Needed to prevent crash when there is no config file - pref.customThemeSettings = false; - config.resetConfiguration(); - setThemeSettings(); - display.layoutDefault(); - console.log(`\n>>> Georgia-ReBORN's ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc file has been successfully reset to default. <<<\n\n`); - } catch (e) { window.Reload(); } - }); - }); - themeConfigMenu.addItem('Reset custom configuration file', false, () => { - const msg = 'Do you want to reset the custom config file to default?\n\n!!! WARNING !!!\n\nThis will delete and replace all custom themes\nto the default custom theme template.\nYou should definitely make a backup first.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - try { // Needed to prevent crash when there is no config file - pref.customThemeSettings = false; - configCustom.resetConfiguration(); - console.log(`\n>>> Georgia-ReBORN's ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc file has been successfully reset to default. <<<\n\n`); - } catch (e) { window.Reload(); } - }); - }); - themeConfigMenu.addSeparator(); - themeConfigMenu.addItem('Reset all', false, () => { - const msg = 'Do you want to reset all theme settings to default?\n\nThis will also clear all library custom views plus filters\nand Georgia-ReBORN config.\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - pref.customThemeSettings = false; - pref.systemFirstLaunch = true; // Reset Georgia-ReBORN theme settings - try { // Needed to prevent crash when there is no config file - fso.DeleteFile(`${fb.ProfilePath}configuration\\foo_ui_columns.dll.cfg`); - config.resetConfiguration(); // Reset Georgia-ReBORN config file - panel.updateProp(ppt, 'default_value'); // Reset Library settings - uiBio.updateProp(pptBio, 'default_value'); // Reset Biography settings - const serverBio = new SettingsBio(); - serverBio.resetCfg(); // Reset Biography server settings - console.log('\n>>> Georgia-ReBORN has been successfully reset <<<\n\n'); - } catch (e) { - fb.ShowPopupMessage('Something went wrong and Georgia-ReBORN has NOT been successfully reset, try again!', 'Resetting Georgia-ReBORN'); + }); + libraryControlsMenu.addSeparator(); + libraryControlsMenu.createRadioSubMenu('Single-click action', ['Select', 'Send to playlist', 'Send to playlist and play', 'Send to playlist and play (add if playing)'], libSet.clickAction, [0, 1, 2, 3], (action) => { + libSet.clickAction = action; + lib.panel.updateProp(1); + }); + libraryControlsMenu.createRadioSubMenu('Double-click action', ['Send to playlist', 'Send to playlist and play', 'Expand/Collapse tree', 'Play only'], libSet.dblClickAction, [0, 1, 2, 3], (action) => { + libSet.dblClickAction = action; + lib.panel.updateProp(1); + }); + libraryControlsMenu.createRadioSubMenu('Middle-click action', ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (alt + double-click removes)'], libSet.mbtnClickAction, [0, 1, 2], (action) => { + libSet.mbtnClickAction = action; + lib.panel.updateProp(1); + }); + libraryControlsMenu.createRadioSubMenu('Alt + mouse click action', ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (alt + double-click removes)'], libSet.altClickAction, [0, 1, 2], (action) => { + libSet.altClickAction = action; + lib.panel.updateProp(1); + }); + const libraryKeystrokeMenu = new Menu('Keystroke action'); + libraryKeystrokeMenu.addToggleItem('Play on Enter or send from menu', libSet, 'autoPlay', () => { lib.panel.updateProp(1); }); + libraryKeystrokeMenu.addSeparator(); + libraryKeystrokeMenu.addRadioItems(['Select', 'Send to Playlist'], libSet.keyAction, [0, 1], (action) => { + libSet.keyAction = action; + lib.panel.updateProp(1); + }); + libraryKeystrokeMenu.appendTo(libraryControlsMenu); + libraryControlsMenu.addSeparator(); + libraryControlsMenu.addToggleItem('Always remember library state', libSet, 'rememberTree', () => { lib.panel.updateProp(1); }); + libraryControlsMenu.addToggleItem('Always load View by same as tree', libSet, 'artTreeSameView', () => { lib.panel.updateProp(1); }); + libraryControlsMenu.addToggleItem('Always load preset with current view pattern', libSet, 'presetLoadCurView', () => { lib.panel.updateProp(1); }); + libraryControlsMenu.addSeparator(); + libraryControlsMenu.addItem('Reset library zoom', false, () => { lib.panel.zoomReset(); }); + libraryControlsMenu.appendTo(libraryMenu); + + // * TRACK ROW * // + const libraryTrackRowMenu = new Menu('Track row'); + libraryTrackRowMenu.createRadioSubMenu('Node root type', ['Hide', 'All Music', 'View name', 'Summary item'], libSet.rootNode, [0, 1, 2, 3], (nodeIndex) => { + libSet.rootNode = nodeIndex; + lib.panel.updateProp(1); + }); + libraryTrackRowMenu.createRadioSubMenu('Node item counts', ['Hidden', '# Tracks', '# Sub-Items'], libSet.nodeCounts, [0, 1, 2], (nodeIndex) => { + libSet.nodeCounts = nodeIndex; + lib.panel.updateProp(1); + }); + libraryTrackRowMenu.createRadioSubMenu('Node item counts position', ['Right', 'Left'], libSet.countsRight, [true, false], (nodeCounts) => { + libSet.countsRight = nodeCounts; + lib.panel.updateProp(1); + }); + libraryTrackRowMenu.createRadioSubMenu('Node auto collapse', ['On', 'Off'], libSet.autoCollapse, [true, false], (nodeCollapse) => { + libSet.autoCollapse = nodeCollapse; + lib.panel.updateProp(1); + }); + libraryTrackRowMenu.addSeparator(); + libraryTrackRowMenu.createRadioSubMenu('Statistics', ['Tracks', 'Bitrate', 'Duration', 'Total size', 'Rating', 'Popularity', 'Date', 'Playback queue', 'Playcount', 'First played', 'Last played', 'Added'], libSet.itemShowStatistics, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], (stats) => { + libSet.itemShowStatistics = stats; + lib.panel.updateProp(1); + }); + libraryTrackRowMenu.addSeparator(); + libraryTrackRowMenu.addToggleItem('Show now playing', libSet, 'highLightNowplaying', () => { lib.panel.updateProp(1); }); + libraryTrackRowMenu.addToggleItem('Show tracks when expanding nodes', libSet, 'showTracks', () => { lib.pop.collapseAll(); lib.panel.updateProp(1); }); + libraryTrackRowMenu.addToggleItem('Show row stripes', libSet, 'rowStripes', () => { lib.panel.updateProp(1); }); + libraryTrackRowMenu.addSeparator(); + libraryTrackRowMenu.addToggleItem('Row fully clickable', libSet, 'fullLineSelection', () => { lib.panel.updateProp(1); }); + libraryTrackRowMenu.addToggleItem('Row mouse hover', grSet, 'libraryRowHover', () => { RepaintWindow(); }); + libraryTrackRowMenu.appendTo(libraryMenu); + + // * FILTER ORDER * // + const libraryFilterOrderMenu = new Menu('Filter order'); + libraryFilterOrderMenu.addRadioItems(['No filter', 'Lossless', 'Lossy', 'Missing replaygain', 'Never played', 'Played often', 'Recently added', 'Recently played', 'Top rated', 'Nowplaying artist'], libSet.filterBy, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], (order) => { + libSet.filterBy = order; + lib.panel.set('Filter', order); + }); + libraryFilterOrderMenu.appendTo(libraryMenu); + + // * SORT ORDER * // + const librarySortOrderMenu = new Menu('Sort order'); + librarySortOrderMenu.addRadioItems(['Default', 'Ascending (hide year)', 'Ascending (show year)', 'Descending (hide year)', 'Descending (show year)'], libSet.sortOrder, [0, 1, 2, 3, 4], (order) => { + libSet.sortOrder = order; + const d = {}; + lib.men.getSortData(d); + lib.men.sortByDate(libSet.sortOrder, d); + }); + librarySortOrderMenu.addSeparator(); + librarySortOrderMenu.addRadioItems(['Action: year after album', 'Action: year before album'], libSet.yearBeforeAlbum, [false, true], (year) => { + libSet.yearBeforeAlbum = year; + }); + librarySortOrderMenu.appendTo(libraryMenu); + + // * VIEW ORDER * // + const libraryViewOrderMenu = new Menu('View order'); + libraryViewOrderMenu.addRadioItems(['Artist', 'Album artist', 'Album artist | album', 'Album', 'Composer', 'Country', 'Country | Genre', 'Genre', 'Label', 'Year', 'Folder structure'], lib.panel.imgView ? libSet.albumArtViewBy : libSet.treeViewBy, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (order) => { + if (libSet.albumArtShow) { + libSet.albumArtViewBy = order; + } else { + libSet.viewBy = order; + libSet.treeViewBy = order; } + lib.lib.logTree(); + lib.pop.clearTree(); + lib.men.loadView(false, !lib.panel.imgView ? (libSet.artTreeSameView ? libSet.viewBy : libSet.treeViewBy) : (libSet.artTreeSameView ? libSet.viewBy : libSet.albumArtViewBy), lib.pop.sel_items[0]); }); - }); - themeConfigMenu.appendTo(settingsMenu); + libraryViewOrderMenu.appendTo(libraryMenu); - // * THEME PERFORMANCE * // - settingsMenu.createRadioSubMenu('Theme performance', ['Lowest quality (fastest speed - very slow CPU)', 'Low quality', 'Balanced (Default)', 'High quality', 'Highest quality (slowest speed - very fast CPU)'], pref.themePerformance, - ['lowestQuality', 'lowQuality', 'balanced', 'highQuality', 'highestQuality'], (perf) => { - const msg = 'Do you want to change the theme performance?\n\nThese presets will change various theme settings!\nIt is recommended to save current theme settings\nto the config file. You should also make a backup\nof your playlists to be on the safe side!\n\n!!! WARNING !!!\n"High quality" and especially "Highest Quality"\ncan freeze foobar, depending how fast your CPU performs.\nIt does not matter if you are using a multi-core CPU,\nonly single-core CPU performance counts!\nIf your foobar is unresponsive, restart\nand change to a lighter preset.\n\nContinue?'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - pref.themePerformance = perf; - setThemePerformance('balanced'); // First reset - setThemePerformance(pref.themePerformance); // Then set - window.Reload(); + // * SOURCE * // + const librarySourceMenu = new Menu('Source'); + librarySourceMenu.addRadioItems(['Library'], libSet.fixedPlaylist ? '' : libSet.libSource, [1], (src) => { + lib.men.setSource(0); }); - }); - settingsMenu.addSeparator(); - - // * OTHER SETTINGS * // - settingsMenu.addItem('Output device', false, () => { - const outputDeviceMenu = (x, y) => { - const menu = window.CreatePopupMenu(); - const output = fb.GetOutputDevices(); - const deviceList = JSON.parse(output); - const last = deviceList.length + 1 - let active = -1; - for (const [i, array] of deviceList.entries()) { - menu.AppendMenuItem(MF_STRING, i + 1, array.name); - if (array.active) active = i; - } - menu.AppendMenuSeparator(); - menu.AppendMenuItem(MF_STRING, last, 'Preferences...'); - - if (active > -1) menu.CheckMenuRadioItem(1, last, active + 1); - const idx = menu.TrackPopupMenu(x, y); - if (idx > 0 && idx < last) fb.RunMainMenuCommand(`Playback/Device/${deviceList[idx - 1].name}`); - else if (idx === last) fb.RunMainMenuCommand('Playback/Device/Preferences...'); - }; - outputDeviceMenu(state.mouse_x, state.mouse_y); - }); + const libraryPlaylistSourceMenu = new Menu('Playlist'); + libraryPlaylistSourceMenu.addRadioItems(['Active playlist'], libSet.fixedPlaylist ? '' : libSet.libSource, [0], (src) => { + lib.men.setSource(2); + lib.men.setActivePlaylist(); + }); + libraryPlaylistSourceMenu.addSeparator(); + const pl_no = Math.ceil(lib.men.pl.length / 30); + const pl_ix = libSet.fixedPlaylist ? plman.FindPlaylist(libSet.fixedPlaylistName) : -1; + for (let j = 0; j < pl_no; j++) { + const n = `# ${j * 30 + 1} - ${Math.min(lib.men.pl.length, 30 + j * 30)}${30 + j * 30 > pl_ix && ((j * 30) - 1) < pl_ix ? ' >>>' : ''}`; + const libraryPlaylistSourceItems = new Menu(n); + + for (let i = j * 30; i < Math.min(lib.men.pl.length, 30 + j * 30); i++) { + libraryPlaylistSourceItems.addRadioItems([lib.men.pl[i].menuName], libSet.fixedPlaylist ? lib.men.pl[pl_ix].menuName : '', [lib.men.pl[i].menuName], () => { + lib.men.setSource(2); + lib.men.setFixedPlaylist(i); + }); + } + libraryPlaylistSourceItems.appendTo(libraryPlaylistSourceMenu); + } + libraryPlaylistSourceMenu.appendTo(librarySourceMenu); + librarySourceMenu.appendTo(libraryMenu); - settingsMenu.addSeparator(); - settingsMenu.addToggleItem('Developer tools', pref, 'devTools', () => { pref.disableRightClick = !pref.devTools; }); - settingsMenu.addSeparator(); - settingsMenu.addToggleItem('Disable right-click', pref, 'disableRightClick'); + if (!context_menu) libraryMenu.appendTo(menu); + } - settingsMenu.appendTo(menu); -} + /** + * Top menu > Options > Biography. + * @param {Menu} menu - Creates the Biography panel menu via a new Menu instance. + * @param {boolean} context_menu - Appends Biography panel options to context menu. + * @protected + */ + biographyOptions(menu, context_menu) { + const biographyMenu = context_menu ? menu : new Menu('Biography'); + const biographyLayoutMenu = new Menu('Layout'); + + // * LAYOUT * // + if (grSet.layout === 'default') { + biographyLayoutMenu.addRadioItems(['Normal', 'Full'], grSet.biographyLayout, ['normal', 'full'], (width) => { + grSet.biographyLayout = width; + if (!grm.ui.displayBiography) grm.ui.displayBiography = true; grm.ui.displayPlaylist = false; grm.ui.displayLibrary = false; + if (grm.ui.displayLyrics) grm.ui.displayLyrics = false; + grm.ui.displayPlaylist = !grm.ui.displayPlaylist; + grm.ui.initBiographyLayout(); + grm.button.initButtonState(); + }); + biographyLayoutMenu.addSeparator(); + biographyLayoutMenu.addToggleItem('Use full preset', grSet, 'biographyLayoutFullPreset', () => { RepaintWindow(); }); + biographyLayoutMenu.addSeparator(); + } + biographyLayoutMenu.addRadioItems(['Top (default)', 'Right', 'Bottom', 'Left', 'Full overlay', 'Part overlay'], bioSet.style, [0, 1, 2, 3, 4, 5], (layout) => { + bioSet.style = layout; + bio.ui.updateProp(1); + }); + biographyLayoutMenu.addSeparator(); + const biographyFilmstripMenu = new Menu('Filmstrip'); + biographyFilmstripMenu.addRadioItems(['Top', 'Right', 'Bottom', 'Left (default)'], bioSet.filmStripPos, [0, 1, 2, 3], (pos) => { + bioSet.filmStripPos = pos; + bio.ui.updateProp(1); + }); + biographyFilmstripMenu.addSeparator(); + biographyFilmstripMenu.addToggleItem('Overlay image area', bioSet, 'filmStripOverlay', () => { bio.ui.updateProp(1); }); + biographyFilmstripMenu.appendTo(biographyLayoutMenu); + biographyLayoutMenu.appendTo(biographyMenu); + + // * THEME * // + const biographyThemeMenu = new Menu('Theme'); + biographyThemeMenu.addRadioItems(['Georgia-ReBORN', 'Dark', 'Blend', 'Light'], grSet.biographyTheme, [0, 1, 2, 3], (theme) => { + grSet.biographyTheme = theme; + bioSet.theme = grSet.biographyTheme; + grm.ui.initTheme(); + bio.call.on_colours_changed(); + bio.ui.updateProp(1); + grm.theme.themeColorAdjustments(); + }); + biographyThemeMenu.appendTo(biographyMenu); + + // * DISPLAY * // + const biographyDisplayMenu = new Menu('Display'); + biographyDisplayMenu.addRadioItems(['Image + text', 'Image', 'Text'], grSet.biographyDisplay, ['Image+text', 'Image', 'Text'], (display) => { + grSet.biographyDisplay = display; + grm.ui.setBiographyDisplay(); + bio.ui.updateProp(1); + }); + biographyDisplayMenu.addSeparator(); + biographyDisplayMenu.addToggleItem('Filmstrip', bioSet, 'showFilmStrip', () => { bio.ui.updateProp(1); }); + biographyDisplayMenu.addToggleItem('Seeker', bioSet, 'imgSeekerShow', () => { bio.ui.updateProp(1); }); + biographyDisplayMenu.addToggleItem('Heading', bioSet, 'heading', () => { bio.ui.updateProp(1); }); + biographyDisplayMenu.addToggleItem('Summary', bioSet, 'summaryShow', () => { bio.ui.updateProp(1); }); + biographyDisplayMenu.addSeparator(); + biographyDisplayMenu.addToggleItem(bioSet.summaryCompact ? 'Summary expand' : 'Summary compact', bioSet, 'summaryCompact', () => { bio.ui.updateProp(1); }); + biographyDisplayMenu.addSeparator(); + biographyDisplayMenu.addRadioItems(['Artist view', 'Album view'], bioSet.artistView, [true, false], (view) => { + bioSet.artistView = view; + bio.ui.updateProp(1); + }); + biographyDisplayMenu.addSeparator(); + biographyDisplayMenu.addRadioItems(['Prefer now playing', 'Follow selected track (playlist)'], bioSet.focus, [false, true], (view) => { + bioSet.focus = view; + bio.ui.updateProp(1); + }); + biographyDisplayMenu.appendTo(biographyMenu); + + // * SOURCES * // + const biographySourcesMenu = new Menu('Sources'); + const biographySourcesTextMenu = new Menu('Text'); + if (!bioSet.sourceAll) { + biographySourcesTextMenu.addRadioItems(['Auto-fallback', 'Static'], bioSet.lockBio, [false, true], (source) => { + bioSet.lockBio = source; + if (bioSet.sourceAll) { + bioSet.lockBio = false; + } else { + bioSet.lockRev = bioSet.lockBio; + } + bio.ui.updateProp(1); + }, !bioSet.sourceAll); + biographySourcesTextMenu.addSeparator(); + } + biographySourcesTextMenu.addToggleItem('Amalgamate', bioSet, 'sourceAll', () => { + bioSet.lockBio = false; + bioSet.lockRev = false; + bio.ui.updateProp(1); + }); + biographySourcesTextMenu.addSeparator(); + biographySourcesTextMenu.addToggleItem('Prefer composition (allmusic && wikipedia review)', bioSet, 'classicalMusicMode', () => { + bioCfg.classicalModeEnable = !bioCfg.classicalModeEnable; + bioSet.classicalAlbFallback = bioSet.classicalMusicMode; + bio.ui.updateProp(1); + }); + biographySourcesTextMenu.appendTo(biographySourcesMenu); + biographySourcesMenu.createRadioSubMenu('Photo', ['Cycle from download folder', 'Cycle from custom folder [fallback to above]', 'Artist (single image [fb2k: display])'], bioSet.cycPhotoLocation, [0, 1, 2], (location) => { + bioSet.cycPhotoLocation = location; + if (location === 0) { + bioSet.cycPhoto = true; + } + else if (location === 1 && !bioSet.get('Panel Biography - System: Photo Folder Checked', false)) { + fb.ShowPopupMessage('Enter folder in options: "Server Settings"\\Photo\\Custom photo folder.', 'Biography: custom folder for photo cycling'); + bioSet.set('Panel Biography - System: Photo Folder Checked', true); + bioSet.cycPhoto = true; + bio.img.artistReset(); + } + else if (location === 2) { + bioSet.cycPhoto = false; + } + bio.img.updImages(); + }); -///////////////////////////////// -// * DEVELOPER TOOLS OPTIONS * // -///////////////////////////////// -/** - * Top menu > Options > Developer tools. - * @param {Menu} menu Creates Developer tools menu via a new Menu instance. - */ -function developerToolsOptions(menu) { - if (!pref.devTools) return; - const debugMenu = new Menu('Developer tools'); - - debugMenu.addItem('Console', false, () => { fb.RunMainMenuCommand('View/Console'); }); // Top menu 'View' does not exist in Artwork/Compact layout - debugMenu.addSeparator(); - debugMenu.addToggleItem('Enable double click refresh', settings, 'doubleClickRefresh'); - debugMenu.addToggleItem('Enable debug output', settings, 'showDebugLog'); - debugMenu.addItem('Enable debug theme output', settings.showDebugThemeLog, () => { - settings.showDebugThemeLog = !settings.showDebugThemeLog; - if (settings.showDebugThemeLog) { - albumArt = null; - on_playback_new_track(fb.GetNowPlaying()); - } - }); - debugMenu.addItem('Enable debug theme overlay', settings.showDebugThemeOverlay, () => { - settings.showDebugThemeOverlay = !settings.showDebugThemeOverlay; - if (settings.showDebugThemeOverlay) { - albumArt = null; - on_playback_new_track(fb.GetNowPlaying()); + const biographySourcesCoverMenu = new Menu('Cover'); + if (!bioSet.loadCovAllFb && !bioSet.loadCovFolder) { + biographySourcesCoverMenu.addRadioItems(['Front', 'Back', 'Disc', 'Icon', 'Artist'], bioSet.covType, [0, 1, 2, 3, 4], (type) => { + bioSet.covType = type; + bio.img.cov.selection = [0, -1, -1, -1, -1]; + bio.img.cov.selFiltered = [0]; + bio.img.getImages(); + }, bioSet.loadCovFolder); } else { - RepaintWindow(); + biographySourcesCoverMenu.addToggleItems(['Front', 'Back', 'Disc', 'Icon', 'Artist'], bio.img.cov.selection, [0, 1, 2, 3, 4], (type) => { + !bioSet.loadCovAllFb ? bioSet.covType = type : bio.img.cov.selection[type] = bio.img.cov.selection[type] === -1 ? type : -1; + bio.img.cov.selFiltered = bio.img.cov.selection.filter(v => v !== -1); + if (!bio.img.cov.selFiltered.length) { + bio.img.cov.selection = [0, -1, -1, -1, -1]; + bio.img.cov.selFiltered = [0]; + } + bioSet.loadCovSelFb = JSON.stringify(bio.img.cov.selection); + !bioSet.loadCovAllFb ? bio.img.getImages() : bio.img.check(); + }, !bioSet.loadCovAllFb && bioSet.loadCovFolder); + } + biographySourcesCoverMenu.addSeparator(); + biographySourcesCoverMenu.addToggleItem('Cycle above', bioSet, 'loadCovAllFb', () => { + bioSet.loadCovAllFb = !bioSet.loadCovAllFb; + bio.img.toggle('loadCovAllFb'); + }); + biographySourcesCoverMenu.addToggleItem('Cycle from download folder', bioSet, 'loadCovFolder', () => { + bioSet.loadCovFolder = !bioSet.loadCovFolder; + bio.img.toggle('loadCovFolder'); + if (bioSet.loadCovFolder) { + fb.ShowPopupMessage("Enter folder in options: \"Server Settings\"\\Cover\\Covers: cycle folder.\n\nDefault: artist photo folder.\n\nImages are updated when the album changes. Any images arriving after choosing the current album aren't included.", 'Biography: load folder for cover cycling'); + } + }); + biographySourcesCoverMenu.appendTo(biographySourcesMenu); + + const biographySourcesOpenFileLocationMenu = new Menu('Open file location'); + biographySourcesOpenFileLocationMenu.addItem('Image', false, () => { + const imgInfo = bio.img.pth(); + bio.men.path.img = imgInfo.imgPth; + OpenExplorer(`explorer /select, "${bio.men.path.img}"`, false); + }); + biographySourcesOpenFileLocationMenu.addSeparator(); + if (bio.txt.bio.am.length || bio.txt.rev.am.length) { + biographySourcesOpenFileLocationMenu.addItem(bioSet.artistView ? 'Biography [allmusic]' : 'Review [allmusic]', false, () => { + bio.men.path.am = bioSet.artistView ? bio.txt.bioPth('Am') : bio.txt.revPth('Am'); + OpenExplorer(`explorer /select, "${bio.men.path.am[1]}"`, false); + }); } - }); - debugMenu.addSeparator(); - debugMenu.addToggleItem('Show draw timing', timings, 'showDrawTiming'); - debugMenu.addToggleItem('Show extra draw timing', timings, 'showExtraDrawTiming'); - debugMenu.addToggleItem('Show debug timing', timings, 'showDebugTiming'); - debugMenu.addSeparator(); - debugMenu.addToggleItem('Show ram usage', timings, 'showRamUsage'); - debugMenu.addToggleItem('Show draw areas', timings, 'drawRepaintRects', () => { - if (timings.drawRepaintRects) { - RepaintRectAreas(); - } else { - RepaintWindow(); + if (bio.txt.bio.lfm.length || bio.txt.rev.lfm.length) { + biographySourcesOpenFileLocationMenu.addItem(bioSet.artistView ? 'Biography [last.fm]' : 'Review [last.fm]', false, () => { + bio.men.path.lfm = bioSet.artistView ? bio.txt.bioPth('Lfm') : bio.txt.revPth('Lfm'); + OpenExplorer(`explorer /select, "${bio.men.path.lfm[1]}"`, false); + }); } - }); - debugMenu.addSeparator(); - debugMenu.addToggleItem('Show panel calls', timings, 'showPanelTraceCall', () => { - if (timings.showPanelTraceCall) { - trace_call = true; - } else { - trace_call = false; - if (timings.showPanelTraceOnMove) { - trace_on_move = false; - timings.showPanelTraceOnMove = false; - } + if (bio.txt.bio.wiki.length || bio.txt.rev.wiki.length) { + biographySourcesOpenFileLocationMenu.addItem(bioSet.artistView ? 'Biography [wikipedia]' : 'Review [wikipedia]', false, () => { + bio.men.path.wiki = bioSet.artistView ? bio.txt.bioPth('Wiki') : bio.txt.revPth('Wiki'); + OpenExplorer(`explorer /select, "${bio.men.path.wiki[1]}"`, false); + }); } - }); - debugMenu.addToggleItem('Show panel moves', timings, 'showPanelTraceOnMove', () => { - if (timings.showPanelTraceOnMove) { - trace_on_move = true; - trace_call = true; - timings.showPanelTraceCall = true; - return; + if (bio.lyrics.lyrics.length) { + biographySourcesOpenFileLocationMenu.addItem('Lyrics', false, () => { + bio.men.path.txt = bioSet.artistView ? bio.txt.txtReaderPth() : bio.txt.txtRevPth(); + OpenExplorer(`explorer /select, "${bio.men.path.txt[1]}"`, false); + }); } - trace_on_move = false; - trace_call = false; - timings.showPanelTraceCall = false; - }); - debugMenu.addToggleItem('Show playlist performance', timings, 'showPlaylistTraceListPerf', () => { - trace_initialize_list_performance = !trace_initialize_list_performance; - }); - debugMenu.addSeparator(); - debugMenu.addItem('Set system first launch to true', false, () => { // Used when creating new config files - const msg = 'Do you really want to set system to first launch?\n\nContinue?\n\n\n'; - ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { - if (!confirmed) return; - window.SetProperty('Georgia-ReBORN - 16. System: System first launch', true); - g_properties.show_scrollbar = false; - pref.showTopMenuCompact = true; - pref.devTools = false; - pref.disableRightClick = true; - console.log('\n>>> Georgia-ReBORN has been set to system first launch <<<\n\n'); - }); - }); - - debugMenu.appendTo(menu); -} + biographySourcesMenu.addSeparator(); + biographySourcesOpenFileLocationMenu.appendTo(biographySourcesMenu); + + biographySourcesMenu.addItem('Force update', false, () => { + bio.panel.callServer(1, bio.panel.id.focus, 'bio_forceUpdate', 0); + bio.ui.updateProp(1); + }); + biographySourcesMenu.appendTo(biographyMenu); + + // * IMAGES * // + const biographyImageMenu = new Menu('Image'); + const biographyImageDefaultMenu = new Menu('Image + text'); + biographyImageDefaultMenu.createRadioSubMenu('Photo', ['Regular', 'Auto-fill (default)', 'Circular'], bioSet.artStyleDual, [0, 1, 2], (style) => { + bioSet.artStyleDual = style; + bio.ui.updateProp(1); + }); + biographyImageDefaultMenu.addToggleItem('Reflection', bioSet, 'artReflDual', () => { bioSet.artShadowDual = false; bio.ui.updateProp(1); }); + biographyImageDefaultMenu.addToggleItem('Shadow', bioSet, 'artShadowDual', () => { bioSet.artReflDual = false; bio.ui.updateProp(1); }); + biographyImageDefaultMenu.addSeparator(); + biographyImageDefaultMenu.createRadioSubMenu('Cover', ['Regular', 'Auto-fill (default)', 'Circular'], bioSet.covStyleDual, [0, 1, 2], (style) => { + bioSet.covStyleDual = style; + bio.ui.updateProp(1); + }); + biographyImageDefaultMenu.addToggleItem('Reflection', bioSet, 'covReflDual', () => { bioSet.covShadowDual = false; bio.ui.updateProp(1); }); + biographyImageDefaultMenu.addToggleItem('Shadow', bioSet, 'covShadowDual', () => { bioSet.covReflDual = false; bio.ui.updateProp(1); }); + + biographyImageDefaultMenu.appendTo(biographyImageMenu); + const biographyImageOnlyMenu = new Menu('Image only'); + biographyImageOnlyMenu.createRadioSubMenu('Photo', ['Regular', 'Auto-fill (default)', 'Circular'], bioSet.artStyleImgOnly, [0, 1, 2], (style) => { + bioSet.artStyleImgOnly = style; + bio.ui.updateProp(1); + }); + biographyImageOnlyMenu.addToggleItem('Reflection', bioSet, 'artReflImgOnly', () => { bioSet.artShadowImgOnly = false; bio.ui.updateProp(1); }); + biographyImageOnlyMenu.addToggleItem('Shadow', bioSet, 'artShadowImgOnly', () => { bioSet.artReflImgOnly = false; bio.ui.updateProp(1); }); + biographyImageOnlyMenu.addSeparator(); + biographyImageOnlyMenu.createRadioSubMenu('Cover', ['Regular', 'Auto-fill (default)', 'Circular'], bioSet.covStyleImgOnly, [0, 1, 2], (style) => { + bioSet.covStyleImgOnly = style; + bio.ui.updateProp(1); + }); + biographyImageOnlyMenu.addToggleItem('Reflection', bioSet, 'covReflImgOnly', () => { bioSet.covShadowImgOnly = false; bio.ui.updateProp(1); }); + biographyImageOnlyMenu.addToggleItem('Shadow', bioSet, 'covShadowImgOnly', () => { bioSet.covReflImgOnly = false; bio.ui.updateProp(1); }); + biographyImageOnlyMenu.appendTo(biographyImageMenu); + + const biographyImageFilmstripMenu = new Menu('Filmstrip'); + biographyImageFilmstripMenu.createRadioSubMenu('Photo', ['Regular', 'Auto-fill (default)', 'Circular'], bioSet.filmPhotoStyle, [0, 1, 2], (style) => { + bioSet.filmPhotoStyle = style; + bio.ui.updateProp(1); + }); + biographyImageFilmstripMenu.createRadioSubMenu('Cover', ['Regular', 'Auto-fill (default)', 'Circular'], bioSet.filmCoverStyle, [0, 1, 2], (style) => { + bioSet.filmCoverStyle = style; + bio.ui.updateProp(1); + }); + biographyImageFilmstripMenu.appendTo(biographyImageMenu); + biographyImageMenu.addSeparator(); + const biographyImageDownloadMenu = new Menu('Downloads'); + biographyImageDownloadMenu.addRadioItems([' 5 Images', '10 Images (default)', '15 Images', '20 Images'], bioCfg.photoNum, [5, 10, 15, 20], (num) => { + bioCfg.photoNum = num; + bio.ui.updateProp(1); + }); + biographyImageDownloadMenu.appendTo(biographyImageMenu); + biographyImageMenu.addSeparator(); + + const biographyImageAutoCycleMenu = new Menu('Auto cycle'); + biographyImageAutoCycleMenu.addToggleItem('Auto cycle', bioSet, 'cycPic', () => { bio.ui.updateProp(1); }); + biographyImageAutoCycleMenu.addSeparator(); + biographyImageAutoCycleMenu.addToggleItem('Smooth transition', bioSet, 'imgSmoothTrans', () => { bio.ui.updateProp(1); }); + biographyImageAutoCycleMenu.addSeparator(); + biographyImageAutoCycleMenu.addRadioItems([' 5 sec', '10 sec', '15 sec (default)', '30 sec', '60 sec'], bioSet.cycTimePic, [5, 10, 15, 30, 60], (dur) => { + bioSet.cycTimePic = dur; + bio.ui.updateProp(1); + }); + biographyImageAutoCycleMenu.appendTo(biographyImageMenu); -/////////////////// -// * INPUT BOX * // -/////////////////// -/** - * An input box that is used for various custom user settings. - * @param {string} option The parameter has following options: - * - 'renameCustomTheme' - renames the name of the custom theme. - * - 'playlistCustomHeaderInfo' - sets custom playlist header info via pattern. - * - 'playlistCustomTrackRow' - sets custom playlist track row via pattern. - * - 'playlistSortCustom' - sets custom playlist sort order via pattern. - * - 'customLibraryDir' - sets the custom Library cache directory. - * - 'customBiographyDir' - sets the custom Biography cache directory. - * - 'customLyricsDir' - sets the custom Lyrics cache directory. - * - 'customWaveformBarDir' - sets the custom Waveform bar cache directory. - * @return {*} The input box. - */ -function inputBox(option) { - const customTheme = - pref.theme === 'custom01' ? 'customTheme01' : - pref.theme === 'custom02' ? 'customTheme02' : - pref.theme === 'custom03' ? 'customTheme03' : - pref.theme === 'custom04' ? 'customTheme04' : - pref.theme === 'custom05' ? 'customTheme05' : - pref.theme === 'custom06' ? 'customTheme06' : - pref.theme === 'custom07' ? 'customTheme07' : - pref.theme === 'custom08' ? 'customTheme08' : - pref.theme === 'custom09' ? 'customTheme09' : - pref.theme === 'custom10' ? 'customTheme10' : ''; - - const customThemeName = - pref.theme === 'custom01' ? customTheme01 : - pref.theme === 'custom02' ? customTheme02 : - pref.theme === 'custom03' ? customTheme03 : - pref.theme === 'custom04' ? customTheme04 : - pref.theme === 'custom05' ? customTheme05 : - pref.theme === 'custom06' ? customTheme06 : - pref.theme === 'custom07' ? customTheme07 : - pref.theme === 'custom08' ? customTheme08 : - pref.theme === 'custom09' ? customTheme09 : - pref.theme === 'custom10' ? customTheme10 : ''; - - const customLibraryDir = option === 'customLibraryDir'; - const customBiographyDir = option === 'customBiographyDir'; - const customLyricsDir = option === 'customLyricsDir'; - const customWaveformBarDir = option === 'customWaveformBarDir'; - const customDirPath = customLibraryDir ? globals.customLibraryDir : customBiographyDir ? globals.customBiographyDir : customLyricsDir ? globals.customLyricsDir : customWaveformBarDir ? globals.customWaveformBarDir : ''; - const customDirString = customLibraryDir ? 'library' : customBiographyDir ? 'biography' : customLyricsDir ? 'lyrics' : customWaveformBarDir ? 'waveform' : ''; - const customDirSchema = customLibraryDir ? customLibraryDirSchema : customBiographyDir ? customBiographyDirSchema : customLyricsDir ? customLyricsDirSchema : customWaveformBarDir ? customWaveformBarDirSchema : ''; - - switch (option) { - case 'renameCustomTheme': { - let newVal; - let input; - try { - input = utils.InputBox(window.ID, 'Enter your desired name for your current active custom theme', 'Georgia-ReBORN', customThemeName.name, true); - newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); - if (typeof newVal !== 'string') throw new Error('Invalid type'); - } - catch (e) { - if (e.message === 'Invalid type' || e.name === 'SyntaxError') { - fb.ShowPopupMessage(`Name is not valid:\n${input}\n\nSomething went wrong...`, 'Custom theme name'); + biographyImageMenu.appendTo(biographyMenu); + + if (!context_menu) biographyMenu.appendTo(menu); + } + + /** + * Top menu > Options > Lyrics. + * @param {Menu} menu - Creates the Lyrics panel menu via a new Menu instance. + * @param {boolean} context_menu - Appends Lyrics panel options to context menu. + * @protected + */ + lyricsOptions(menu, context_menu) { + const lyricsMenu = context_menu ? menu : new Menu('Lyrics'); + + if (grSet.layout === 'default') { + lyricsMenu.createRadioSubMenu('Layout', ['Normal', 'Full'], grSet.lyricsLayout, ['normal', 'full'], (width) => { + grSet.lyricsLayout = width; + if (!grm.ui.displayLyrics && grSet.lyricsLayout === 'full' || grm.ui.noAlbumArtStub) { + grm.ui.displayPlaylist = true; + grm.ui.displayLyrics = true; + grm.ui.displayLibrary = false; + grm.ui.displayBiography = false; + grm.ui.lyricsLayoutFullWidth = grSet.lyricsLayout === 'full'; } - return; + if (grm.ui.displayLyrics && grSet.lyricsLayout === 'full') { + grm.ui.displayPlaylist = false; + grm.ui.displayLyrics = true; + grm.ui.lyricsLayoutFullWidth = grSet.lyricsLayout === 'full'; + } + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.lyrics.initLyrics(); + on_playback_seek(); + grm.ui.resizeArtwork(true); + grm.button.initButtonState(); + RepaintWindow(); + }); + } + + const lyricsDisplayMenu = new Menu('Display'); + lyricsDisplayMenu.createRadioSubMenu('Show drop shadow', ['None', 'Small', 'Normal', 'Large'], grSet.lyricsDropShadowLevel, [0, 1, 2, 3], (size) => { + grSet.lyricsDropShadowLevel = size; + grm.lyrics.initLyrics(); + RepaintWindow(); + }); + lyricsDisplayMenu.addToggleItem('Show fade scroll', grSet, 'lyricsFadeScroll', () => { + grm.lyrics.initLyrics(); + RepaintWindow(); + }); + lyricsDisplayMenu.addToggleItem('Show larger current sync', grSet, 'lyricsLargerCurrentSync', () => { + bioSet.largerSyncLyricLine = grSet.lyricsLargerCurrentSync; + grm.lyrics.initLyrics(); + bio.ui.updateProp(1); + RepaintWindow(); + }); + lyricsDisplayMenu.addToggleItem('Show lyrics on album art', grSet, 'lyricsAlbumArt', () => { + grm.theme.initMainColors(); + RepaintWindow(); + }); + lyricsDisplayMenu.appendTo(lyricsMenu); + + const lyricsControlsMenu = new Menu('Controls'); + lyricsControlsMenu.addToggleItem('Remember lyrics panel state', grSet, 'lyricsRememberPanelState'); + lyricsControlsMenu.appendTo(lyricsMenu); + + const lyricsScrollSpeedMenu = new Menu('Scroll speed'); + lyricsScrollSpeedMenu.addRadioItems(['Fastest (very slow CPU)', 'Fast', 'Normal', 'Slow', 'Slowest (very fast CPU)'], grSet.lyricsScrollSpeed, ['fastest', 'fast', 'normal', 'slow', 'slowest'], (speed) => { + grSet.lyricsScrollSpeed = speed; + switch (speed) { + case 'fastest': + grSet.lyricsScrollRateAvg = 300; + grSet.lyricsScrollRateMax = 150; + break; + case 'fast': + grSet.lyricsScrollRateAvg = 500; + grSet.lyricsScrollRateMax = 250; + break; + case 'normal': + grSet.lyricsScrollRateAvg = 750; + grSet.lyricsScrollRateMax = 375; + break; + case 'slow': + grSet.lyricsScrollRateAvg = 1000; + grSet.lyricsScrollRateMax = 500; + break; + case 'slowest': + grSet.lyricsScrollRateAvg = 1500; + grSet.lyricsScrollRateMax = 725; + break; } - customThemeName.name = newVal; - configCustom.updateConfigObjValues(customTheme, true); + grm.lyrics.initLyrics(); + RepaintWindow(); + }); + lyricsScrollSpeedMenu.appendTo(lyricsMenu); + lyricsMenu.addSeparator(); + + lyricsMenu.addItem('Lyrics information', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Lyric information'); }); + lyricsMenu.addItem('Lyrics search', false, () => { fb.RunMainMenuCommand('View/ESLyric/Search...'); }); + lyricsMenu.addSeparator(); + lyricsMenu.addItem('Next lyrics', false, () => { grm.lyrics.nextLyrics(); }); + lyricsMenu.addItem('Edit lyrics', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Edit lyric'); }); + lyricsMenu.addItem('Delete lyrics', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Delete lyric'); }); + lyricsMenu.addSeparator(); + lyricsMenu.addItem('Save lyrics to tags', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Save Lyric To/Tags'); }); + lyricsMenu.addSeparator(); + lyricsMenu.addItem('Open containing folder', false, () => { fb.RunMainMenuCommand('View/ESLyric/Panels/Open containing folder'); }); + + if (!context_menu) lyricsMenu.appendTo(menu); + } + + /** + * Top menu > Options > Settings. + * @param {Menu} menu - Creates the Settings menu via a new Menu instance. + * @protected + */ + settingsOptions(menu) { + const settingsMenu = new Menu('Settings'); + + // * THEME DAY/NIGHT MODE * // + const themeDayNightModeMenu = new Menu('Theme day/night mode'); + + const dayNightTimeRangeDefaults = ['6-18', '7-19', '8-20', '9-21', '10-22']; + const dayNightTimeRangeLabels = dayNightTimeRangeDefaults.map(FormatThemeDayNightModeString); + dayNightTimeRangeLabels.unshift('Deactivated (default)'); + + const dayNightTimeRangeCustom = grSet.themeDayNightMode && !dayNightTimeRangeDefaults.includes(grSet.themeDayNightMode); + const dayNightTimeRangeCustomLabel = `Custom: ${FormatThemeDayNightModeString(grSet.themeDayNightMode)}`; + const dayNightTimeRangeVal = dayNightTimeRangeCustom ? grSet.themeDayNightMode : (grSet.themeDayNightMode || false); + const dayNightTimeRangeValues = [false, ...dayNightTimeRangeDefaults]; + + if (dayNightTimeRangeCustom) { + dayNightTimeRangeLabels.push(dayNightTimeRangeCustomLabel); + dayNightTimeRangeValues.push(grSet.themeDayNightMode); } - break; - case 'playlistCustomHeaderInfo': { - const oldValStr = JSON.stringify(settings.playlistCustomHeaderInfo).replace(/"/g, ''); - let newVal; - let input; - try { - input = utils.InputBox(window.ID, 'Enter your custom playlist header info pattern:', 'Georgia-ReBORN', oldValStr, true); - newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); - if (typeof newVal !== 'string') throw new Error('Invalid type'); + themeDayNightModeMenu.addItem('Set custom time range', false, () => { + grm.inputBox.themeDayNightModeCustom(); + }); + themeDayNightModeMenu.addSeparator(); + themeDayNightModeMenu.addRadioItems(dayNightTimeRangeLabels, dayNightTimeRangeVal, dayNightTimeRangeValues, (time) => { + grSet.themeDayNightMode = time; + if (!grSet.themeDayNightMode) { + grSet.themeDayNightMode = false; + clearInterval(grm.ui.themeDayNightModeTimer); + grm.ui.themeDayNightModeTimer = null; + return; } - catch (e) { - if (e.message === 'Invalid type' || e.name === 'SyntaxError') { - fb.ShowPopupMessage(`Pattern is not valid:\n${input}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExamples of correct patterns:\n\n%album artist% %date% %album% %discnumber% %tracknumber% %title%\n\n$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%`, 'Custom playlist header info'); + const msg = 'Do you want to activate the theme day/night mode?\n\nThe default daytime theme is White\nand the nighttime theme is Black.\n\nYou can set up and configure\na new theme and styles for both modes\nin the theme day/night mode setup.\n\nContinue?\n\n\n'; + const msgFb = 'Theme day/night mode is active:\n\nThe default daytime theme is White\nand the nighttime theme is Black.\n\nYou can set up and configure\na new theme and styles for both modes\nin the theme day/night mode setup.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) { + grm.ui.resetTheme(); + initThemeDayNightMode(new Date()); + grm.ui.initThemeFull = true; + if (grSet.theme.startsWith('custom')) grm.ui.initCustomTheme(); + if (!fb.IsPlaying) grm.color.setThemeColors(); + grm.ui.initTheme(); + grm.ui.initStyleState(); + grm.preset.initThemePresetState(); + } else { + grSet.themeDayNightMode = false; + if (grSet.presetAutoRandomMode !== 'off') { + grSet.presetAutoRandomMode = 'dblclick'; + grm.preset.getRandomThemePreset(); + } } + }); + }); + themeDayNightModeMenu.addSeparator(); + themeDayNightModeMenu.appendTo(settingsMenu); + themeDayNightModeMenu.addItem(!grSet.themeSetupDay ? 'Theme setup for daytime' : 'Save and exit daytime theme setup', false, () => { + grSet.themeSetupDay = !grSet.themeSetupDay; + grSet.themeSetupNight = false; + RepaintWindow(); + if (!grSet.themeSetupDay) { + grm.ui.themeNotification = ''; return; } - settings.playlistCustomHeaderInfo = newVal; - config.updateConfigObjValues('settings', true); - } - break; - - case 'playlistCustomTrackRow': { - const oldValStr1 = JSON.stringify(settings.playlistCustomTitle).replace(/"/g, ''); - const oldValStr2 = JSON.stringify(settings.playlistCustomTitleNoHeader).replace(/"/g, ''); - let newVal1; - let newVal2; - let input1; - let input2; - try { - input1 = utils.InputBox(window.ID, 'Enter your custom playlist track row pattern:', 'Georgia-ReBORN', oldValStr1, true); - input2 = utils.InputBox(window.ID, 'Enter your custom playlist track row pattern when no header displayed:', 'Georgia-ReBORN', oldValStr2, true); - newVal1 = !input1 || typeof input1 !== 'string' && !input1.length ? '' : JSON.parse(`"${input1}"`); - newVal2 = !input2 || typeof input2 !== 'string' && !input2.length ? '' : JSON.parse(`"${input2}"`); - if (typeof newVal1 !== 'string') throw new Error('Invalid type'); - if (typeof newVal2 !== 'string') throw new Error('Invalid type'); - } - catch (e) { - if (e.message === 'Invalid type' || e.name === 'SyntaxError') { - fb.ShowPopupMessage(`Pattern is not valid:\n${input1 || input2}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExamples of correct patterns:\n\n%album artist% %date% %album% %discnumber% %tracknumber% %title%\n\n$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%`, 'Custom playlist track row'); - } + const msg = '>>> Theme setup for daytime is active <<<\n\nPlease select your theme and styles for daytime usage.\nAfter configuring the theme settings, revisit this menu to save them.\n\n\n'; + ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + grm.ui.resetTheme(); + setThemeDayNightTheme(true); + grm.ui.initThemeFull = true; + if (grSet.theme.startsWith('custom')) grm.ui.initCustomTheme(); + if (!fb.IsPlaying) grm.color.setThemeColors(); + grm.ui.initTheme(); + grm.ui.initStyleState(); + grm.preset.initThemePresetState(); + }); + themeDayNightModeMenu.addItem(!grSet.themeSetupNight ? 'Theme setup for nighttime' : 'Save and exit nighttime theme setup', false, () => { + grSet.themeSetupDay = false; + grSet.themeSetupNight = !grSet.themeSetupNight; + RepaintWindow(); + if (!grSet.themeSetupNight) { + grm.ui.themeNotification = ''; return; } - settings.playlistCustomTitle = newVal1; - settings.playlistCustomTitleNoHeader = newVal2; - config.updateConfigObjValues('settings', true); + const msg = '>>> Theme setup for nighttime is active <<<\n\nPlease select your theme and styles for nighttime usage.\nAfter configuring the theme settings, revisit this menu to save them.\n\n\n'; + ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + grm.ui.resetTheme(); + setThemeDayNightTheme(false); + grm.ui.initThemeFull = true; + if (grSet.theme.startsWith('custom')) grm.ui.initCustomTheme(); + if (!fb.IsPlaying) grm.color.setThemeColors(); + grm.ui.initTheme(); + grm.ui.initStyleState(); + grm.preset.initThemePresetState(); + }); + + // * HIDE OTHER MENUS WHEN THEME DAY/NIGHT SETUP IS ACTIVE + if (grSet.themeSetupDay || grSet.themeSetupNight) { + settingsMenu.appendTo(menu); + return; } - break; - case 'playlistSortCustom': { - const oldValStr = JSON.stringify(settings.playlistSortCustom).replace(/"/g, ''); - let newVal; - let input; - try { - input = utils.InputBox(window.ID, 'Enter your custom playlist order pattern:', 'Georgia-ReBORN', oldValStr, true); - newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input}"`); - if (typeof newVal !== 'string') throw new Error('Invalid type'); - } - catch (e) { - if (e.message === 'Invalid type' || e.name === 'SyntaxError') { - fb.ShowPopupMessage(`Pattern is not valid:\n${input}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExamples of correct patterns:\n\n%album artist% %date% %album% %discnumber% %tracknumber% %title%\n\n$if2(%artist sort order%,%album artist%) $if2(%album sort order%,%album%) %edition% %codec% %discnumber% %tracknumber%`, 'Custom playlist order'); - } + // * THEME SANDBOX * // + const themeSandboxMenu = new Menu('Theme sandbox'); + const restoreThemeStylePresetSettings = (reset) => { + grSet.presetAutoRandomMode = 'dblclick'; + grm.ui.setThemePresetSelection(true); // * Reactivate all + grm.ui.resetTheme(); + if (reset) grm.ui.restoreThemeStylePreset(true); else grm.ui.restoreThemeStylePreset(); + if (grSet.savedPreset !== false) grm.preset.setThemePreset(grSet.savedPreset); + grm.ui.updateStyle(); + } + themeSandboxMenu.addToggleItem('Enabled', grSet, 'themeSandbox', () => { + if (!grSet.themeSandbox) { + const msg = 'Do you want to restore\nor keep current theme settings?\n\nThis will restore previously used\ntheme, styles, preset\nor use the current active.\n\nContinue?\n\n\n'; + const msgFb = 'Theme settings restored:\n\nTheme, styles or preset have been restored.'; + ShowPopup(true, msgFb, msg, 'Restore', 'Keep', (confirmed) => { + if (confirmed) { + restoreThemeStylePresetSettings(); + } else { + restoreThemeStylePresetSettings(true); + } + }); return; } - settings.playlistSortCustom = newVal; - config.updateConfigObjValues('settings', true); - } - break; + const msg = 'Do you want to activate the theme sandbox?\n\nThis mode is useful when trying out\nthemes, styles, presets or writing theme tags.\n\nAfter disabling the theme sandbox mode,\npreviously used theme settings can be restored.\n\nContinue?\n\n\n'; + const msgFb = 'Theme sandbox mode activated:\n\nThis mode is useful when trying out\nthemes, styles, presets or writing theme tags.\n\nAfter disabling the theme sandbox mode,\npreviously used theme settings will be restored.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) grSet.themeSandbox = false; + }); + }); + themeSandboxMenu.addSeparator(); + themeSandboxMenu.addItem('Restore theme settings', false, () => { + const msg = 'Do you want to restore theme settings?\n\nThis will restore previously used\ntheme, styles, preset.\n\nContinue?\n\n\n'; + const msgFb = 'Theme settings restored:\n\nTheme, styles or preset have been restored.'; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) restoreThemeStylePresetSettings(); + }); + }, grSet.themeSandbox); + themeSandboxMenu.appendTo(settingsMenu); - case 'customLibraryDir': case 'customBiographyDir': case 'customLyricsDir': case 'customWaveformBarDir': { - const oldValStr = JSON.stringify(customDirPath).replace(/["[\]]/g, '').replace(/\\\\/g, '\\'); - let newVal; - let input; - try { - input = utils.InputBox(window.ID, `Enter your custom ${customDirString} directory:`, 'Georgia-ReBORN', oldValStr, true); - newVal = !input || typeof input !== 'string' && !input.length ? '' : JSON.parse(`"${input.replace(/[\\/]/g, '\\\\')}"`); - if (typeof newVal !== 'string') throw new Error('Invalid type'); + // * THEME FONTS * // + const themeFontMenu = new Menu('Theme fonts'); + themeFontMenu.addToggleItem('Use custom theme fonts', grSet, 'customThemeFonts', () => { + const msg = 'Do you want to use custom theme fonts?\n\nYou need to set your custom fonts in your config file located in\nfoobar\\profile\\georgia-reborn\\configs\\georgia-reborn-config.jsonc\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + grSet.customThemeFonts = confirmed; + }); + window.Reload(); + }); + themeFontMenu.appendTo(settingsMenu); + + // * THEME IMAGES * // + const themeImagesMenu = new Menu('Theme images'); + themeImagesMenu.addToggleItem('Use custom preloader logo', grSet, 'customPreloaderLogo', () => { + if (!grSet.customPreloaderLogo) return window.Reload(); + const customLogoPath = `${fb.ProfilePath}georgia-reborn\\images\\custom\\logo\\_4K-custom-logo.png and _custom-logo.png`; + const msg = `The custom logo placeholder can be replaced\nwith a new logo:\n\n${customLogoPath}\n\nRecommended logo dimensions are:\n500x500 pixels for 4K\n250x250 pixels for HD\n\n\n`; + ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + window.Reload(); + }); + themeImagesMenu.addToggleItem('Use custom theme images', grSet, 'customThemeImages', () => { + if (!grSet.customThemeImages) return window.Reload(); + const customImagesPath = `${fb.ProfilePath}georgia-reborn\\images\\custom\\`; + const msg = `All theme images can be safely replaced\nwith new custom ones:\n\n${customImagesPath}\n\nPlease ensure all images have the same names\nas the original ones, which are located in the\nparent directory.\n\n\n`; + ShowPopup(true, msg, msg, 'OK', false, (confirmed) => {}); + window.Reload(); + }); + themeImagesMenu.appendTo(settingsMenu); + + // * THEME CACHE * // + const themeCacheMenu = new Menu('Theme cache'); + const themeCacheLibraryMenu = new Menu('Library'); + themeCacheLibraryMenu.addToggleItem('Image disk cache enabled', libSet, 'albumArtDiskCache'); + themeCacheLibraryMenu.addToggleItem('Preload images in disk cache', libSet, 'albumArtPreLoad'); + themeCacheLibraryMenu.addToggleItem('Use custom library directory', grSet, 'customLibraryDir', () => { + if (grSet.customLibraryDir) grm.inputBox.customCacheDir('library'); + window.Reload(); + }); + themeCacheLibraryMenu.addSeparator(); + themeCacheLibraryMenu.addItem('Open library cache directory', false, () => { + const cacheDir = grSet.customLibraryDir ? grCfg.customLibraryDir : `${fb.ProfilePath}cache\\library\\library-tree-cache`; + try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} + OpenExplorer(`explorer /open, "${cacheDir}"`, false); + }); + themeCacheLibraryMenu.addItem('Delete library cache', false, () => { + const msg = 'Do you want to delete the library cache?\n\nThis will permanently delete cached library album art thumbnails.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) DeleteLibraryCache(); + }); + }); + themeCacheLibraryMenu.addSeparator(); + themeCacheLibraryMenu.addToggleItem('Auto-delete library cache on startup', grSet, 'libraryAutoDelete', () => { + const msg = 'Do you want to set auto-delete for library cache?\n\nThis will always auto-delete cached library album art thumbnails on startup.\n\nContinue?\n\n\n'; + if (grSet.libraryAutoDelete) { + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + grSet.libraryAutoDelete = confirmed; + }); } - catch (e) { - if (e.message === 'Invalid type' || e.name === 'SyntaxError') { - fb.ShowPopupMessage(`Path is not valid:\n${input}\n\nDo not use any " at the beginning and the end of your pattern.\n\nExample of a correct path:\n\nD:\\Stuff\\Directory\\`, `Custom ${customDirString} directory`); - } - return; + }); + themeCacheLibraryMenu.appendTo(themeCacheMenu); + + const themeCacheBiographyMenu = new Menu('Biography'); + themeCacheBiographyMenu.addToggleItem('Use custom biography directory', grSet, 'customBiographyDir', () => { + if (grSet.customBiographyDir) { + grm.inputBox.customCacheDir('biography'); + const bioCfg = new BioSettings(); + bioCfg.resetCfg(); } - configCustom.addConfigurationObject(customDirSchema, [newVal]); - configCustom.writeConfiguration(); - } - break; + window.Reload(); + }); + themeCacheBiographyMenu.addSeparator(); + themeCacheBiographyMenu.addItem('Open biography cache directory', false, () => { + const cacheDir = grSet.customBiographyDir ? grCfg.customBiographyDir : `${fb.ProfilePath}cache\\biography\\biography-cache`; + try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} + OpenExplorer(`explorer /open, "${cacheDir}"`, false); + }); + themeCacheBiographyMenu.addItem('Delete biography cache', false, () => { + const msg = 'Do you want to delete the biography cache?\n\nThis will permanently delete downloaded biography images and text files\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) DeleteBiographyCache(); + }); + }); + themeCacheBiographyMenu.addSeparator(); + themeCacheBiographyMenu.addToggleItem('Auto-delete biography cache on startup', grSet, 'biographyAutoDelete', () => { + const msg = 'Do you want to set auto-delete for biography cache?\n\nThis will always auto-delete downloaded biography images\nand text on startup\n\nContinue?\n\n\n'; + if (grSet.biographyAutoDelete) { + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + grSet.biographyAutoDelete = confirmed; + }); + } + }); + themeCacheBiographyMenu.appendTo(themeCacheMenu); - case 'themeDayNightModeCustom': { - const oldValues = Array.isArray(themeSettings.themeDayNightMode) ? themeSettings.themeDayNightMode.join('-') : themeSettings.themeDayNightMode || '6-18'; - let input; - try { - input = utils.InputBox(window.ID, 'Enter your custom day-night mode (e.g 6-18):', 'Georgia-ReBORN', oldValues, true); + const themeCacheLyricsMenu = new Menu('Lyrics'); + themeCacheLyricsMenu.addToggleItem('Use custom lyrics directory', grSet, 'customLyricsDir', () => { + if (grSet.customLyricsDir) grm.inputBox.customCacheDir('lyrics'); + window.Reload(); + }); + themeCacheLyricsMenu.addSeparator(); + themeCacheLyricsMenu.addItem('Open lyrics directory', false, () => { + const cacheDir = grSet.customLyricsDir ? grCfg.customLyricsDir : `${fb.ProfilePath}cache\\lyrics`; + try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} + OpenExplorer(`explorer /open, "${cacheDir}"`, false); + }); + themeCacheLyricsMenu.addItem('Delete lyrics', false, () => { + const msg = 'Do you want to delete all lyrics?\n\nThis will permanently delete downloaded lyrics.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) DeleteLyrics(); + }); + }); + themeCacheLyricsMenu.addSeparator(); + themeCacheLyricsMenu.addToggleItem('Auto-delete lyrics on startup', grSet, 'lyricsAutoDelete', () => { + const msg = 'Do you want to set auto-delete for lyrics?\n\nThis will always auto-delete downloaded lyrics on startup.\n\nContinue?\n\n\n'; + if (grSet.lyricsAutoDelete) { + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + grSet.lyricsAutoDelete = confirmed; + }); + } + }); + themeCacheLyricsMenu.appendTo(themeCacheMenu); + + const themeCacheWaveformBarMenu = new Menu('Waveform bar'); + themeCacheWaveformBarMenu.addToggleItem('Use custom waveform bar directory', grSet, 'customWaveformBarDir', () => { + if (grSet.customWaveformBarDir) grm.inputBox.customCacheDir('waveformBar'); + window.Reload(); + }); + themeCacheWaveformBarMenu.addSeparator(); + themeCacheWaveformBarMenu.addItem('Open waveform bar cache directory', false, () => { + const cacheDir = grSet.customWaveformBarDir ? grCfg.customWaveformBarDir : `${fb.ProfilePath}cache\\waveform`; + try { if (!IsFolder(cacheDir)) CreateFolder(cacheDir); } catch (e) {} + OpenExplorer(`explorer /open, "${cacheDir}"`, false); + }); + themeCacheWaveformBarMenu.addItem('Delete waveform bar cache', false, () => { + const msg = 'Do you want to delete all waveform bar cache?\n\nThis will permanently delete analyzed files.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) DeleteWaveformBarCache(); + }); + }); + + themeCacheWaveformBarMenu.addToggleItem('Auto-delete waveform bar cache on startup', grSet, 'waveformBarAutoDelete', () => { + const msg = 'Do you want to set auto-delete for waveform bar?\n\nThis will always auto-delete waveform bar cache on startup.\n\nContinue?\n\n\n'; + if (grSet.waveformBarAutoDelete) { + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + grSet.waveformBarAutoDelete = confirmed; + }); + } + }); + themeCacheWaveformBarMenu.appendTo(themeCacheMenu); + themeCacheMenu.appendTo(settingsMenu); + + // * THEME BACKUP * // + const themeBackupMenu = new Menu('Theme backup'); + themeBackupMenu.addItem('Make backup', false, () => { + const msg = `Do you want to make a backup of the theme?\n\nThis will create a backup in ${fb.ProfilePath}backup\n\nOn new fb2k installation, you can copy/paste and replace it with ${fb.ProfilePath}\n\nIf a backup already exist, you can use\nOptions > Settings > Theme backup > Restore backup\n\nContinue?\n\n\n`; + const msgFb = `You can find the Georgia-ReBORN theme backup in ${fb.ProfilePath}backup\n\nOn new fb2k installation, you can copy/paste and replace it with ${fb.ProfilePath}\n\nIf a backup already exist, you can use\nOptions > Settings > Theme backup > Restore backup`; + ShowPopup(true, msgFb, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) ManageBackup(true); + }); + }); + + themeBackupMenu.addItem('Restore backup', false, () => { + const msg = `Do you want to restore your backup of the theme?\n\n>>> WARNING <<<\n\nThis will restore your backup from ${fb.ProfilePath}\n\nChanges and modifications since your last backup\n(new theme settings, new playlists and play statistics)\nwill be lost!\n\nIt is recommended to make a new backup\nbefore you restore.\n\nContinue?\n\n\n`; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (confirmed) ManageBackup(false, true); + }); + }); + themeBackupMenu.appendTo(settingsMenu); - const validFormat = /^\s*(\d+)\s*-\s*(\d+)\s*$/; // Regex to match a valid input format - const match = input.match(validFormat); - if (!match) throw new Error('Invalid format'); + // * THEME CONFIGURATION * // + const themeConfigMenu = new Menu('Theme configuration'); + themeConfigMenu.addItem('Save settings to config file', false, () => { + const msg = 'Do you want to save all current theme settings?\n\nThis will overwrite all settings from the top menu "Options"\nin the georgia-reborn-config.jsonc file.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + const start = async () => { + await grm.settings.setThemeSettings(true); + await window.Reload(); + }; + start(); + console.log(`\n>>> Georgia-ReBORN theme settings have been successfully saved in ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc <<<\n\n`); + }); + }); + themeConfigMenu.addItem('Load settings from config file', false, () => { + const msg = 'Do you want to load all theme settings\nfrom the georgia-reborn-config.jsonc file?\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + const start = async () => { + await grm.settings.setThemeSettings(false, true); + await window.Reload(); + }; + start(); + console.log(`\n>>> Georgia-ReBORN theme settings have been successfully loaded from ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc <<<\n\n`); + }); + }); + themeConfigMenu.addSeparator(); + themeConfigMenu.addItem('Load default settings', false, () => { + const msg = 'Do you want to load default theme settings?\n\nThis will not overwrite the georgia-reborn-config.jsonc file,\nbut you should probably first save your settings.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + const start = async () => { + await grm.settings.setThemeSettings(false, false, true); + await grm.display.autoDetectRes(); + await window.Reload(); + }; + start(); + console.log('\n>>> Default Georgia-ReBORN theme settings have been successfully loaded <<<\n\n'); + }); + }); + themeConfigMenu.addSeparator(); + themeConfigMenu.addItem('Edit main configuration file', false, () => { + try { + OpenFile(`${grCfg.config.getPath()}`); + } catch (e) { + OpenExplorer(`explorer /select, "${grCfg.config.getPath()}"`, false); + } + }); + themeConfigMenu.addItem('Edit custom configuration file', false, () => { + try { + OpenFile(`${grCfg.configCustom.getPath()}`); + } catch (e) { + OpenExplorer(`explorer /select, "${grCfg.configCustom.getPath()}"`, false); + } + }); + themeConfigMenu.addSeparator(); + themeConfigMenu.addItem('Reset main configuration file', false, () => { + const msg = 'Do you want to reset the config file to default?\n\n!!! WARNING !!!\n\nThis will set all settings to default.\nYou should probably make a backup first.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + try { // Needed to prevent crash when there is no config file + grCfg.config.resetConfiguration(); + grm.settings.setThemeSettings(false, false, true); + grm.display.layoutDefault(); + console.log(`\n>>> Georgia-ReBORN's ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc file has been successfully reset to default. <<<\n\n`); + } catch (e) { window.Reload(); } + }); + }); + themeConfigMenu.addItem('Reset custom configuration file', false, () => { + const msg = 'Do you want to reset the custom config file to default?\n\n!!! WARNING !!!\n\nThis will delete and replace all custom themes\nto the default custom theme template.\nYou should definitely make a backup first.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + try { // Needed to prevent crash when there is no config file + grCfg.configCustom.resetConfiguration(); + console.log(`\n>>> Georgia-ReBORN's ${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc file has been successfully reset to default. <<<\n\n`); + } catch (e) { window.Reload(); } + }); + }); + themeConfigMenu.addSeparator(); + themeConfigMenu.addItem('Reset all', false, () => { + const msg = 'Do you want to reset all theme settings to default?\n\nThis will also clear all library custom views plus filters\nand Georgia-ReBORN config.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + grSet.systemFirstLaunch = true; // Reset Georgia-ReBORN theme settings + try { // Needed to prevent crash when there is no config file + fso.DeleteFile(`${fb.ProfilePath}configuration\\foo_ui_columns.dll.cfg`); + grCfg.config.resetConfiguration(); // Reset Georgia-ReBORN config file + lib.panel.updateProp(libSet, 'default_value'); // Reset Library settings + bio.ui.updateProp(bioSet, 'default_value'); // Reset Biography settings + const server = new BioSettings(); + server.resetCfg(); // Reset Biography server settings + console.log('\n>>> Georgia-ReBORN has been successfully reset <<<\n\n'); + } catch (e) { + fb.ShowPopupMessage('Something went wrong and Georgia-ReBORN has NOT been successfully reset, try again!', 'Resetting Georgia-ReBORN'); + } + }); + }); + themeConfigMenu.appendTo(settingsMenu); - const startTime = Number(match[1]); - const endTime = Number(match[2]); - if (startTime === endTime || startTime < 0 || startTime > 23 || endTime < 0 || endTime > 23) { - throw new Error('Invalid time'); + // * THEME PERFORMANCE * // + settingsMenu.createRadioSubMenu('Theme performance', ['Lowest quality (fastest speed - very slow CPU)', 'Low quality', 'Balanced (Default)', 'High quality', 'Highest quality (slowest speed - very fast CPU)'], grSet.themePerformance, + ['lowestQuality', 'lowQuality', 'balanced', 'highQuality', 'highestQuality'], (perf) => { + const msg = 'Do you want to change the theme performance?\n\nThese presets will change various theme settings!\nIt is recommended to save current theme settings\nto the config file. You should also make a backup\nof your playlists to be on the safe side!\n\n!!! WARNING !!!\n"High quality" and especially "Highest Quality"\ncan freeze foobar, depending how fast your CPU performs.\nIt does not matter if you are using a multi-core CPU,\nonly single-core CPU performance counts!\nIf your foobar is unresponsive, restart\nand change to a lighter preset.\n\nContinue?'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + grSet.themePerformance = perf; + grm.settings.setThemePerformance('balanced'); // First reset + grm.settings.setThemePerformance(grSet.themePerformance); // Then set + window.Reload(); + }); + }); + settingsMenu.addSeparator(); + + // * OTHER SETTINGS * // + settingsMenu.addItem('Output device', false, () => { + const outputDeviceMenu = (x, y) => { + const menu = window.CreatePopupMenu(); + const output = fb.GetOutputDevices(); + const deviceList = JSON.parse(output); + const last = deviceList.length + 1 + let active = -1; + for (const [i, array] of deviceList.entries()) { + menu.AppendMenuItem(MF_STRING, i + 1, array.name); + if (array.active) active = i; } + menu.AppendMenuSeparator(); + menu.AppendMenuItem(MF_STRING, last, 'Preferences...'); - themeSettings.themeDayNightMode = pref.themeDayNightMode = input; + if (active > -1) menu.CheckMenuRadioItem(1, last, active + 1); + const idx = menu.TrackPopupMenu(x, y); + if (idx > 0 && idx < last) fb.RunMainMenuCommand(`Playback/Device/${deviceList[idx - 1].name}`); + else if (idx === last) fb.RunMainMenuCommand('Playback/Device/Preferences...'); + }; + outputDeviceMenu(grm.ui.state.mouse_x, grm.ui.state.mouse_y); + }); + + settingsMenu.addSeparator(); + settingsMenu.addToggleItem('Developer tools', grSet, 'devTools', () => { grSet.disableRightClick = !grSet.devTools; }); + settingsMenu.addSeparator(); + settingsMenu.addToggleItem('Disable right-click', grSet, 'disableRightClick'); + + settingsMenu.appendTo(menu); + } + + /** + * Top menu > Options > Developer tools. + * @param {Menu} menu - Creates Developer tools menu via a new Menu instance. + * @protected + */ + developerToolsOptions(menu) { + if (!grSet.devTools) return; + const debugMenu = new Menu('Developer tools'); + + const clearAutoDownloadBio = () => { + grm.ui.autoDownloadBio = false; + grm.button.setPlaybackOrder(grm.ui.btnImg.PlaybackDefault, 'default', PlaybackOrder.Default, 'Default'); + grm.ui.displayBiography = false; + clearInterval(grm.ui.autoDownloadBioTimer); + }; + + const clearAutoDownloadLyrics = () => { + grm.ui.autoDownloadLyrics = false; + grm.button.setPlaybackOrder(grm.ui.btnImg.PlaybackDefault, 'default', PlaybackOrder.Default, 'Default'); + grm.ui.displayLyrics = false; + clearInterval(grm.ui.autoDownloadLyricsTimer); + }; + + debugMenu.addItem('Console', false, () => { fb.RunMainMenuCommand('View/Console'); }); // Top menu 'View' does not exist in Artwork/Compact layout + debugMenu.addSeparator(); + debugMenu.addToggleItem('Enable auto-download biography', grm.ui, 'autoDownloadBio', () => { + if (!grm.ui.autoDownloadBio) { + clearAutoDownloadBio(); + } else { + const msg = 'Do you want to enable\nthe auto-download biography mode?\n\nThis will set the playback order to shuffle\nand activate a 6-second timer to automatically\ndownload the biography.\n\nThis is recommended when you leave your PC\nunattended for a longer period of time.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + grm.ui.autoDownloadBio = false; + return; + } + clearAutoDownloadLyrics(); + fb.Play(); + grm.button.setPlaybackOrder(grm.ui.btnImg.PlaybackShuffle, 'shuffle', PlaybackOrder.ShuffleTracks, 'Shuffle (tracks)'); + grm.ui.displayBiography = true; + grm.ui.autoDownloadBioTimer = setInterval(() => { fb.Next(); }, 6000); + }); + } + grm.ui.btn.playbackOrder.repaint(); + grm.button.initButtonState(); + window.Repaint(); + }); + debugMenu.addToggleItem('Enable auto-download lyrics', grm.ui, 'autoDownloadLyrics', () => { + if (!grm.ui.autoDownloadLyrics) { + clearAutoDownloadLyrics(); + } else { + const msg = 'Do you want to enable\nthe auto-download lyrics mode?\n\nThis will set the playback order to playlist\nand activate a 15-second timer to automatically\ndownload the lyrics.\n\nThis is recommended when you leave your PC\nunattended for a longer period of time.\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) { + grm.ui.autoDownloadLyrics = false; + return; + } + clearAutoDownloadBio(); + fb.Play(); + grm.button.setPlaybackOrder(grm.ui.btnImg.PlaybackRepeatPlaylist, 'repeatPlaylist', PlaybackOrder.RepeatPlaylist, 'Repeat (playlist)'); + grm.ui.displayLyrics = true; + grm.lyrics.initLyrics(); + grm.ui.autoDownloadLyricsTimer = setInterval(() => { fb.Next(); }, 15000); + }); + } + grm.ui.btn.playbackOrder.repaint(); + grm.button.initButtonState(); + window.Repaint(); + }); + debugMenu.addSeparator(); + debugMenu.addToggleItem('Enable double click refresh', grCfg.settings, 'doubleClickRefresh'); + debugMenu.addSeparator(); + debugMenu.addToggleItem('Enable debug output', grCfg.settings, 'showDebugLog'); + debugMenu.addItem('Enable debug theme output', grCfg.settings.showDebugThemeLog, () => { + grCfg.settings.showDebugThemeLog = !grCfg.settings.showDebugThemeLog; + if (grCfg.settings.showDebugThemeLog) { + grm.ui.albumArt = null; + on_playback_new_track(fb.GetNowPlaying()); + } + }); + debugMenu.addItem('Enable debug theme overlay', grCfg.settings.showDebugThemeOverlay, () => { + grCfg.settings.showDebugThemeOverlay = !grCfg.settings.showDebugThemeOverlay; + if (grCfg.settings.showDebugThemeOverlay) { + grm.ui.albumArt = null; + on_playback_new_track(fb.GetNowPlaying()); + } else { + RepaintWindow(); + } + }); + debugMenu.addSeparator(); + debugMenu.addToggleItem('Show draw timing', grm.ui, 'showDrawTiming'); + debugMenu.addToggleItem('Show extra draw timing', grm.ui, 'showExtraDrawTiming'); + debugMenu.addToggleItem('Show debug timing', grm.ui, 'showDebugTiming'); + debugMenu.addSeparator(); + debugMenu.addToggleItem('Show ram usage', grm.ui, 'showRamUsage'); + debugMenu.addToggleItem('Show draw areas', grm.ui, 'drawRepaintRects', () => { + if (grm.ui.drawRepaintRects) { + RepaintRectAreas(); + } else { + RepaintWindow(); } - catch (e) { - if (e.message === 'Invalid format' || e.message === 'Invalid time') { - fb.ShowPopupMessage(`Input is not valid: ${input}\n\nPlease enter valid times in 24-hour format separated by a hyphen (e.g 6-18), where both times are between 0 and 23.`, 'Custom Day/Night Mode'); + }); + debugMenu.addSeparator(); + debugMenu.addToggleItem('Show panel calls', grm.ui, 'showPanelTraceCall', () => { + if (grm.ui.showPanelTraceCall) { + grm.ui.traceCall = true; + } else { + grm.ui.traceCall = false; + if (grm.ui.showPanelTraceOnMove) { + grm.ui.traceOnMove = false; + grm.ui.showPanelTraceOnMove = false; } } - config.updateConfigObjValues('themeSettings', true); - initThemeDayNightMode(new Date()); - resetTheme(); - initThemeFull = true; - if (pref.theme.startsWith('custom')) initCustomTheme(); - if (!fb.IsPlaying) setThemeColors(); - initTheme(); - initStyleState(); - initThemePresetState(); - break; - } + }); + debugMenu.addToggleItem('Show panel moves', grm.ui, 'showPanelTraceOnMove', () => { + if (grm.ui.showPanelTraceOnMove) { + grm.ui.traceOnMove = true; + grm.ui.traceCall = true; + grm.ui.showPanelTraceCall = true; + return; + } + grm.ui.traceOnMove = false; + grm.ui.traceCall = false; + grm.ui.showPanelTraceCall = false; + }); + debugMenu.addToggleItem('Show playlist performance', grm.ui, 'showPlaylistTraceListPerf', () => { + grm.ui.traceListPerformance = !grm.ui.traceListPerformance; + }); + debugMenu.addSeparator(); + debugMenu.addItem('Set system first launch to true', false, () => { // Used when creating new config files + const msg = 'Do you really want to set system to first launch?\n\nContinue?\n\n\n'; + ShowPopup(false, false, msg, 'Yes', 'No', (confirmed) => { + if (!confirmed) return; + window.SetProperty('Georgia-ReBORN - 16. System: System first launch', true); + plSet.show_scrollbar = false; + grSet.showTopMenuCompact = true; + grSet.devTools = false; + grSet.disableRightClick = true; + console.log('\n>>> Georgia-ReBORN has been set to system first launch <<<\n\n'); + }); + }); + + debugMenu.appendTo(menu); } + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-settings.js b/profile/georgia-reborn/scripts/Base/gr-settings.js index 5ca789ae..783b3322 100644 --- a/profile/georgia-reborn/scripts/Base/gr-settings.js +++ b/profile/georgia-reborn/scripts/Base/gr-settings.js @@ -1,117 +1,153 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Settings * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Settings * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////// -// * VARIABLES * // -/////////////////// -/** @type {string} The Georgia-ReBORN config file path. */ -const configPath = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-config.jsonc`; -/** @type {Configuration} The Georgia-ReBORN config object. */ -const config = new Configuration(configPath); -/** @type {string} The Georgia-ReBORN custom config file path. */ -const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; -/** @type {Configuration} The Georgia-ReBORN custom config object. */ -const configCustom = new Configuration(configPathCustom); -/** @type {string} The Georgia-ReBORN current version. */ -const currentVersion = '3.0-DEV'; -/** @type {string} The Georgia-ReBORN version will be overwritten when loaded from config file. */ -let configVersion = currentVersion; -/** @type {string} The Georgia-ReBORN version will be shown on the right side of the lower bar when nothing is playing. */ -let lowerBarStoppedTime = `Georgia-ReBORN v${currentVersion}`; -/** @type {boolean} The update state if a new update is available on Github releases page. */ -let updateAvailable = false; -/** @type {Hyperlink} The update link will be shown on the right side of the lower bar. */ -let updateHyperlink; -/** @type {number} The update retry, don't hammer the server if it's not working, used only in checkForUpdates(). */ -let updateRetryCount = 0; -/** @type {number} The update timeout timer used only in scheduleUpdateCheck(). */ -let updateTimer; - -/** @type {*} */ -const globals = {}; -/** @type {*} */ -let settings = {}; -/** @type {*} */ -let theme = {}; -/** @type {*} */ -let style = {}; -/** @type {*} */ -let preset = {}; -/** @type {*} */ -let themePlayerSize = {}; -/** @type {*} */ -let themeLayout = {}; -/** @type {*} */ -let themeDisplay = {}; -/** @type {*} */ -let themeBrightness = {}; -/** @type {*} */ -let themeFontSize = {}; -/** @type {*} */ -let themeControls = {}; -/** @type {*} */ -let themePlaylist = {}; -/** @type {*} */ -let themeDetails = {}; -/** @type {*} */ -let themeLibrary = {}; -/** @type {*} */ -let themeBiography = {}; -/** @type {*} */ -let themeLyrics = {}; -/** @type {*} */ -let themeSettings = {}; -/** @type {MetadataGridEntry[]} */ -let metadataGrid; - -// * Custom theme -/** @type {*} */ -let customFont = {}; -/** @type {*} */ -let customStylePreset = {}; -/** @type {*} */ -let customDiscArtStub = {}; -/** @type {*} */ -let customTheme01 = {}; -/** @type {*} */ -let customTheme02 = {}; -/** @type {*} */ -let customTheme03 = {}; -/** @type {*} */ -let customTheme04 = {}; -/** @type {*} */ -let customTheme05 = {}; -/** @type {*} */ -let customTheme06 = {}; -/** @type {*} */ -let customTheme07 = {}; -/** @type {*} */ -let customTheme08 = {}; -/** @type {*} */ -let customTheme09 = {}; -/** @type {*} */ -let customTheme10 = {}; - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// ! THEME PROPERTIES - AFTER INITIAL RUN, THESE VALUES ARE CHANGED IN OPTIONS MENU OR BY RIGHT CLICK >> PROPERTIES AND NOT HERE ! // -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -pref.add_properties({ - version: ['Georgia-ReBORN - #Version: Do not hand edit!', currentVersion], - - // * Settings chronological ordered by top menu > Options +////////////////////////// +// * PANEL PROPERTIES * // +////////////////////////// +/** + * A class that creates an object with a name and a value, and provides methods to get and set the value while also storing. + */ +class PanelProperty { + /** + * Creates the `PanelProperty` instance. + * @param {string} name - The name of the property, used as a key to store and retrieve the property value. + * @param {*} defaultValue - The initial value that will be used if there is no existing value stored for the property. + */ + constructor(name, defaultValue) { + /** @constant {string} */ + this.name = name; + /** @private @type {*} */ + this.value = window.GetProperty(this.name, defaultValue); + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Gets the current value of the panel property (backward compatibility method). + * @returns {*} The current value of the property. + */ + get() { + return this.value; + } + + /** + * Sets the panel property to a new value and updates the internal state as well as the stored value. + * @param {*} newValue - The new value to set for the property. + */ + set(newValue) { + if (this.value !== newValue) { + window.SetProperty(this.name, newValue); + this.value = newValue; + } + } + // #endregion +} + + +/** + * A class that allows to add and manage SMP properties with their names and default values. + */ +class PanelProperties { + /** + * Creates the `PanelProperties` instance. + */ + constructor() { + /** @private @type {{[key: string]: boolean}} An object used for collision checks only and shared between objects. */ + this.properties_name_list = {}; + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Adds a new property to an object and creates a getter and setter for that property. + * @param {Array} item - An array that contains the name and default value. + * @param {string} itemId - A unique identifier for the property item. + * @returns {*} Defining a new property on an object and setting its getter and setter methods. + * @private + */ + _addPropertyItem(item, itemId) { + this.properties_name_list[item[0]] = 1; + + this[`${itemId}_internal`] = new PanelProperty(item[0], item[1]); + + Object.defineProperty(this, itemId, { + get() { + return this[`${itemId}_internal`].get(); + }, + set(newValue) { + this[`${itemId}_internal`].set(newValue); + } + }); + } + + /** + * Validates a property item and throws appropriate errors if any validation fails. + * @param {Array} item - An array where the first element is a string representing the property name, and the second element is the default value of the property. + * @param {string} itemId - A unique identifier for the property item. + * @throws {InvalidTypeError} Throws an error if the property item is not an array of the expected format. + * @throws {ArgumentError} Throws an error if the property_id is reserved, already occupied, or if the property_name is already taken. + * @private + */ + _validatePropertyItem(item, itemId) { + if (!Array.isArray(item) || item.length !== 2 || !IsString(item[0])) { + throw new InvalidTypeError('property', typeof item, '{ string, [string, any] }', 'Usage: addProperties({\n property_id: [property_name, property_default_value]\n})'); + } + if (itemId === 'add_properties') { + throw new ArgumentError('property_id', itemId, 'This id is reserved'); + } + if (this[itemId] || this[`${itemId}_internal`]) { + throw new ArgumentError('property_id', itemId, 'This id is already occupied'); + } + if (this.properties_name_list[item[0]]) { + throw new ArgumentError('property_name', item[0], 'This name is already occupied'); + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Adds multiple properties to the object by defining a new property with a getter and setter for each item in the `properties` object. + * @param {{[key: string]: [string, *]}} properties - An object where keys are property identifiers, and values are arrays containing the property name and default value. + */ + addProperties(properties) { + for (const key of Object.keys(properties)) { + this._validatePropertyItem(properties[key], key); + this._addPropertyItem(properties[key], key); + } + } + // #endregion +} + + +//////////////////////// +// * THEME SETTINGS * // +//////////////////////// +/** + * The instance of `PanelProperties` class for Georgia-ReBORN panel property settings. + * @typedef {PanelProperties} + * @global + */ +const grSet = new PanelProperties(); + +/** + * ! AFTER INITIAL RUN, THESE VALUES ARE CHANGED IN OPTIONS MENU OR BY RIGHT CLICK > PROPERTIES AND NOT HERE. + * Settings chronological ordered by top menu > Options. + */ +grSet.addProperties({ + version: ['Georgia-ReBORN - #Version: Do not hand edit!', '3.0-DEV'], // * Theme theme: ['Georgia-ReBORN - 01. Theme:', 'reborn'], // Use reborn theme as default @@ -370,6 +406,7 @@ pref.add_properties({ showPanelOnStartup: ['Georgia-ReBORN - 09. Player controls: Show panel on startup', 'playlist'], // "cover", "playlist", "details", "library", "biography", "lyrics" - show panel on foobar startup showPreloaderLogo: ['Georgia-ReBORN - 09. Player controls: Show logo on preloader', true], // true: Show logo on preloader returnToHomeOnPlaybackStop: ['Georgia-ReBORN - 09. Player controls: Return to home on playback stop', true], // true: Return to home on playback stop + addTracksPlaylistSwitch: ['Georgia-ReBORN - 09. Player controls: Switch to playlist when adding songs', false], // When adding songs from Library or Playlist to another playlist hideMiddlePanelShadow: ['Georgia-ReBORN - 09. Player controls: Hide middle panel shadow', false], // false: Hides the middle panel shadow lockPlayerSize: ['Georgia-ReBORN - 09. Player controls: Lock player size', false], // false: Locks the player size transportButtonSize_default: ['Georgia-ReBORN - 09. Player controls: Transport button size (Default)', 32], // Size in pixels of the buttons in Default layout @@ -387,6 +424,9 @@ pref.add_properties({ showReloadBtn_default: ['Georgia-ReBORN - 09. Player controls: Show reload button (Default)', false], // false: Show reload button in lower bar in Default layout showReloadBtn_artwork: ['Georgia-ReBORN - 09. Player controls: Show reload button (Artwork)', false], // false: Show reload button in lower bar in Artwork layout showReloadBtn_compact: ['Georgia-ReBORN - 09. Player controls: Show reload button (Compact)', false], // false: Show reload button in lower bar in Compact layout + showAddTracksBtn_default: ['Georgia-ReBORN - 09. Player controls: Show add tracks button (Default)', false], // false: Show add tracks button in lower bar in Default layout + showAddTracksBtn_artwork: ['Georgia-ReBORN - 09. Player controls: Show add tracks button (Artwork)', false], // false: Show add tracks button in lower bar in Artwork layout + showAddTracksBtn_compact: ['Georgia-ReBORN - 09. Player controls: Show add tracks button (Compact)', false], // false: Show add tracks button in lower bar in Compact layout showVolumeBtn_default: ['Georgia-ReBORN - 09. Player controls: Show volume button (Default)', true], // true: Show volume button in lower bar in Default layout showVolumeBtn_artwork: ['Georgia-ReBORN - 09. Player controls: Show volume button (Artwork)', true], // true: Show volume button in lower bar in Artwork layout showVolumeBtn_compact: ['Georgia-ReBORN - 09. Player controls: Show volume button (Compact)', true], // true: Show volume button in lower bar in Compact layout @@ -421,6 +461,7 @@ pref.add_properties({ showWaveformBar_default: ['Georgia-ReBORN - 09. Player controls: Show waveform bar (Default)', true], // true: Show waveform bar in Default layout, otherwise hide it (useful is using another panel for this) showWaveformBar_artwork: ['Georgia-ReBORN - 09. Player controls: Show waveform bar (Artwork)', true], // true: Show waveform bar in Artwork layout, otherwise hide it (useful is using another panel for this) showWaveformBar_compact: ['Georgia-ReBORN - 09. Player controls: Show waveform bar (Compact)', true], // true: Show waveform bar in Compact layout, otherwise hide it (useful is using another panel for this) + addTracksPlaylist: ['Georgia-ReBORN - 09. Player controls: Add tracks playlist', 'Favorites'], // 'Favorites', the playlist where tracks will be added when using the add tracks button. seekbar: ['Georgia-ReBORN - 09. Player controls: Seekbar', 'progressbar'], // 'progressbar', 'peakmeterbar', 'waveformbar' - Seekbar type progressBarWheelSeekSpeed: ['Georgia-ReBORN - 09. Player controls: Progress bar mouse wheel seek speed', 5], // Progress bar mouse wheel seeking speed, seconds per wheel step progressBarRefreshRate: ['Georgia-ReBORN - 09. Player controls: Progress bar refresh rate', 'variable'], // variable - default: Update progress bar multiple times a second. Smoother, but uses more CPU @@ -535,7 +576,6 @@ pref.add_properties({ libraryThumbnailSize: ['Georgia-ReBORN - 12. Library: Thumbnail size', 'auto'], // Library thumbnail size - auto (default) libraryThumbnailSizeSaved: ['Georgia-ReBORN - 12. Library: Thumbnail size saved', 'auto'], // Library thumbnail size saved setting, used to restore user setting when switching from library split layout to full layout libraryThumbnailBorder: ['Georgia-ReBORN - 12. Library: Thumbnail border', 'border'], // Library thumbnail border - border (default) - libraryPlaylistSwitch: ['Georgia-ReBORN - 12. Library: Switch to playlist when adding songs', false], // When adding songs from Library auto-switch to Playlist libraryRowHover: ['Georgia-ReBORN - 12. Library: Row mouse hover', true], // Enable library row mouse hover effect savedAlbumArtShow: ['Georgia-ReBORN - 12. Library: Album art view saved', false], // Used to resume library view mode from split layout when not using library layout presets savedAlbumArtLabelType: ['Georgia-ReBORN - 12. Library: Album art label type saved', 1], // Used to resume album art label type from split layout when not using library layout presets @@ -555,12 +595,11 @@ pref.add_properties({ lyricsFadeScroll: ['Georgia-ReBORN - 14. Lyrics: Show fade scroll', true], // true: Show lyrics fade scroll lyricsLargerCurrentSync: ['Georgia-ReBORN - 14. Lyrics: Show larger current sync', true], // true: Displays larger font on current synced lyric lyricsAlbumArt: ['Georgia-ReBORN - 14. Lyrics: Show lyrics on album art', true], // true: Show lyrics on album art - lyricsRememberActiveState: ['Georgia-ReBORN - 14. Lyrics: Remember lyrics active state', false], // true: Show active lyrics even when switching through panels - lyricsRememberPanelState: ['Georgia-ReBORN - 14. Lyrics: Remember lyrics panel state', false], // true: Show lyrics on startup if they were displayed when theme last reloaded + lyricsRememberPanelState: ['Georgia-ReBORN - 14. Lyrics: Remember lyrics panel state', false], // true: Displays lyrics panel state on startup and when switching through panels lyricsScrollSpeed: ['Georgia-ReBORN - 14. Lyrics: Scroll speed', 'normal'], // 'fastest', 'fast', 'normal', 'slow', 'slowest' - lyrics scroll speed based on scroll average and maximum lyricsScrollRateAvg: ['Georgia-ReBORN - 14. Lyrics: Scroll speed avg rate', 750], // 300, 500, 750, 1000, 1500 - average lyrics scroll in ms lyricsScrollRateMax: ['Georgia-ReBORN - 14. Lyrics: Scroll speed max rate', 375], // average lyrics scroll / 2 = maximum lyrics scroll in ms - displayLyrics: ['Georgia-ReBORN - 14. Lyrics: Show lyrics', false], // true: Shows lyrics, always set to false at startup unless lyricsRememberDisplay is true + lyricsPanelState: ['Georgia-ReBORN - 14. Lyrics: Lyrics panel state', false], // false: Saved the lyrics panel state, used for lyricsRememberPanelState // * Settings themeDayNightMode: ['Georgia-ReBORN - 15. Settings: Auto-day/night mode', false], // false: The theme day/night mode state controlled by OS clock and users set themeSettings.themeDayNightMode value @@ -578,7 +617,7 @@ pref.add_properties({ customLyricsDir: ['Georgia-ReBORN - 15. Settings: Use custom lyrics directory', false], // false: Use custom lyrics directory lyricsAutoDelete: ['Georgia-ReBORN - 15. Settings: Auto-delete lyrics', false], // false: This will auto-delete downloaded lyrics on startup customWaveformBarDir: ['Georgia-ReBORN - 15. Settings: Use custom waveform bar directory', false], // false: Use custom waveform bar directory - waveformBarAutoDelete: ['Georgia-ReBORN - 15. Settings: Auto-delete waveform bar cache', false], // false: This will auto-delete analyized waveform bar files on startup + waveformBarAutoDelete: ['Georgia-ReBORN - 15. Settings: Auto-delete waveform bar cache', false], // false: This will auto-delete analyzed waveform bar files on startup customThemeSettings: ['Georgia-ReBORN - 15. Settings: Use custom theme settings', true], // true: User can set own custom theme settings in the config file themePerformance: ['Georgia-ReBORN - 15. Settings: Theme performance', 'balanced'], // 'balanced' - default: How the theme performs, either fast speed, balanced or good quality depending on CPU devTools: ['Georgia-ReBORN - 15. Settings: Enable developer tools', false], // true: Show developer tools in options context menu @@ -596,1715 +635,988 @@ pref.add_properties({ savedWidth_compact: ['Georgia-ReBORN - 16. System: Saved width (Compact)', 484], // Default saved width for Compact layout savedHeight_compact: ['Georgia-ReBORN - 16. System: Saved height (Compact)', 730], // Default saved height for Compact layout systemFirstLaunch: ['Georgia-ReBORN - 16. System: System first launch', true] // true: Init and reset to theme factory settings - - // check_multich: ['Check for MultiChannel version', false] // true: Search paths in tf.MultiCh_paths to see if there is a multichannel version of the current album available }); -// * Fixup properties -/** @type {string} */ -const savedLayout = pref.savedLayout; -if (savedLayout !== 'default' || savedLayout !== 'artwork' || savedLayout !== 'compact') { - pref.savedLayout = 'default'; -} - - -//////////////////////// -// * THEME SETTINGS * // -//////////////////////// +//////////////////////////////// +// * THEME SETTINGS MANAGER * // +//////////////////////////////// /** - * Loads default theme settings when pref.customThemeSettings is false, otherwise it loads settings from the config file. - * When using with the parameter, it saves all settings to the config file. - * @param {boolean} save Saves current used theme settings to config file. + * A class that manages theme settings for the application, allowing to save, load, and apply default configurations. */ -async function setThemeSettings(save) { - const custom = pref.customThemeSettings; - libraryCanReload = false; - - // * Themes - if (save) { - theme.theme = pref.theme; - theme.theme_day = pref.theme_day; - theme.theme_night = pref.theme_night; - } else { - pref.theme = custom ? theme.theme : 'reborn'; - pref.theme_day = custom ? theme.theme_day : 'white'; - pref.theme_night = custom ? theme.theme_night : 'black'; +class ThemeSettingsManager { + /** + * Creates the `ThemeSettingsManager` instance. + * @param {boolean} saveCfg - The current settings will be saved to the configuration. + * @param {boolean} loadCfg - The settings will be loaded from the configuration. + * @param {boolean} defaultCfg - The default settings will be applied. + */ + constructor(saveCfg, loadCfg, defaultCfg) { + /** @private @type {boolean} */ + this.saveCfg = saveCfg; + /** @private @type {boolean} */ + this.loadCfg = loadCfg; + /** @private @type {boolean} */ + this.defaultCfg = defaultCfg; } - // * Style - if (save) { - style.default = pref.styleDefault; - style.nighttime = pref.styleNighttime; - style.bevel = pref.styleBevel; - style.blend = pref.styleBlend; - style.blend2 = pref.styleBlend2; - style.gradient = pref.styleGradient; - style.gradient2 = pref.styleGradient2; - style.alternative = pref.styleAlternative; - style.alternative2 = pref.styleAlternative2; - style.blackAndWhite = pref.styleBlackAndWhite; - style.blackAndWhite2 = pref.styleBlackAndWhite2; - style.blackAndWhiteReborn = pref.styleBlackAndWhiteReborn; - style.blackReborn = pref.styleBlackReborn; - style.rebornWhite = pref.styleRebornWhite; - style.rebornBlack = pref.styleRebornBlack; - style.rebornFusion = pref.styleRebornFusion; - style.rebornFusion2 = pref.styleRebornFusion2; - style.rebornFusionAccent = pref.styleRebornFusionAccent; - style.randomPastel = pref.styleRandomPastel; - style.randomDark = pref.styleRandomDark; - style.randomAutoColor = pref.styleRandomAutoColor; - style.topMenuButtons = pref.styleTopMenuButtons; - style.transportButtons = pref.styleTransportButtons; - style.progressBarDesign = pref.styleProgressBarDesign; - style.progressBar = pref.styleProgressBar; - style.progressBarFill = pref.styleProgressBarFill; - style.volumeBarDesign = pref.styleVolumeBarDesign; - style.volumeBar = pref.styleVolumeBar; - style.volumeBarFill = pref.styleVolumeBarFill; - style.nighttime_day = pref.styleNighttime_day; - style.bevel_day = pref.styleBevel_day; - style.blend_day = pref.styleBlend_day; - style.blend2_day = pref.styleBlend2_day; - style.gradient_day = pref.styleGradient_day; - style.gradient2_day = pref.styleGradient2_day; - style.alternative_day = pref.styleAlternative_day; - style.alternative2_day = pref.styleAlternative2_day; - style.blackAndWhite_day = pref.styleBlackAndWhite_day; - style.blackAndWhite2_day = pref.styleBlackAndWhite2_day; - style.blackAndWhiteReborn_day = pref.styleBlackAndWhiteReborn_day; - style.blackReborn_day = pref.styleBlackReborn_day; - style.rebornWhite_day = pref.styleRebornWhite_day; - style.rebornBlack_day = pref.styleRebornBlack_day; - style.rebornFusion_day = pref.styleRebornFusion_day; - style.rebornFusion2_day = pref.styleRebornFusion2_day; - style.rebornFusionAccent_day = pref.styleRebornFusionAccent_day; - style.randomPastel_day = pref.styleRandomPastel_day; - style.randomDark_day = pref.styleRandomDark_day; - style.randomAutoColor_day = pref.styleRandomAutoColor_day; - style.topMenuButtons_day = pref.styleTopMenuButtons_day; - style.transportButtons_day = pref.styleTransportButtons_day; - style.progressBarDesign_day = pref.styleProgressBarDesign_day; - style.progressBar_day = pref.styleProgressBar_day; - style.progressBarFill_day = pref.styleProgressBarFill_day; - style.volumeBarDesign_day = pref.styleVolumeBarDesign_day; - style.volumeBar_day = pref.styleVolumeBar_day; - style.volumeBarFill_day = pref.styleVolumeBarFill_day; - style.nighttime_night = pref.styleNighttime_night; - style.bevel_night = pref.styleBevel_night; - style.blend_night = pref.styleBlend_night; - style.blend2_night = pref.styleBlend2_night; - style.gradient_night = pref.styleGradient_night; - style.gradient2_night = pref.styleGradient2_night; - style.alternative_night = pref.styleAlternative_night; - style.alternative2_night = pref.styleAlternative2_night; - style.blackAndWhite_night = pref.styleBlackAndWhite_night; - style.blackAndWhite2_night = pref.styleBlackAndWhite2_night; - style.blackAndWhiteReborn_night = pref.styleBlackAndWhiteReborn_night; - style.blackReborn_night = pref.styleBlackReborn_night; - style.rebornWhite_night = pref.styleRebornWhite_night; - style.rebornBlack_night = pref.styleRebornBlack_night; - style.rebornFusion_night = pref.styleRebornFusion_night; - style.rebornFusion2_night = pref.styleRebornFusion2_night; - style.rebornFusionAccent_night = pref.styleRebornFusionAccent_night; - style.randomPastel_night = pref.styleRandomPastel_night; - style.randomDark_night = pref.styleRandomDark_night; - style.randomAutoColor_night = pref.styleRandomAutoColor_night; - style.topMenuButtons_night = pref.styleTopMenuButtons_night; - style.transportButtons_night = pref.styleTransportButtons_night; - style.progressBarDesign_night = pref.styleProgressBarDesign_night; - style.progressBar_night = pref.styleProgressBar_night; - style.progressBarFill_night = pref.styleProgressBarFill_night; - style.volumeBarDesign_night = pref.styleVolumeBarDesign_night; - style.volumeBar_night = pref.styleVolumeBar_night; - style.volumeBarFill_night = pref.styleVolumeBarFill_night; - } else { - pref.styleDefault = custom ? style.default : true; - pref.styleNighttime = custom ? style.nighttime : false; - pref.styleBevel = custom ? style.bevel : false; - pref.styleBlend = custom ? style.blend : false; - pref.styleBlend2 = custom ? style.blend2 : false; - pref.styleGradient = custom ? style.gradient : false; - pref.styleGradient2 = custom ? style.gradient2 : false; - pref.styleAlternative = custom ? style.alternative : false; - pref.styleAlternative2 = custom ? style.alternative2 : false; - pref.styleBlackAndWhite = custom ? style.blackAndWhite : false; - pref.styleBlackAndWhite2 = custom ? style.blackAndWhite2 : false; - pref.styleBlackAndWhiteReborn = custom ? style.blackAndWhiteReborn : false; - pref.styleBlackReborn = custom ? style.blackReborn : false; - pref.styleRebornWhite = custom ? style.rebornWhite : false; - pref.styleRebornBlack = custom ? style.rebornBlack : false; - pref.styleRebornFusion = custom ? style.rebornFusion : false; - pref.styleRebornFusion2 = custom ? style.rebornFusion2 : false; - pref.styleRebornFusionAccent = custom ? style.rebornFusionAccent : false; - pref.styleRandomPastel = custom ? style.randomPastel : false; - pref.styleRandomDark = custom ? style.randomDark : false; - pref.styleRandomAutoColor = custom ? style.randomAutoColor : 'off'; - pref.styleTopMenuButtons = custom ? style.topMenuButtons : 'default'; - pref.styleTransportButtons = custom ? style.transportButtons : 'default'; - pref.styleProgressBarDesign = custom ? style.progressBarDesign : 'default'; - pref.styleProgressBar = custom ? style.progressBar : 'default'; - pref.styleProgressBarFill = custom ? style.progressBarFill : 'default'; - pref.styleVolumeBarDesign = custom ? style.volumeBarDesign : 'default'; - pref.styleVolumeBar = custom ? style.volumeBar : 'default'; - pref.styleVolumeBarFill = custom ? style.volumeBarFill : 'default'; - pref.styleNighttime_day = custom ? style.nighttime_day : false; - pref.styleBevel_day = custom ? style.bevel_day : false; - pref.styleBlend_day = custom ? style.blend_day : false; - pref.styleBlend2_day = custom ? style.blend2_day : false; - pref.styleGradient_day = custom ? style.gradient_day : false; - pref.styleGradient2_day = custom ? style.gradient2_day : false; - pref.styleAlternative_day = custom ? style.alternative_day : false; - pref.styleAlternative2_day = custom ? style.alternative2_day : false; - pref.styleBlackAndWhite_day = custom ? style.blackAndWhite_day : false; - pref.styleBlackAndWhite2_day = custom ? style.blackAndWhite2_day : false; - pref.styleBlackAndWhiteReborn_day = custom ? style.blackAndWhiteReborn_day : false; - pref.styleBlackReborn_day = custom ? style.blackReborn_day : false; - pref.styleRebornWhite_day = custom ? style.rebornWhite_day : false; - pref.styleRebornBlack_day = custom ? style.rebornBlack_day : false; - pref.styleRebornFusion_day = custom ? style.rebornFusion_day : false; - pref.styleRebornFusion2_day = custom ? style.rebornFusion2_day : false; - pref.styleRebornFusionAccent_day = custom ? style.rebornFusionAccent_day : false; - pref.styleRandomPastel_day = custom ? style.randomPastel_day : false; - pref.styleRandomDark_day = custom ? style.randomDark_day : false; - pref.styleRandomAutoColor_day = custom ? style.randomAutoColor_day : 'off'; - pref.styleTopMenuButtons_day = custom ? style.topMenuButtons_day : 'default'; - pref.styleTransportButtons_day = custom ? style.transportButtons_day : 'default'; - pref.styleProgressBarDesign_day = custom ? style.progressBarDesign_day : 'default'; - pref.styleProgressBar_day = custom ? style.progressBar_day : 'default'; - pref.styleProgressBarFill_day = custom ? style.progressBarFill_day : 'default'; - pref.styleVolumeBarDesign_day = custom ? style.volumeBarDesign_day : 'default'; - pref.styleVolumeBar_day = custom ? style.volumeBar_day : 'default'; - pref.styleVolumeBarFill_day = custom ? style.volumeBarFill_day : 'default'; - pref.styleNighttime_night = custom ? style.nighttime_night : false; - pref.styleBevel_night = custom ? style.bevel_night : false; - pref.styleBlend_night = custom ? style.blend_night : false; - pref.styleBlend2_night = custom ? style.blend2_night : false; - pref.styleGradient_night = custom ? style.gradient_night : false; - pref.styleGradient2_night = custom ? style.gradient2_night : false; - pref.styleAlternative_night = custom ? style.alternative_night : false; - pref.styleAlternative2_night = custom ? style.alternative2_night : false; - pref.styleBlackAndWhite_night = custom ? style.blackAndWhite_night : false; - pref.styleBlackAndWhite2_night = custom ? style.blackAndWhite2_night : false; - pref.styleBlackAndWhiteReborn_night = custom ? style.blackAndWhiteReborn_night : false; - pref.styleBlackReborn_night = custom ? style.blackReborn_night : false; - pref.styleRebornWhite_night = custom ? style.rebornWhite_night : false; - pref.styleRebornBlack_night = custom ? style.rebornBlack_night : false; - pref.styleRebornFusion_night = custom ? style.rebornFusion_night : false; - pref.styleRebornFusion2_night = custom ? style.rebornFusion2_night : false; - pref.styleRebornFusionAccent_night = custom ? style.rebornFusionAccent_night : false; - pref.styleRandomPastel_night = custom ? style.randomPastel_night : false; - pref.styleRandomDark_night = custom ? style.randomDark_night : false; - pref.styleRandomAutoColor_night = custom ? style.randomAutoColor_night : 'off'; - pref.styleTopMenuButtons_night = custom ? style.topMenuButtons_night : 'default'; - pref.styleTransportButtons_night = custom ? style.transportButtons_night : 'default'; - pref.styleProgressBarDesign_night = custom ? style.progressBarDesign_night : 'default'; - pref.styleProgressBar_night = custom ? style.progressBar_night : 'default'; - pref.styleProgressBarFill_night = custom ? style.progressBarFill_night : 'default'; - pref.styleVolumeBarDesign_night = custom ? style.volumeBarDesign_night : 'default'; - pref.styleVolumeBar_night = custom ? style.volumeBar_night : 'default'; - pref.styleVolumeBarFill_night = custom ? style.volumeBarFill_night : 'default'; + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Sets an individual setting based on the config args from the other set methods as follows: + * - `saveCfg`: configObj.keyname = prefObj.keyname; + * - `loadCfg`: prefObj.keyname = configObj.keyname; + * - `defaultCfg`: prefObj.keyname = defaultValue;. + * @param {object} prefObj - The preferences object. + * @param {string} prefKey - The pref key in the preferences object. + * @param {object} configObj - The config object. + * @param {string} configKey - The config key in the presets object. + * @param {*} defaultValue - The default value to set if defaultCfg is true. + * @private + */ + _setSetting(prefObj, prefKey, configObj, configKey, defaultValue) { + if (this.saveCfg && configKey !== false) { + configObj[configKey] = prefObj[prefKey]; + } + else if (this.loadCfg && configKey !== false) { + prefObj[prefKey] = configObj[configKey]; + } + else if (this.defaultCfg) { + prefObj[prefKey] = defaultValue; + } } + // #endregion - // * Preset - if (save) { - preset.selectMode = pref.presetSelectMode; - preset.selectWhitePresets = pref.presetSelectWhite; - preset.selectBlackPresets = pref.presetSelectBlack; - preset.selectRebornPresets = pref.presetSelectReborn; - preset.selectRandomPresets = pref.presetSelectRandom; - preset.selectBluePresets = pref.presetSelectBlue; - preset.selectDarkbluePresets = pref.presetSelectDarkblue; - preset.selectRedPresets = pref.presetSelectRed; - preset.selectCreamPresets = pref.presetSelectCream; - preset.selectNbluePresets = pref.presetSelectNblue; - preset.selectNgreenPresets = pref.presetSelectNgreen; - preset.selectNredPresets = pref.presetSelectNred; - preset.selectNgoldPresets = pref.presetSelectNgold; - preset.selectCustomPresets = pref.presetSelectCustom; - preset.autoRandomMode = pref.presetAutoRandomMode; - preset.indicator = pref.presetIndicator; - } else { - pref.presetSelectMode = custom ? preset.selectMode : 'default'; - pref.presetSelectWhite = custom ? preset.selectWhitePresets : true; - pref.presetSelectBlack = custom ? preset.selectBlackPresets : true; - pref.presetSelectReborn = custom ? preset.selectRebornPresets : true; - pref.presetSelectRandom = custom ? preset.selectRandomPresets : true; - pref.presetSelectBlue = custom ? preset.selectBluePresets : true; - pref.presetSelectDarkblue = custom ? preset.selectDarkbluePresets : true; - pref.presetSelectRed = custom ? preset.selectRedPresets : true; - pref.presetSelectCream = custom ? preset.selectCreamPresets : true; - pref.presetSelectNblue = custom ? preset.selectNbluePresets : true; - pref.presetSelectNgreen = custom ? preset.selectNgreenPresets : true; - pref.presetSelectNred = custom ? preset.selectNredPresets : true; - pref.presetSelectNgold = custom ? preset.selectNgoldPresets : true; - pref.presetSelectCustom = custom ? preset.selectCustomPresets : true; - pref.presetAutoRandomMode = custom ? preset.autoRandomMode : 'dblclick'; - pref.presetIndicator = custom ? preset.indicator : true; + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Loads default theme settings when pref.customThemeSettings is false, otherwise it loads settings from the config file. + * When using with the parameter, it saves all settings to the config file. + * @param {boolean} saveCfg - Saves current theme settings from the `pref` panel properties object to config file. + * @param {boolean} loadCfg - Loads theme settings from the config file to the `pref` panel properties object. + * @param {boolean} defaultCfg - Loads theme settings based on default setting values. + */ + async setThemeSettings(saveCfg = false, loadCfg = false, defaultCfg = false) { + grm.ui.libraryCanReload = false; + + this.saveCfg = saveCfg; + this.loadCfg = loadCfg; + this.defaultCfg = defaultCfg; + + await this.setTheme(); + await this.setStyle(); + await this.setPreset(); + await this.setPlayerSize(); + await this.setLayout(); + await this.setDisplay(); + await this.setBrightness(); + await this.setFontSize(); + await this.setPlayerControls(); + await this.setPlaylist(); + await this.setDetails(); + await this.setLibrary(); + await this.setBiography(); + await this.setLyrics(); + await this.setSettings(); + await this.setSettingsNotInConfig(); + + // * Set variable imgBlended when switching from default settings to config settings that has style Blend or Blend2 activated + await this.setStyleBlend(); + + // * Reinitialize theme presets when user has reset style settings by clicking on "Default" and reloading the config file + await grm.preset.initThemePresetState(); + + // * Reinitialize the saved player size + await grm.display.initPlayerSize(); + + grm.ui.libraryCanReload = true; } - // * Player size - if (save) { - themePlayerSize.playerSize = pref.playerSize; - themePlayerSize.savedWidth_default = pref.savedWidth_default; - themePlayerSize.savedHeight_default = pref.savedHeight_default; - themePlayerSize.savedWidth_artwork = pref.savedWidth_artwork; - themePlayerSize.savedHeight_artwork = pref.savedHeight_artwork; - themePlayerSize.savedWidth_compact = pref.savedWidth_compact; - themePlayerSize.savedHeight_compact = pref.savedHeight_compact; - } else { - pref.playerSize = custom ? themePlayerSize.playerSize : 'small'; - pref.savedWidth_default = custom ? themePlayerSize.savedWidth_default : 1140; - pref.savedHeight_default = custom ? themePlayerSize.savedHeight_default : 730; - pref.savedWidth_artwork = custom ? themePlayerSize.savedWidth_artwork : 526; - pref.savedHeight_artwork = custom ? themePlayerSize.savedHeight_artwork : 686; - pref.savedWidth_compact = custom ? themePlayerSize.savedWidth_compact : 484; - pref.savedHeight_compact = custom ? themePlayerSize.savedHeight_compact : 730; - pref.playerSize_4K_small = false; // ! System setting, not configurable for users - pref.playerSize_4K_normal = false; // ! System setting, not configurable for users - pref.playerSize_4K_large = false; // ! System setting, not configurable for users - pref.playerSize_QHD_small = false; // ! System setting, not configurable for users - pref.playerSize_QHD_normal = false; // ! System setting, not configurable for users - pref.playerSize_QHD_large = false; // ! System setting, not configurable for users - pref.playerSize_HD_small = false; // ! System setting, not configurable for users - pref.playerSize_HD_normal = false; // ! System setting, not configurable for users - pref.playerSize_HD_large = false; // ! System setting, not configurable for users + /** + * Sets theme settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setTheme() { + this._setSetting(grSet, 'theme', grCfg.theme, 'theme', 'reborn'); + this._setSetting(grSet, 'theme_day', grCfg.theme, 'theme_day', 'white'); + this._setSetting(grSet, 'theme_night', grCfg.theme, 'theme_night', 'black'); } - // * Layout - if (save) { - themeLayout.layout = pref.layout; - } else { - pref.layout = custom ? themeLayout.layout : 'default'; + /** + * Sets style settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setStyle() { + this._setSetting(grSet, 'styleDefault', grCfg.style, 'default', true); + this._setSetting(grSet, 'styleNighttime', grCfg.style, 'nighttime', false); + this._setSetting(grSet, 'styleBevel', grCfg.style, 'bevel', false); + this._setSetting(grSet, 'styleBlend', grCfg.style, 'blend', false); + this._setSetting(grSet, 'styleBlend2', grCfg.style, 'blend2', false); + this._setSetting(grSet, 'styleGradient', grCfg.style, 'gradient', false); + this._setSetting(grSet, 'styleGradient2', grCfg.style, 'gradient2', false); + this._setSetting(grSet, 'styleAlternative', grCfg.style, 'alternative', false); + this._setSetting(grSet, 'styleAlternative2', grCfg.style, 'alternative2', false); + this._setSetting(grSet, 'styleBlackAndWhite', grCfg.style, 'blackAndWhite', false); + this._setSetting(grSet, 'styleBlackAndWhite2', grCfg.style, 'blackAndWhite2', false); + this._setSetting(grSet, 'styleBlackAndWhiteReborn', grCfg.style, 'blackAndWhiteReborn', false); + this._setSetting(grSet, 'styleBlackReborn', grCfg.style, 'blackReborn', false); + this._setSetting(grSet, 'styleRebornWhite', grCfg.style, 'rebornWhite', false); + this._setSetting(grSet, 'styleRebornBlack', grCfg.style, 'rebornBlack', false); + this._setSetting(grSet, 'styleRebornFusion', grCfg.style, 'rebornFusion', false); + this._setSetting(grSet, 'styleRebornFusion2', grCfg.style, 'rebornFusion2', false); + this._setSetting(grSet, 'styleRebornFusionAccent', grCfg.style, 'rebornFusionAccent', false); + this._setSetting(grSet, 'styleRandomPastel', grCfg.style, 'randomPastel', false); + this._setSetting(grSet, 'styleRandomDark', grCfg.style, 'randomDark', false); + this._setSetting(grSet, 'styleRandomAutoColor', grCfg.style, 'randomAutoColor', 'off'); + this._setSetting(grSet, 'styleTopMenuButtons', grCfg.style, 'topMenuButtons', 'default'); + this._setSetting(grSet, 'styleTransportButtons', grCfg.style, 'transportButtons', 'default'); + this._setSetting(grSet, 'styleProgressBarDesign', grCfg.style, 'progressBarDesign', 'default'); + this._setSetting(grSet, 'styleProgressBar', grCfg.style, 'progressBar', 'default'); + this._setSetting(grSet, 'styleProgressBarFill', grCfg.style, 'progressBarFill', 'default'); + this._setSetting(grSet, 'styleVolumeBarDesign', grCfg.style, 'volumeBarDesign', 'default'); + this._setSetting(grSet, 'styleVolumeBar', grCfg.style, 'volumeBar', 'default'); + this._setSetting(grSet, 'styleVolumeBarFill', grCfg.style, 'volumeBarFill', 'default'); + this._setSetting(grSet, 'styleNighttime_day', grCfg.style, 'nighttime_day', false); + this._setSetting(grSet, 'styleBevel_day', grCfg.style, 'bevel_day', false); + this._setSetting(grSet, 'styleBlend_day', grCfg.style, 'blend_day', false); + this._setSetting(grSet, 'styleBlend2_day', grCfg.style, 'blend2_day', false); + this._setSetting(grSet, 'styleGradient_day', grCfg.style, 'gradient_day', false); + this._setSetting(grSet, 'styleGradient2_day', grCfg.style, 'gradient2_day', false); + this._setSetting(grSet, 'styleAlternative_day', grCfg.style, 'alternative_day', false); + this._setSetting(grSet, 'styleAlternative2_day', grCfg.style, 'alternative2_day', false); + this._setSetting(grSet, 'styleBlackAndWhite_day', grCfg.style, 'blackAndWhite_day', false); + this._setSetting(grSet, 'styleBlackAndWhite2_day', grCfg.style, 'blackAndWhite2_day', false); + this._setSetting(grSet, 'styleBlackAndWhiteReborn_day', grCfg.style, 'blackAndWhiteReborn_day', false); + this._setSetting(grSet, 'styleBlackReborn_day', grCfg.style, 'blackReborn_day', false); + this._setSetting(grSet, 'styleRebornWhite_day', grCfg.style, 'rebornWhite_day', false); + this._setSetting(grSet, 'styleRebornBlack_day', grCfg.style, 'rebornBlack_day', false); + this._setSetting(grSet, 'styleRebornFusion_day', grCfg.style, 'rebornFusion_day', false); + this._setSetting(grSet, 'styleRebornFusion2_day', grCfg.style, 'rebornFusion2_day', false); + this._setSetting(grSet, 'styleRebornFusionAccent_day', grCfg.style, 'rebornFusionAccent_day', false); + this._setSetting(grSet, 'styleRandomPastel_day', grCfg.style, 'randomPastel_day', false); + this._setSetting(grSet, 'styleRandomDark_day', grCfg.style, 'randomDark_day', false); + this._setSetting(grSet, 'styleRandomAutoColor_day', grCfg.style, 'randomAutoColor_day', 'off'); + this._setSetting(grSet, 'styleTopMenuButtons_day', grCfg.style, 'topMenuButtons_day', 'default'); + this._setSetting(grSet, 'styleTransportButtons_day', grCfg.style, 'transportButtons_day', 'default'); + this._setSetting(grSet, 'styleProgressBarDesign_day', grCfg.style, 'progressBarDesign_day', 'default'); + this._setSetting(grSet, 'styleProgressBar_day', grCfg.style, 'progressBar_day', 'default'); + this._setSetting(grSet, 'styleProgressBarFill_day', grCfg.style, 'progressBarFill_day', 'default'); + this._setSetting(grSet, 'styleVolumeBarDesign_day', grCfg.style, 'volumeBarDesign_day', 'default'); + this._setSetting(grSet, 'styleVolumeBar_day', grCfg.style, 'volumeBar_day', 'default'); + this._setSetting(grSet, 'styleVolumeBarFill_day', grCfg.style, 'volumeBarFill_day', 'default'); + this._setSetting(grSet, 'styleNighttime_night', grCfg.style, 'nighttime_night', false); + this._setSetting(grSet, 'styleBevel_night', grCfg.style, 'bevel_night', false); + this._setSetting(grSet, 'styleBlend_night', grCfg.style, 'blend_night', false); + this._setSetting(grSet, 'styleBlend2_night', grCfg.style, 'blend2_night', false); + this._setSetting(grSet, 'styleGradient_night', grCfg.style, 'gradient_night', false); + this._setSetting(grSet, 'styleGradient2_night', grCfg.style, 'gradient2_night', false); + this._setSetting(grSet, 'styleAlternative_night', grCfg.style, 'alternative_night', false); + this._setSetting(grSet, 'styleAlternative2_night', grCfg.style, 'alternative2_night', false); + this._setSetting(grSet, 'styleBlackAndWhite_night', grCfg.style, 'blackAndWhite_night', false); + this._setSetting(grSet, 'styleBlackAndWhite2_night', grCfg.style, 'blackAndWhite2_night', false); + this._setSetting(grSet, 'styleBlackAndWhiteReborn_night', grCfg.style, 'blackAndWhiteReborn_night', false); + this._setSetting(grSet, 'styleBlackReborn_night', grCfg.style, 'blackReborn_night', false); + this._setSetting(grSet, 'styleRebornWhite_night', grCfg.style, 'rebornWhite_night', false); + this._setSetting(grSet, 'styleRebornBlack_night', grCfg.style, 'rebornBlack_night', false); + this._setSetting(grSet, 'styleRebornFusion_night', grCfg.style, 'rebornFusion_night', false); + this._setSetting(grSet, 'styleRebornFusion2_night', grCfg.style, 'rebornFusion2_night', false); + this._setSetting(grSet, 'styleRebornFusionAccent_night', grCfg.style, 'rebornFusionAccent_night', false); + this._setSetting(grSet, 'styleRandomPastel_night', grCfg.style, 'randomPastel_night', false); + this._setSetting(grSet, 'styleRandomDark_night', grCfg.style, 'randomDark_night', false); + this._setSetting(grSet, 'styleRandomAutoColor_night', grCfg.style, 'randomAutoColor_night', 'off'); + this._setSetting(grSet, 'styleTopMenuButtons_night', grCfg.style, 'topMenuButtons_night', 'default'); + this._setSetting(grSet, 'styleTransportButtons_night', grCfg.style, 'transportButtons_night', 'default'); + this._setSetting(grSet, 'styleProgressBarDesign_night', grCfg.style, 'progressBarDesign_night', 'default'); + this._setSetting(grSet, 'styleProgressBar_night', grCfg.style, 'progressBar_night', 'default'); + this._setSetting(grSet, 'styleProgressBarFill_night', grCfg.style, 'progressBarFill_night', 'default'); + this._setSetting(grSet, 'styleVolumeBarDesign_night', grCfg.style, 'volumeBarDesign_night', 'default'); + this._setSetting(grSet, 'styleVolumeBar_night', grCfg.style, 'volumeBar_night', 'default'); + this._setSetting(grSet, 'styleVolumeBarFill_night', grCfg.style, 'volumeBarFill_night', 'default'); } - // * Display - if (save) { - themeDisplay.resolution = pref.displayRes; - } else { - pref.displayRes = custom ? themeDisplay.resolution : 'HD'; + /** + * Sets preset settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setPreset() { + this._setSetting(grSet, 'presetSelectMode', grCfg.preset, 'selectMode', 'default'); + this._setSetting(grSet, 'presetSelectWhite', grCfg.preset, 'selectWhitePresets', true); + this._setSetting(grSet, 'presetSelectBlack', grCfg.preset, 'selectBlackPresets', true); + this._setSetting(grSet, 'presetSelectReborn', grCfg.preset, 'selectRebornPresets', true); + this._setSetting(grSet, 'presetSelectRandom', grCfg.preset, 'selectRandomPresets', true); + this._setSetting(grSet, 'presetSelectBlue', grCfg.preset, 'selectBluePresets', true); + this._setSetting(grSet, 'presetSelectDarkblue', grCfg.preset, 'selectDarkbluePresets', true); + this._setSetting(grSet, 'presetSelectRed', grCfg.preset, 'selectRedPresets', true); + this._setSetting(grSet, 'presetSelectCream', grCfg.preset, 'selectCreamPresets', true); + this._setSetting(grSet, 'presetSelectNblue', grCfg.preset, 'selectNbluePresets', true); + this._setSetting(grSet, 'presetSelectNgreen', grCfg.preset, 'selectNgreenPresets', true); + this._setSetting(grSet, 'presetSelectNred', grCfg.preset, 'selectNredPresets', true); + this._setSetting(grSet, 'presetSelectNgold', grCfg.preset, 'selectNgoldPresets', true); + this._setSetting(grSet, 'presetSelectCustom', grCfg.preset, 'selectCustomPresets', true); + this._setSetting(grSet, 'presetAutoRandomMode', grCfg.preset, 'autoRandomMode', 'dblclick'); + this._setSetting(grSet, 'presetIndicator', grCfg.preset, 'indicator', true); } - // * Brightness - if (save) { - themeBrightness.themeBrightness = pref.themeBrightness; - themeBrightness.themeBrightness_day = pref.themeBrightness_day; - themeBrightness.themeBrightness_night = pref.themeBrightness_night; - } else { - pref.themeBrightness = custom ? themeBrightness.themeBrightness : 'default'; - pref.themeBrightness_day = custom ? themeBrightness.themeBrightness_day : 'default'; - pref.themeBrightness_night = custom ? themeBrightness.themeBrightness_night : 'default'; + /** + * Sets player size settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setPlayerSize() { + this._setSetting(grSet, 'playerSize', grCfg.themePlayerSize, 'playerSize', 'small'); + this._setSetting(grSet, 'savedWidth_default', grCfg.themePlayerSize, 'savedWidth_default', 1140); + this._setSetting(grSet, 'savedHeight_default', grCfg.themePlayerSize, 'savedHeight_default', 730); + this._setSetting(grSet, 'savedWidth_artwork', grCfg.themePlayerSize, 'savedWidth_artwork', 526); + this._setSetting(grSet, 'savedHeight_artwork', grCfg.themePlayerSize, 'savedHeight_artwork', 686); + this._setSetting(grSet, 'savedWidth_compact', grCfg.themePlayerSize, 'savedWidth_compact', 484); + this._setSetting(grSet, 'savedHeight_compact', grCfg.themePlayerSize, 'savedHeight_compact', 730); + + // ! System settings not configurable + grSet.playerSize_4K_small = false; + grSet.playerSize_4K_normal = false; + grSet.playerSize_4K_large = false; + grSet.playerSize_QHD_small = false; + grSet.playerSize_QHD_normal = false; + grSet.playerSize_QHD_large = false; + grSet.playerSize_HD_small = false; + grSet.playerSize_HD_normal = false; + grSet.playerSize_HD_large = false; } - // * Font size - if (save) { - themeFontSize.menuFontSize_default = pref.menuFontSize_default; - themeFontSize.menuFontSize_artwork = pref.menuFontSize_artwork; - themeFontSize.menuFontSize_compact = pref.menuFontSize_compact; - themeFontSize.lowerBarFontSize_default = pref.lowerBarFontSize_default; - themeFontSize.lowerBarFontSize_artwork = pref.lowerBarFontSize_artwork; - themeFontSize.lowerBarFontSize_compact = pref.lowerBarFontSize_compact; - themeFontSize.notificationFontSize_default = pref.notificationFontSize_default; - themeFontSize.notificationFontSize_artwork = pref.notificationFontSize_artwork; - themeFontSize.notificationFontSize_compact = pref.notificationFontSize_compact; - themeFontSize.popupFontSize_default = pref.popupFontSize_default; - themeFontSize.popupFontSize_artwork = pref.popupFontSize_artwork; - themeFontSize.popupFontSize_compact = pref.popupFontSize_compact; - themeFontSize.tooltipFontSize_default = pref.tooltipFontSize_default; - themeFontSize.tooltipFontSize_artwork = pref.tooltipFontSize_artwork; - themeFontSize.tooltipFontSize_compact = pref.tooltipFontSize_compact; - themeFontSize.gridArtistFontSize_default = pref.gridArtistFontSize_default; - themeFontSize.gridArtistFontSize_artwork = pref.gridArtistFontSize_artwork; - themeFontSize.gridTrackNumFontSize_default = pref.gridTrackNumFontSize_default; - themeFontSize.gridTrackNumFontSize_artwork = pref.gridTrackNumFontSize_artwork; - themeFontSize.gridTitleFontSize_default = pref.gridTitleFontSize_default; - themeFontSize.gridTitleFontSize_artwork = pref.gridTitleFontSize_artwork; - themeFontSize.gridAlbumFontSize_default = pref.gridAlbumFontSize_default; - themeFontSize.gridAlbumFontSize_artwork = pref.gridAlbumFontSize_artwork; - themeFontSize.gridKeyFontSize_default = pref.gridKeyFontSize_default; - themeFontSize.gridKeyFontSize_artwork = pref.gridKeyFontSize_artwork; - themeFontSize.gridValueFontSize_default = pref.gridValueFontSize_default; - themeFontSize.gridValueFontSize_artwork = pref.gridValueFontSize_artwork; - themeFontSize.playlistHeaderFontSize_default = pref.playlistHeaderFontSize_default; - themeFontSize.playlistHeaderFontSize_artwork = pref.playlistHeaderFontSize_artwork; - themeFontSize.playlistHeaderFontSize_compact = pref.playlistHeaderFontSize_compact; - themeFontSize.playlistFontSize_default = pref.playlistFontSize_default; - themeFontSize.playlistFontSize_artwork = pref.playlistFontSize_artwork; - themeFontSize.playlistFontSize_compact = pref.playlistFontSize_compact; - themeFontSize.libraryFontSize_default = ppt.baseFontSize_default; - themeFontSize.libraryFontSize_artwork = ppt.baseFontSize_artwork; - themeFontSize.biographyFontSize_default = pptBio.baseFontSizeBio_default; - themeFontSize.biographyFontSize_artwork = pptBio.baseFontSizeBio_artwork; - themeFontSize.lyricsFontSize_default = pref.lyricsFontSize_default; - themeFontSize.lyricsFontSize_artwork = pref.lyricsFontSize_artwork; - } else { - pref.menuFontSize_default = custom ? themeFontSize.menuFontSize_default : RES_QHD ? 14 : 12; - pref.menuFontSize_artwork = custom ? themeFontSize.menuFontSize_artwork : RES_QHD ? 14 : 12; - pref.menuFontSize_compact = custom ? themeFontSize.menuFontSize_compact : RES_QHD ? 14 : 12; - pref.lowerBarFontSize_default = custom ? themeFontSize.lowerBarFontSize_default : RES_QHD ? 20 : 18; - pref.lowerBarFontSize_artwork = custom ? themeFontSize.lowerBarFontSize_artwork : RES_QHD ? 18 : 16; - pref.lowerBarFontSize_compact = custom ? themeFontSize.lowerBarFontSize_compact : RES_QHD ? 18 : 16; - pref.notificationFontSize_default = custom ? themeFontSize.notificationFontSize_default : RES_QHD ? 20 : 18; - pref.notificationFontSize_artwork = custom ? themeFontSize.notificationFontSize_artwork : RES_QHD ? 18 : 16; - pref.notificationFontSize_compact = custom ? themeFontSize.notificationFontSize_compact : RES_QHD ? 18 : 16; - pref.popupFontSize_default = custom ? themeFontSize.popupFontSize_default : RES_QHD ? 18 : 16; - pref.popupFontSize_artwork = custom ? themeFontSize.popupFontSize_artwork : RES_QHD ? 16 : 14; - pref.popupFontSize_compact = custom ? themeFontSize.popupFontSize_compact : RES_QHD ? 16 : 14; - pref.tooltipFontSize_default = custom ? themeFontSize.tooltipFontSize_default : RES_QHD ? 18 : 16; - pref.tooltipFontSize_artwork = custom ? themeFontSize.tooltipFontSize_artwork : RES_QHD ? 16 : 14; - pref.tooltipFontSize_compact = custom ? themeFontSize.tooltipFontSize_compact : RES_QHD ? 16 : 14; - pref.gridArtistFontSize_default = custom ? themeFontSize.gridArtistFontSize_default : RES_QHD ? 20 : 18; - pref.gridArtistFontSize_artwork = custom ? themeFontSize.gridArtistFontSize_artwork : RES_QHD ? 20 : 18; - pref.gridTrackNumFontSize_default = custom ? themeFontSize.gridTrackNumFontSize_default : RES_QHD ? 20 : 18; - pref.gridTrackNumFontSize_artwork = custom ? themeFontSize.gridTrackNumFontSize_artwork : RES_QHD ? 20 : 18; - pref.gridTitleFontSize_default = custom ? themeFontSize.gridTitleFontSize_default : RES_QHD ? 20 : 18; - pref.gridTitleFontSize_artwork = custom ? themeFontSize.gridTitleFontSize_artwork : RES_QHD ? 20 : 18; - pref.gridAlbumFontSize_default = custom ? themeFontSize.gridAlbumFontSize_default : RES_QHD ? 20 : 18; - pref.gridAlbumFontSize_artwork = custom ? themeFontSize.gridAlbumFontSize_artwork : RES_QHD ? 20 : 18; - pref.gridKeyFontSize_default = custom ? themeFontSize.gridKeyFontSize_default : RES_QHD ? 19 : 17; - pref.gridKeyFontSize_artwork = custom ? themeFontSize.gridKeyFontSize_artwork : RES_QHD ? 19 : 17; - pref.gridValueFontSize_default = custom ? themeFontSize.gridValueFontSize_default : RES_QHD ? 19 : 17; - pref.gridValueFontSize_artwork = custom ? themeFontSize.gridValueFontSize_artwork : RES_QHD ? 19 : 17; - pref.playlistHeaderFontSize_default = custom ? themeFontSize.playlistHeaderFontSize_default : RES_QHD ? 17 : 15; - pref.playlistHeaderFontSize_artwork = custom ? themeFontSize.playlistHeaderFontSize_artwork : RES_QHD ? 17 : 15; - pref.playlistHeaderFontSize_compact = custom ? themeFontSize.playlistHeaderFontSize_compact : RES_QHD ? 17 : 15; - pref.playlistFontSize_default = custom ? themeFontSize.playlistFontSize_default : RES_QHD ? 14 : 12; - pref.playlistFontSize_artwork = custom ? themeFontSize.playlistFontSize_artwork : RES_QHD ? 14 : 12; - pref.playlistFontSize_compact = custom ? themeFontSize.playlistFontSize_compact : RES_QHD ? 14 : 12; - ppt.baseFontSize_default = custom ? themeFontSize.libraryFontSize_default : RES_4K ? 24 : RES_QHD ? 14 : 12; - ppt.baseFontSize_artwork = custom ? themeFontSize.libraryFontSize_artwork : RES_4K ? 24 : RES_QHD ? 14 : 12; - pptBio.baseFontSizeBio_default = custom ? themeFontSize.biographyFontSize_default : RES_4K ? 24 : RES_QHD ? 14 : 12; - pptBio.baseFontSizeBio_artwork = custom ? themeFontSize.biographyFontSize_artwork : RES_4K ? 24 : RES_QHD ? 14 : 12; - pref.lyricsFontSize_default = custom ? themeFontSize.lyricsFontSize_default : RES_QHD ? 22 : 20; - pref.lyricsFontSize_artwork = custom ? themeFontSize.lyricsFontSize_artwork : RES_QHD ? 22 : 20; + /** + * Sets layout settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setLayout() { + this._setSetting(grSet, 'layout', grCfg.themeLayout, 'layout', 'default'); } - // * Player controls - if (save) { - themeControls.showPanelDetails_default = pref.showPanelDetails_default; - themeControls.showPanelDetails_artwork = pref.showPanelDetails_artwork; - themeControls.showPanelLibrary_default = pref.showPanelLibrary_default; - themeControls.showPanelLibrary_artwork = pref.showPanelLibrary_artwork; - themeControls.showPanelBiography_default = pref.showPanelBiography_default; - themeControls.showPanelBiography_artwork = pref.showPanelBiography_artwork; - themeControls.showPanelLyrics_default = pref.showPanelLyrics_default; - themeControls.showPanelLyrics_artwork = pref.showPanelLyrics_artwork; - themeControls.showPanelRating_default = pref.showPanelRating_default; - themeControls.showPanelRating_artwork = pref.showPanelRating_artwork; - themeControls.topMenuAlignment = pref.topMenuAlignment; - themeControls.topMenuCompact = pref.topMenuCompact; - themeControls.albumArtAlign = pref.albumArtAlign; - themeControls.albumArtBg = pref.albumArtBg; - themeControls.albumArtScale = pref.albumArtScale; - themeControls.albumArtAspectRatioLimit = pref.albumArtAspectRatioLimit; - themeControls.cycleArt = pref.cycleArt; - themeControls.cycleArtMWheel = pref.cycleArtMWheel; - themeControls.loadEmbeddedAlbumArtFirst = pref.loadEmbeddedAlbumArtFirst; - themeControls.showHiResAudioBadge = pref.showHiResAudioBadge; - themeControls.hiResAudioBadgeRound = pref.hiResAudioBadgeRound; - themeControls.hiResAudioBadgeSize = pref.hiResAudioBadgeSize; - themeControls.hiResAudioBadgePos = pref.hiResAudioBadgePos; - themeControls.showPause = pref.showPause; - themeControls.jumpSearchIncludeLibrary = pref.jumpSearchIncludeLibrary; - themeControls.jumpSearchIncludePlaylist = pref.jumpSearchIncludePlaylist; - themeControls.jumpSearchComposerOnly = pref.jumpSearchComposerOnly; - themeControls.playlistWheelScrollSteps = pref.playlistWheelScrollSteps; - themeControls.playlistWheelScrollDuration = pref.playlistWheelScrollDuration; - themeControls.playlistAutoScrollNowPlaying = pref.playlistAutoScrollNowPlaying; - themeControls.playlistAutoHideScrollbar = pref.playlistAutoHideScrollbar; - themeControls.playlistSmoothScrolling = pref.playlistSmoothScrolling; - themeControls.scrollStepLib = ppt.scrollStep; - themeControls.durationScrollLib = ppt.durationScroll; - themeControls.libraryAutoScrollNowPlaying = pref.libraryAutoScrollNowPlaying; - themeControls.libraryAutoHideScrollbar = pref.libraryAutoHideScrollbar; - themeControls.smoothLib = ppt.smooth; - themeControls.scrollStepBio = pptBio.scrollStep; - themeControls.durationScrollBio = pptBio.durationScroll; - themeControls.biographyAutoHideScrollbar = pref.biographyAutoHideScrollbar; - themeControls.smoothBio = pptBio.smooth; - themeControls.showTooltipTruncated = pref.showTooltipTruncated; - themeControls.showTooltipTimeline = pref.showTooltipTimeline; - themeControls.showTooltipVolume = pref.showTooltipVolume; - themeControls.showTooltipVolumeInPercent = pref.showTooltipVolumeInPercent; - themeControls.showTooltipMain = pref.showTooltipMain; - themeControls.showTooltipLibrary = pref.showTooltipLibrary; - themeControls.showTooltipBiography = pref.showTooltipBiography; - themeControls.showStyledTooltips = pref.showStyledTooltips; - themeControls.panelWidthAuto = pref.panelWidthAuto; - themeControls.showPanelOnStartup = pref.showPanelOnStartup; - themeControls.showPreloaderLogo = pref.showPreloaderLogo; - themeControls.returnToHomeOnPlaybackStop = pref.returnToHomeOnPlaybackStop; - themeControls.hideMiddlePanelShadow = pref.hideMiddlePanelShadow; - themeControls.lockPlayerSize = pref.lockPlayerSize; - themeControls.transportButtonSize_default = pref.transportButtonSize_default; - themeControls.transportButtonSize_artwork = pref.transportButtonSize_artwork; - themeControls.transportButtonSize_compact = pref.transportButtonSize_compact; - themeControls.transportButtonSpacing_default = pref.transportButtonSpacing_default; - themeControls.transportButtonSpacing_artwork = pref.transportButtonSpacing_artwork; - themeControls.transportButtonSpacing_compact = pref.transportButtonSpacing_compact; - themeControls.showTransportControls_default = pref.showTransportControls_default; - themeControls.showTransportControls_artwork = pref.showTransportControls_artwork; - themeControls.showTransportControls_compact = pref.showTransportControls_compact; - themeControls.showPlaybackOrderBtn_default = pref.showPlaybackOrderBtn_default; - themeControls.showPlaybackOrderBtn_artwork = pref.showPlaybackOrderBtn_artwork; - themeControls.showPlaybackOrderBtn_compact = pref.showPlaybackOrderBtn_compact; - themeControls.showReloadBtn_default = pref.showReloadBtn_default; - themeControls.showReloadBtn_artwork = pref.showReloadBtn_artwork; - themeControls.showReloadBtn_compact = pref.showReloadBtn_compact; - themeControls.showVolumeBtn_default = pref.showVolumeBtn_default; - themeControls.showVolumeBtn_artwork = pref.showVolumeBtn_artwork; - themeControls.showVolumeBtn_compact = pref.showVolumeBtn_compact; - themeControls.autoHideVolumeBar = pref.autoHideVolumeBar; - themeControls.showPlaybackTime_default = pref.showPlaybackTime_default; - themeControls.showPlaybackTime_artwork = pref.showPlaybackTime_artwork; - themeControls.showPlaybackTime_compact = pref.showPlaybackTime_compact; - themeControls.showLowerBarArtist_default = pref.showLowerBarArtist_default; - themeControls.showLowerBarArtist_artwork = pref.showLowerBarArtist_artwork; - themeControls.showLowerBarArtist_compact = pref.showLowerBarArtist_compact; - themeControls.showLowerBarTrackNum_default = pref.showLowerBarTrackNum_default; - themeControls.showLowerBarTrackNum_artwork = pref.showLowerBarTrackNum_artwork; - themeControls.showLowerBarTrackNum_compact = pref.showLowerBarTrackNum_compact; - themeControls.showLowerBarTitle_default = pref.showLowerBarTitle_default; - themeControls.showLowerBarTitle_artwork = pref.showLowerBarTitle_artwork; - themeControls.showLowerBarTitle_compact = pref.showLowerBarTitle_compact; - themeControls.showLowerBarComposer_default = pref.showLowerBarComposer_default; - themeControls.showLowerBarComposer_artwork = pref.showLowerBarComposer_artwork; - themeControls.showLowerBarComposer_compact = pref.showLowerBarComposer_compact; - themeControls.showLowerBarArtistFlags_default = pref.showLowerBarArtistFlags_default; - themeControls.showLowerBarArtistFlags_artwork = pref.showLowerBarArtistFlags_artwork; - themeControls.showLowerBarArtistFlags_compact = pref.showLowerBarArtistFlags_compact; - themeControls.showLowerBarVersion_default = pref.showLowerBarVersion_default; - themeControls.showLowerBarVersion_artwork = pref.showLowerBarVersion_artwork; - themeControls.showLowerBarVersion_compact = pref.showLowerBarVersion_compact; - themeControls.showProgressBar_default = pref.showProgressBar_default; - themeControls.showProgressBar_artwork = pref.showProgressBar_artwork; - themeControls.showProgressBar_compact = pref.showProgressBar_compact; - themeControls.showPeakmeterBar_default = pref.showPeakmeterBar_default; - themeControls.showPeakmeterBar_artwork = pref.showPeakmeterBar_artwork; - themeControls.showPeakmeterBar_compact = pref.showPeakmeterBar_compact; - themeControls.showWaveformBar_default = pref.showWaveformBar_default; - themeControls.showWaveformBar_artwork = pref.showWaveformBar_artwork; - themeControls.showWaveformBar_compact = pref.showWaveformBar_compact; - themeControls.seekbar = pref.seekbar; - themeControls.progressBarWheelSeekSpeed = pref.progressBarWheelSeekSpeed; - themeControls.progressBarRefreshRate = pref.progressBarRefreshRate; - themeControls.peakmeterBarDesign = pref.peakmeterBarDesign; - themeControls.peakmeterBarVertSize = pref.peakmeterBarVertSize; - themeControls.peakmeterBarVertDbRange = pref.peakmeterBarVertDbRange; - themeControls.peakmeterBarOverBars = pref.peakmeterBarOverBars; - themeControls.peakmeterBarOuterBars = pref.peakmeterBarOuterBars; - themeControls.peakmeterBarOuterPeaks = pref.peakmeterBarOuterPeaks; - themeControls.peakmeterBarMainBars = pref.peakmeterBarMainBars; - themeControls.peakmeterBarMainPeaks = pref.peakmeterBarMainPeaks; - themeControls.peakmeterBarMiddleBars = pref.peakmeterBarMiddleBars; - themeControls.peakmeterBarProgBar = pref.peakmeterBarProgBar; - themeControls.peakmeterBarGaps = pref.peakmeterBarGaps; - themeControls.peakmeterBarGrid = pref.peakmeterBarGrid; - themeControls.peakmeterBarInfo = pref.peakmeterBarInfo; - themeControls.peakmeterBarVertPeaks = pref.peakmeterBarVertPeaks; - themeControls.peakmeterBarVertBaseline = pref.peakmeterBarVertBaseline; - themeControls.peakmeterBarRefreshRate = pref.peakmeterBarRefreshRate; - themeControls.waveformBarMode = pref.waveformBarMode; - themeControls.waveformBarAnalysis = pref.waveformBarAnalysis; - themeControls.waveformBarDesign = pref.waveformBarDesign; - themeControls.waveformBarSizeWave = pref.waveformBarSizeWave; - themeControls.waveformBarSizeBars = pref.waveformBarSizeBars; - themeControls.waveformBarSizeDots = pref.waveformBarSizeDots; - themeControls.waveformBarSizeHalf = pref.waveformBarSizeHalf; - themeControls.waveformBarSizeNormalize = pref.waveformBarSizeNormalize; - themeControls.waveformBarPaint = pref.waveformBarPaint; - themeControls.waveformBarPrepaint = pref.waveformBarPrepaint; - themeControls.waveformBarPrepaintFront = pref.waveformBarPrepaintFront === Infinity ? 'Infinity' : pref.waveformBarPrepaintFront; - themeControls.waveformBarAnimate = pref.waveformBarAnimate; - themeControls.waveformBarBPM = pref.waveformBarBPM; - themeControls.waveformBarInvertHalfbars = pref.waveformBarInvertHalfbars; - themeControls.waveformBarIndicator = pref.waveformBarIndicator; - themeControls.waveformBarRefreshRate = pref.waveformBarRefreshRate; - themeControls.waveformBarRefreshRateVar = pref.waveformBarRefreshRateVar; - themeControls.maximizeToFullscreen = pref.maximizeToFullscreen; - themeControls.switchPlaybackTime = pref.switchPlaybackTime; - themeControls.playbackOrder = pref.playbackOrder; - } else { - pref.showPanelDetails_default = custom ? themeControls.showPanelDetails_default : true; - pref.showPanelDetails_artwork = custom ? themeControls.showPanelDetails_artwork : true; - pref.showPanelLibrary_default = custom ? themeControls.showPanelLibrary_default : true; - pref.showPanelLibrary_artwork = custom ? themeControls.showPanelLibrary_artwork : true; - pref.showPanelBiography_default = custom ? themeControls.showPanelBiography_default : true; - pref.showPanelBiography_artwork = custom ? themeControls.showPanelBiography_artwork : true; - pref.showPanelLyrics_default = custom ? themeControls.showPanelLyrics_default : true; - pref.showPanelLyrics_artwork = custom ? themeControls.showPanelLyrics_artwork : true; - pref.showPanelRating_default = custom ? themeControls.showPanelRating_default : true; - pref.showPanelRating_artwork = custom ? themeControls.showPanelRating_artwork : true; - pref.topMenuAlignment = custom ? themeControls.topMenuAlignment : 'center'; - pref.topMenuCompact = custom ? themeControls.topMenuCompact : true; - pref.albumArtAlign = custom ? themeControls.albumArtAlign : 'right'; - pref.albumArtBg = custom ? themeControls.albumArtBg : 'left'; - pref.albumArtScale = custom ? themeControls.albumArtScale : 'cropped'; - pref.albumArtAspectRatioLimit = custom ? themeControls.albumArtAspectRatioLimit : true; - pref.cycleArt = custom ? themeControls.cycleArt : false; - pref.cycleArtMWheel = custom ? themeControls.cycleArtMWheel : true; - pref.loadEmbeddedAlbumArtFirst = custom ? themeControls.loadEmbeddedAlbumArtFirst : false; - pref.showHiResAudioBadge = custom ? themeControls.showHiResAudioBadge : false; - pref.hiResAudioBadgeRound = custom ? themeControls.hiResAudioBadgeRound : false; - pref.hiResAudioBadgeSize = custom ? themeControls.hiResAudioBadgeSize : 'normal'; - pref.hiResAudioBadgePos = custom ? themeControls.hiResAudioBadgePos : 'bottomright'; - pref.showPause = custom ? themeControls.showPause : true; - pref.jumpSearchIncludeLibrary = custom ? themeControls.jumpSearchIncludeLibrary : true; - pref.jumpSearchIncludePlaylist = custom ? themeControls.jumpSearchIncludePlaylist : true; - pref.jumpSearchComposerOnly = custom ? themeControls.jumpSearchComposerOnly : false; - pref.playlistWheelScrollSteps = custom ? themeControls.playlistWheelScrollSteps : 3; - pref.playlistWheelScrollDuration = custom ? themeControls.playlistWheelScrollDuration : 300; - pref.playlistAutoScrollNowPlaying = custom ? themeControls.playlistAutoScrollNowPlaying : false; - pref.playlistAutoHideScrollbar = custom ? themeControls.playlistAutoHideScrollbar : true; - pref.playlistSmoothScrolling = custom ? themeControls.playlistSmoothScrolling : true; - ppt.scrollStep = custom ? themeControls.scrollStepLib : 3; - ppt.durationScroll = custom ? themeControls.durationScrollLib : 500; - pref.libraryAutoScrollNowPlaying = custom ? themeControls.libraryAutoScrollNowPlaying : false; - pref.libraryAutoHideScrollbar = custom ? themeControls.libraryAutoHideScrollbar : true; - ppt.sbarShow = pref.libraryAutoHideScrollbar ? 1 : 2; - ppt.smooth = custom ? themeControls.smoothLib : true; - pptBio.scrollStep = custom ? themeControls.scrollStepBio : 3; - pptBio.durationScroll = custom ? themeControls.durationScrollBio : 500; - pref.biographyAutoHideScrollbar = custom ? themeControls.biographyAutoHideScrollbar : true; - pptBio.sbarShow = pref.biographyAutoHideScrollbar ? 1 : 2; - pptBio.smooth = custom ? themeControls.smoothBio : true; - pref.showTooltipTruncated = custom ? themeControls.showTooltipTruncated : true; - pref.showTooltipTimeline = custom ? themeControls.showTooltipTimeline : true; - pref.showTooltipVolume = custom ? themeControls.showTooltipVolume : false; - pref.showTooltipVolumeInPercent = custom ? themeControls.showTooltipVolumeInPercent : false; - pref.showTooltipMain = custom ? themeControls.showTooltipMain : false; - pref.showTooltipLibrary = custom ? themeControls.showTooltipLibrary : false; - pref.showTooltipBiography = custom ? themeControls.showTooltipBiography : false; - pref.showStyledTooltips = custom ? themeControls.showStyledTooltips : true; - pref.panelWidthAuto = custom ? themeControls.panelWidthAuto : false; - pref.showPanelOnStartup = custom ? themeControls.showPanelOnStartup : 'playlist'; - pref.showPreloaderLogo = custom ? themeControls.showPreloaderLogo : true; - pref.returnToHomeOnPlaybackStop = custom ? themeControls.returnToHomeOnPlaybackStop : true; - pref.hideMiddlePanelShadow = custom ? themeControls.hideMiddlePanelShadow : false; - pref.lockPlayerSize = custom ? themeControls.lockPlayerSize : false; - pref.transportButtonSize_default = custom ? themeControls.transportButtonSize_default : 32; - pref.transportButtonSize_artwork = custom ? themeControls.transportButtonSize_artwork : 32; - pref.transportButtonSize_compact = custom ? themeControls.transportButtonSize_compact : 32; - pref.transportButtonSpacing_default = custom ? themeControls.transportButtonSpacing_default : 5; - pref.transportButtonSpacing_artwork = custom ? themeControls.transportButtonSpacing_artwork : 5; - pref.transportButtonSpacing_compact = custom ? themeControls.transportButtonSpacing_compact : 5; - pref.showTransportControls_default = custom ? themeControls.showTransportControls_default : true; - pref.showTransportControls_artwork = custom ? themeControls.showTransportControls_artwork : true; - pref.showTransportControls_compact = custom ? themeControls.showTransportControls_compact : true; - pref.showPlaybackOrderBtn_default = custom ? themeControls.showPlaybackOrderBtn_default : true; - pref.showPlaybackOrderBtn_artwork = custom ? themeControls.showPlaybackOrderBtn_artwork : true; - pref.showPlaybackOrderBtn_compact = custom ? themeControls.showPlaybackOrderBtn_compact : true; - pref.showReloadBtn_default = custom ? themeControls.showReloadBtn_default : false; - pref.showReloadBtn_artwork = custom ? themeControls.showReloadBtn_artwork : false; - pref.showReloadBtn_compact = custom ? themeControls.showReloadBtn_compact : false; - pref.showVolumeBtn_default = custom ? themeControls.showVolumeBtn_default : true; - pref.showVolumeBtn_artwork = custom ? themeControls.showVolumeBtn_artwork : true; - pref.showVolumeBtn_compact = custom ? themeControls.showVolumeBtn_compact : true; - pref.autoHideVolumeBar = custom ? themeControls.autoHideVolumeBar : true; - pref.showPlaybackTime_default = custom ? themeControls.showPlaybackTime_default : true; - pref.showPlaybackTime_artwork = custom ? themeControls.showPlaybackTime_artwork : true; - pref.showPlaybackTime_compact = custom ? themeControls.showPlaybackTime_compact : true; - pref.showLowerBarArtist_default = custom ? themeControls.showLowerBarArtist_default : true; - pref.showLowerBarArtist_artwork = custom ? themeControls.showLowerBarArtist_artwork : true; - pref.showLowerBarArtist_compact = custom ? themeControls.showLowerBarArtist_compact : true; - pref.showLowerBarTrackNum_default = custom ? themeControls.showLowerBarTrackNum_default : true; - pref.showLowerBarTrackNum_artwork = custom ? themeControls.showLowerBarTrackNum_artwork : true; - pref.showLowerBarTrackNum_compact = custom ? themeControls.showLowerBarTrackNum_compact : true; - pref.showLowerBarTitle_default = custom ? themeControls.showLowerBarTitle_default : true; - pref.showLowerBarTitle_artwork = custom ? themeControls.showLowerBarTitle_artwork : true; - pref.showLowerBarTitle_compact = custom ? themeControls.showLowerBarTitle_compact : true; - pref.showLowerBarComposer_default = custom ? themeControls.showLowerBarComposer_default : false; - pref.showLowerBarComposer_artwork = custom ? themeControls.showLowerBarComposer_artwork : false; - pref.showLowerBarComposer_compact = custom ? themeControls.showLowerBarComposer_compact : false; - pref.showLowerBarArtistFlags_default = custom ? themeControls.showLowerBarArtistFlags_default : true; - pref.showLowerBarArtistFlags_artwork = custom ? themeControls.showLowerBarArtistFlags_artwork : true; - pref.showLowerBarArtistFlags_compact = custom ? themeControls.showLowerBarArtistFlags_compact : true; - pref.showLowerBarVersion_default = custom ? themeControls.showLowerBarVersion_default : true; - pref.showLowerBarVersion_artwork = custom ? themeControls.showLowerBarVersion_artwork : true; - pref.showLowerBarVersion_compact = custom ? themeControls.showLowerBarVersion_compact : true; - pref.showProgressBar_default = custom ? themeControls.showProgressBar_default : true; - pref.showProgressBar_artwork = custom ? themeControls.showProgressBar_artwork : true; - pref.showProgressBar_compact = custom ? themeControls.showProgressBar_compact : true; - pref.showPeakmeterBar_default = custom ? themeControls.showPeakmeterBar_default : true; - pref.showPeakmeterBar_artwork = custom ? themeControls.showPeakmeterBar_artwork : true; - pref.showPeakmeterBar_compact = custom ? themeControls.showPeakmeterBar_compact : true; - pref.showWaveformBar_default = custom ? themeControls.showWaveformBar_default : true; - pref.showWaveformBar_artwork = custom ? themeControls.showWaveformBar_artwork : true; - pref.showWaveformBar_compact = custom ? themeControls.showWaveformBar_compact : true; - pref.seekbar = custom ? themeControls.seekbar : 'progressbar'; - pref.progressBarWheelSeekSpeed = custom ? themeControls.progressBarWheelSeekSpeed : 5; - pref.progressBarRefreshRate = custom ? themeControls.progressBarRefreshRate : 'variable'; - pref.peakmeterBarDesign = custom ? themeControls.peakmeterBarDesign : 'horizontal'; - pref.peakmeterBarVertSize = custom ? themeControls.peakmeterBarVertSize : 20; - pref.peakmeterBarVertDbRange = custom ? themeControls.peakmeterBarVertDbRange : 220; - pref.peakmeterBarOverBars = custom ? themeControls.peakmeterBarOverBars : true; - pref.peakmeterBarOuterBars = custom ? themeControls.peakmeterBarOuterBars : true; - pref.peakmeterBarOuterPeaks = custom ? themeControls.peakmeterBarOuterPeaks : true; - pref.peakmeterBarMainBars = custom ? themeControls.peakmeterBarMainBars : true; - pref.peakmeterBarMainPeaks = custom ? themeControls.peakmeterBarMainPeaks : true; - pref.peakmeterBarMiddleBars = custom ? themeControls.peakmeterBarMiddleBars : true; - pref.peakmeterBarProgBar = custom ? themeControls.peakmeterBarProgBar : true; - pref.peakmeterBarGaps = custom ? themeControls.peakmeterBarGaps : false; - pref.peakmeterBarGrid = custom ? themeControls.peakmeterBarGrid : false; - pref.peakmeterBarInfo = custom ? themeControls.peakmeterBarInfo : false; - pref.peakmeterBarVertPeaks = custom ? themeControls.peakmeterBarVertPeaks : true; - pref.peakmeterBarVertBaseline = custom ? themeControls.peakmeterBarVertBaseline : true; - pref.peakmeterBarRefreshRate = custom ? themeControls.peakmeterBarRefreshRate : 80; - pref.waveformBarMode = custom ? themeControls.waveformBarMode : 'audiowaveform'; - pref.waveformBarAnalysis = custom ? themeControls.waveformBarAnalysis : 'rms_level'; - pref.waveformBarDesign = custom ? themeControls.waveformBarDesign : 'halfbars'; - pref.waveformBarSizeWave = custom ? themeControls.waveformBarSizeWave : 3; - pref.waveformBarSizeBars = custom ? themeControls.waveformBarSizeBars : 1; - pref.waveformBarSizeDots = custom ? themeControls.waveformBarSizeDots : 2; - pref.waveformBarSizeHalf = custom ? themeControls.waveformBarSizeHalf : 4; - pref.waveformBarSizeNormalize = custom ? themeControls.waveformBarSizeNormalize : false; - pref.waveformBarPaint = custom ? themeControls.waveformBarPaint : 'partial'; - pref.waveformBarPrepaint = custom ? themeControls.waveformBarPrepaint : true; - pref.waveformBarPrepaintFront = custom ? themeControls.waveformBarPrepaintFront : false; // ! Do not use Infinity here, set to false has same effect - pref.waveformBarAnimate = custom ? themeControls.waveformBarAnimate : true; - pref.waveformBarBPM = custom ? themeControls.waveformBarBPM : true; - pref.waveformBarInvertHalfbars = custom ? themeControls.waveformBarInvertHalfbars : true; - pref.waveformBarIndicator = custom ? themeControls.waveformBarIndicator : false; - pref.waveformBarRefreshRate = custom ? themeControls.waveformBarRefreshRate : 200; - pref.waveformBarRefreshRateVar = custom ? themeControls.waveformBarRefreshRateVar : false; - pref.maximizeToFullscreen = custom ? themeControls.maximizeToFullscreen : true; - pref.switchPlaybackTime = custom ? themeControls.switchPlaybackTime : false; - pref.playbackOrder = custom ? themeControls.playbackOrder : 'default'; + /** + * Sets display settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setDisplay() { + this._setSetting(grSet, 'displayRes', grCfg.themeDisplay, 'resolution', 'HD'); } - // * Playlist - if (save) { - themePlaylist.playlistLayout = pref.playlistLayout; - themePlaylist.showPlaylistManager_default = pref.showPlaylistManager_default; - themePlaylist.showPlaylistManager_artwork = pref.showPlaylistManager_artwork; - themePlaylist.showPlaylistManager_compact = pref.showPlaylistManager_compact; - themePlaylist.showPlaylistHistory = pref.showPlaylistHistory; - themePlaylist.autoHidePlman = pref.autoHidePlman; - themePlaylist.show_album_art = g_properties.show_album_art; - themePlaylist.auto_album_art = g_properties.auto_album_art; - themePlaylist.show_header = g_properties.show_header; - themePlaylist.show_rating_header = g_properties.show_rating_header; - themePlaylist.show_PLR_header = g_properties.show_PLR_header; - themePlaylist.use_compact_header = g_properties.use_compact_header; - themePlaylist.auto_collapse = g_properties.auto_collapse; - themePlaylist.hyperlinksCtrlClick = pref.hyperlinksCtrlClick; - themePlaylist.show_disc_header = g_properties.show_disc_header; - themePlaylist.show_group_info = g_properties.show_group_info; - themePlaylist.showWeblinks = pref.showWeblinks; - themePlaylist.showPlaylistFullDate = pref.showPlaylistFullDate; - themePlaylist.show_row_stripes = g_properties.show_row_stripes; - themePlaylist.show_playcount = g_properties.show_playcount; - themePlaylist.show_queue_position = g_properties.show_queue_position; - themePlaylist.show_rating = g_properties.show_rating; - themePlaylist.use_rating_from_tags = g_properties.use_rating_from_tags; - themePlaylist.showPlaylistRatingGrid = pref.showPlaylistRatingGrid; - themePlaylist.show_PLR = g_properties.show_PLR; - themePlaylist.showPlaylistTrackNumbers = pref.showPlaylistTrackNumbers; - themePlaylist.showPlaylistIndexNumbers = pref.showPlaylistIndexNumbers; - themePlaylist.showDifferentArtist = pref.showDifferentArtist; - themePlaylist.showArtistPlaylistRows = pref.showArtistPlaylistRows; - themePlaylist.showAlbumPlaylistRows = pref.showAlbumPlaylistRows; - themePlaylist.playlistTimeRemaining = pref.playlistTimeRemaining; - themePlaylist.showVinylNums = pref.showVinylNums; - themePlaylist.lastFmScrobblesFallback = pref.lastFmScrobblesFallback; - themePlaylist.playlistRowHover = pref.playlistRowHover; - themePlaylist.playlistSortOrderAuto = pref.playlistSortOrderAuto; - themePlaylist.playlistSortOrder = pref.playlistSortOrder; - themePlaylist.playlistSortOrderDirection = pref.playlistSortOrderDirection; - themePlaylist.playlist_stats_include_artist = g_properties.playlist_stats_include_artist; - themePlaylist.playlist_stats_include_album = g_properties.playlist_stats_include_album; - themePlaylist.playlist_stats_include_track = g_properties.playlist_stats_include_track; - themePlaylist.playlist_stats_include_year = g_properties.playlist_stats_include_year; - themePlaylist.playlist_stats_include_genre = g_properties.playlist_stats_include_genre; - themePlaylist.playlist_stats_include_label = g_properties.playlist_stats_include_label; - themePlaylist.playlist_stats_include_country = g_properties.playlist_stats_include_country; - themePlaylist.playlist_stats_include_stats = g_properties.playlist_stats_include_stats; - themePlaylist.playlist_stats_sort_by = g_properties.playlist_stats_sort_by; - themePlaylist.playlist_stats_sort_direction = g_properties.playlist_stats_sort_direction; - } else { - pref.playlistLayout = custom ? themePlaylist.playlistLayout : 'normal'; - pref.playlistLayoutNormal = true; - pref.showPlaylistManager_default = custom ? themePlaylist.showPlaylistManager_default : true; - pref.showPlaylistManager_artwork = custom ? themePlaylist.showPlaylistManager_artwork : false; - pref.showPlaylistManager_compact = custom ? themePlaylist.showPlaylistManager_compact : false; - pref.showPlaylistHistory = custom ? themePlaylist.showPlaylistHistory : true; - pref.autoHidePlman = custom ? themePlaylist.autoHidePlman : true; - g_properties.show_album_art = custom ? themePlaylist.show_album_art : true; - g_properties.auto_album_art = custom ? themePlaylist.auto_album_art : false; - g_properties.show_header = custom ? themePlaylist.show_header : true; - g_properties.show_rating_header = custom ? themePlaylist.show_rating_header : true; - g_properties.show_PLR_header = custom ? themePlaylist.show_PLR_header : false; - g_properties.use_compact_header = custom ? themePlaylist.use_compact_header : false; - g_properties.auto_collapse = custom ? themePlaylist.auto_collapse : false; - pref.hyperlinksCtrlClick = custom ? themePlaylist.hyperlinksCtrlClick : false; - g_properties.show_disc_header = custom ? themePlaylist.show_disc_header : true; - g_properties.show_group_info = custom ? themePlaylist.show_group_info : true; - pref.showWeblinks = custom ? themePlaylist.showWeblinks : true; - pref.showPlaylistFullDate = custom ? themePlaylist.showPlaylistFullDate : false; - g_properties.show_row_stripes = custom ? themePlaylist.show_row_stripes : false; - g_properties.show_playcount = custom ? themePlaylist.show_playcount : true; - g_properties.show_queue_position = custom ? themePlaylist.show_queue_position : true; - g_properties.show_rating = custom ? themePlaylist.show_rating : true; - g_properties.use_rating_from_tags = custom ? themePlaylist.use_rating_from_tags : false; - g_properties.show_PLR = custom ? themePlaylist.show_PLR : false; - pref.showPlaylistRatingGrid = custom ? themePlaylist.showPlaylistRatingGrid : false; - pref.showPlaylistTrackNumbers = custom ? themePlaylist.showPlaylistTrackNumbers : true; - pref.showPlaylistIndexNumbers = custom ? themePlaylist.showPlaylistIndexNumbers : false; - pref.showDifferentArtist = custom ? themePlaylist.showDifferentArtist : false; - pref.showArtistPlaylistRows = custom ? themePlaylist.showArtistPlaylistRows : false; - pref.showAlbumPlaylistRows = custom ? themePlaylist.showAlbumPlaylistRows : false; - pref.playlistTimeRemaining = custom ? themePlaylist.playlistTimeRemaining : false; - pref.showVinylNums = custom ? themePlaylist.showVinylNums : true; - pref.lastFmScrobblesFallback = custom ? themePlaylist.lastFmScrobblesFallback : true; - pref.playlistRowHover = custom ? themePlaylist.playlistRowHover : true; - pref.playlistSortOrderAuto = custom ? themePlaylist.playlistSortOrderAuto : false; - pref.playlistSortOrder = custom ? themePlaylist.playlistSortOrder : ''; - pref.playlistSortOrderDirection = custom ? themePlaylist.playlistSortOrderDirection : '_asc'; - g_properties.playlist_stats_include_artist = custom ? themePlaylist.playlist_stats_include_artist : true; - g_properties.playlist_stats_include_album = custom ? themePlaylist.playlist_stats_include_album : true; - g_properties.playlist_stats_include_track = custom ? themePlaylist.playlist_stats_include_track : true; - g_properties.playlist_stats_include_year = custom ? themePlaylist.playlist_stats_include_year : false; - g_properties.playlist_stats_include_genre = custom ? themePlaylist.playlist_stats_include_genre : false; - g_properties.playlist_stats_include_label = custom ? themePlaylist.playlist_stats_include_label : false; - g_properties.playlist_stats_include_country = custom ? themePlaylist.playlist_stats_include_country : false; - g_properties.playlist_stats_include_stats = custom ? themePlaylist.playlist_stats_include_stats : true; - g_properties.playlist_stats_sort_by = custom ? themePlaylist.playlist_stats_sort_by : ''; - g_properties.playlist_stats_sort_direction = custom ? themePlaylist.playlist_stats_sort_direction : '_dsc'; + /** + * Sets brightness settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setBrightness() { + this._setSetting(grSet, 'themeBrightness', grCfg.themeBrightness, 'themeBrightness', 'default'); + this._setSetting(grSet, 'themeBrightness_day', grCfg.themeBrightness, 'themeBrightness_day', 'default'); + this._setSetting(grSet, 'themeBrightness_night', grCfg.themeBrightness, 'themeBrightness_night', 'default'); } - // * Playlist properties - g_properties.list_left_pad = 0; - g_properties.list_top_pad = 0; - g_properties.list_right_pad = 0; - g_properties.list_bottom_pad = 15; - g_properties.show_scrollbar = false; - g_properties.scrollbar_right_pad = 0; - g_properties.scrollbar_top_pad = 0; - g_properties.scrollbar_bottom_pad = 3; - g_properties.scrollbar_w = ''; - g_properties.row_h = 20; - g_properties.scroll_pos = 0; - g_properties.wheel_scroll_page = false; - g_properties.rows_in_header = 4; - g_properties.rows_in_compact_header = 3; - g_properties.show_playlist_info = true; - g_properties.collapse_on_playlist_switch = false; - g_properties.collapse_on_start = false; - - // * Details - if (save) { - themeDetails.showDiscArtStub = pref.showDiscArtStub; - themeDetails.noDiscArtStub = pref.noDiscArtStub; - themeDetails.discArtStub = pref.discArtStub; - themeDetails.displayDiscArt = pref.displayDiscArt; - themeDetails.discArtOnTop = pref.discArtOnTop; - themeDetails.filterDiscJpgsFromAlbumArt = pref.filterDiscJpgsFromAlbumArt; - themeDetails.spinDiscArt = pref.spinDiscArt; - themeDetails.spinDiscArtImageCount = pref.spinDiscArtImageCount; - themeDetails.spinDiscArtRedrawInterval = pref.spinDiscArtRedrawInterval; - themeDetails.rotateDiscArt = pref.rotateDiscArt; - themeDetails.rotationAmt = pref.rotationAmt; - themeDetails.artRotateDelay = pref.artRotateDelay; - themeDetails.discArtDisplayAmount = pref.discArtDisplayAmount; - themeDetails.detailsAlbumArtOpacity = pref.detailsAlbumArtOpacity; - themeDetails.detailsAlbumArtDiscAreaOpacity = pref.detailsAlbumArtDiscAreaOpacity; - themeDetails.showGridArtist_default = pref.showGridArtist_default; - themeDetails.showGridArtist_artwork = pref.showGridArtist_artwork; - themeDetails.showGridTrackNum_default = pref.showGridTrackNum_default; - themeDetails.showGridTrackNum_artwork = pref.showGridTrackNum_artwork; - themeDetails.showGridTitle_default = pref.showGridTitle_default; - themeDetails.showGridTitle_artwork = pref.showGridTitle_artwork; - themeDetails.showGridPlayingPlaylist = pref.showGridPlayingPlaylist; - themeDetails.showGridTimeline_default = pref.showGridTimeline_default; - themeDetails.showGridTimeline_artwork = pref.showGridTimeline_artwork; - themeDetails.showGridArtistFlags_default = pref.showGridArtistFlags_default; - themeDetails.showGridArtistFlags_artwork = pref.showGridArtistFlags_artwork; - themeDetails.showGridReleaseFlags_default = pref.showGridReleaseFlags_default; - themeDetails.showGridReleaseFlags_artwork = pref.showGridReleaseFlags_artwork; - themeDetails.showGridCodecLogo_default = pref.showGridCodecLogo_default; - themeDetails.showGridCodecLogo_artwork = pref.showGridCodecLogo_artwork; - themeDetails.showGridChannelLogo_default = pref.showGridChannelLogo_default; - themeDetails.showGridChannelLogo_artwork = pref.showGridChannelLogo_artwork; - themeDetails.autoHideGridMetadata = pref.autoHideGridMetadata; - themeDetails.noDiscArtBg = pref.noDiscArtBg; - themeDetails.labelArtOnBg = pref.labelArtOnBg; - } else { - pref.showDiscArtStub = custom ? themeDetails.showDiscArtStub : true; - pref.noDiscArtStub = custom ? themeDetails.noDiscArtStub : false; - pref.discArtStub = custom ? themeDetails.discArtStub : 'cdAlbumCover'; - pref.displayDiscArt = custom ? themeDetails.displayDiscArt : true; - pref.discArtOnTop = custom ? themeDetails.discArtOnTop : false; - pref.filterDiscJpgsFromAlbumArt = custom ? themeDetails.filterDiscJpgsFromAlbumArt : true; - pref.spinDiscArt = custom ? themeDetails.spinDiscArt : false; - pref.spinDiscArtImageCount = custom ? themeDetails.spinDiscArtImageCount : 72; - pref.spinDiscArtRedrawInterval = custom ? themeDetails.spinDiscArtRedrawInterval : 75; - pref.rotateDiscArt = custom ? themeDetails.rotateDiscArt : true; - pref.rotationAmt = custom ? themeDetails.rotationAmt : 3; - pref.artRotateDelay = custom ? themeDetails.artRotateDelay : 30; - pref.discArtDisplayAmount = custom ? themeDetails.discArtDisplayAmount : 0.5; - pref.detailsAlbumArtOpacity = custom ? themeDetails.detailsAlbumArtOpacity : 255; - pref.detailsAlbumArtDiscAreaOpacity = custom ? themeDetails.detailsAlbumArtDiscAreaOpacity : 255; - pref.showGridArtist_default = custom ? themeDetails.showGridArtist_default : false; - pref.showGridArtist_artwork = custom ? themeDetails.showGridArtist_artwork : false; - pref.showGridTrackNum_default = custom ? themeDetails.showGridTrackNum_default : false; - pref.showGridTrackNum_artwork = custom ? themeDetails.showGridTrackNum_artwork : false; - pref.showGridTitle_default = custom ? themeDetails.showGridTitle_default : false; - pref.showGridTitle_artwork = custom ? themeDetails.showGridTitle_artwork : false; - pref.showGridPlayingPlaylist = custom ? themeDetails.showGridPlayingPlaylist : false; - pref.showGridTimeline_default = custom ? themeDetails.showGridTimeline_default : true; - pref.showGridTimeline_artwork = custom ? themeDetails.showGridTimeline_artwork : true; - pref.showGridArtistFlags_default = custom ? themeDetails.showGridArtistFlags_default : true; - pref.showGridArtistFlags_artwork = custom ? themeDetails.showGridArtistFlags_artwork : true; - pref.showGridReleaseFlags_default = custom ? themeDetails.showGridReleaseFlags_default : 'logo'; - pref.showGridReleaseFlags_artwork = custom ? themeDetails.showGridReleaseFlags_artwork : 'logo'; - pref.showGridCodecLogo_default = custom ? themeDetails.showGridCodecLogo_default : 'logo'; - pref.showGridCodecLogo_artwork = custom ? themeDetails.showGridCodecLogo_artwork : 'logo'; - pref.showGridChannelLogo_default = custom ? themeDetails.showGridChannelLogo_default : 'logo'; - pref.showGridChannelLogo_artwork = custom ? themeDetails.showGridChannelLogo_artwork : 'logo'; - pref.autoHideGridMetadata = custom ? themeDetails.autoHideGridMetadata : true; - pref.noDiscArtBg = custom ? themeDetails.noDiscArtBg : true; - pref.labelArtOnBg = custom ? themeDetails.labelArtOnBg : false; + /** + * Sets font size settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setFontSize() { + this._setSetting(grSet, 'menuFontSize_default', grCfg.themeFontSize, 'menuFontSize_default', RES._QHD ? 14 : 12); + this._setSetting(grSet, 'menuFontSize_artwork', grCfg.themeFontSize, 'menuFontSize_artwork', RES._QHD ? 14 : 12); + this._setSetting(grSet, 'menuFontSize_compact', grCfg.themeFontSize, 'menuFontSize_compact', RES._QHD ? 14 : 12); + this._setSetting(grSet, 'lowerBarFontSize_default', grCfg.themeFontSize, 'lowerBarFontSize_default', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'lowerBarFontSize_artwork', grCfg.themeFontSize, 'lowerBarFontSize_artwork', RES._QHD ? 18 : 16); + this._setSetting(grSet, 'lowerBarFontSize_compact', grCfg.themeFontSize, 'lowerBarFontSize_compact', RES._QHD ? 18 : 16); + this._setSetting(grSet, 'notificationFontSize_default', grCfg.themeFontSize, 'notificationFontSize_default', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'notificationFontSize_artwork', grCfg.themeFontSize, 'notificationFontSize_artwork', RES._QHD ? 18 : 16); + this._setSetting(grSet, 'notificationFontSize_compact', grCfg.themeFontSize, 'notificationFontSize_compact', RES._QHD ? 18 : 16); + this._setSetting(grSet, 'popupFontSize_default', grCfg.themeFontSize, 'popupFontSize_default', RES._QHD ? 18 : 16); + this._setSetting(grSet, 'popupFontSize_artwork', grCfg.themeFontSize, 'popupFontSize_artwork', RES._QHD ? 16 : 14); + this._setSetting(grSet, 'popupFontSize_compact', grCfg.themeFontSize, 'popupFontSize_compact', RES._QHD ? 16 : 14); + this._setSetting(grSet, 'tooltipFontSize_default', grCfg.themeFontSize, 'tooltipFontSize_default', RES._QHD ? 18 : 16); + this._setSetting(grSet, 'tooltipFontSize_artwork', grCfg.themeFontSize, 'tooltipFontSize_artwork', RES._QHD ? 16 : 14); + this._setSetting(grSet, 'tooltipFontSize_compact', grCfg.themeFontSize, 'tooltipFontSize_compact', RES._QHD ? 16 : 14); + this._setSetting(grSet, 'gridArtistFontSize_default', grCfg.themeFontSize, 'gridArtistFontSize_default', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridArtistFontSize_artwork', grCfg.themeFontSize, 'gridArtistFontSize_artwork', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridTrackNumFontSize_default', grCfg.themeFontSize, 'gridTrackNumFontSize_default', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridTrackNumFontSize_artwork', grCfg.themeFontSize, 'gridTrackNumFontSize_artwork', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridTitleFontSize_default', grCfg.themeFontSize, 'gridTitleFontSize_default', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridTitleFontSize_artwork', grCfg.themeFontSize, 'gridTitleFontSize_artwork', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridAlbumFontSize_default', grCfg.themeFontSize, 'gridAlbumFontSize_default', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridAlbumFontSize_artwork', grCfg.themeFontSize, 'gridAlbumFontSize_artwork', RES._QHD ? 20 : 18); + this._setSetting(grSet, 'gridKeyFontSize_default', grCfg.themeFontSize, 'gridKeyFontSize_default', RES._QHD ? 19 : 17); + this._setSetting(grSet, 'gridKeyFontSize_artwork', grCfg.themeFontSize, 'gridKeyFontSize_artwork', RES._QHD ? 19 : 17); + this._setSetting(grSet, 'gridValueFontSize_default', grCfg.themeFontSize, 'gridValueFontSize_default', RES._QHD ? 19 : 17); + this._setSetting(grSet, 'gridValueFontSize_artwork', grCfg.themeFontSize, 'gridValueFontSize_artwork', RES._QHD ? 19 : 17); + this._setSetting(grSet, 'playlistHeaderFontSize_default', grCfg.themeFontSize, 'playlistHeaderFontSize_default', RES._QHD ? 17 : 15); + this._setSetting(grSet, 'playlistHeaderFontSize_artwork', grCfg.themeFontSize, 'playlistHeaderFontSize_artwork', RES._QHD ? 17 : 15); + this._setSetting(grSet, 'playlistHeaderFontSize_compact', grCfg.themeFontSize, 'playlistHeaderFontSize_compact', RES._QHD ? 17 : 15); + this._setSetting(grSet, 'playlistFontSize_default', grCfg.themeFontSize, 'playlistFontSize_default', RES._QHD ? 14 : 12); + this._setSetting(grSet, 'playlistFontSize_artwork', grCfg.themeFontSize, 'playlistFontSize_artwork', RES._QHD ? 14 : 12); + this._setSetting(grSet, 'playlistFontSize_compact', grCfg.themeFontSize, 'playlistFontSize_compact', RES._QHD ? 14 : 12); + this._setSetting(libSet, 'baseFontSize_default', grCfg.themeFontSize, 'libraryFontSize_default', RES._4K ? 24 : RES._QHD ? 14 : 12); + this._setSetting(libSet, 'baseFontSize_artwork', grCfg.themeFontSize, 'libraryFontSize_artwork', RES._4K ? 24 : RES._QHD ? 14 : 12); + this._setSetting(bioSet, 'baseFontSizeBio_default', grCfg.themeFontSize, 'biographyFontSize_default', RES._4K ? 24 : RES._QHD ? 14 : 12); + this._setSetting(bioSet, 'baseFontSizeBio_artwork', grCfg.themeFontSize, 'biographyFontSize_artwork', RES._4K ? 24 : RES._QHD ? 14 : 12); + this._setSetting(grSet, 'lyricsFontSize_default', grCfg.themeFontSize, 'lyricsFontSize_default', RES._QHD ? 22 : 20); + this._setSetting(grSet, 'lyricsFontSize_artwork', grCfg.themeFontSize, 'lyricsFontSize_artwork', RES._QHD ? 22 : 20); } - // * Library - if (save) { - themeLibrary.libraryLayout = pref.libraryLayout; - themeLibrary.libraryLayoutFullPreset = pref.libraryLayoutFullPreset; - themeLibrary.libraryLayoutSplitPreset = pref.libraryLayoutSplitPreset; - themeLibrary.libraryLayoutSplitPreset2 = pref.libraryLayoutSplitPreset2; - themeLibrary.libraryLayoutSplitPreset3 = pref.libraryLayoutSplitPreset3; - themeLibrary.libraryLayoutSplitPreset4 = pref.libraryLayoutSplitPreset4; - themeLibrary.libraryDesign = pref.libraryDesign; - themeLibrary.libraryTheme = pref.libraryTheme; - themeLibrary.libraryThumbnailSize = pref.libraryThumbnailSize; - themeLibrary.libraryThumbnailBorder = pref.libraryThumbnailBorder; - themeLibrary.albumArtShow = ppt.albumArtShow; - themeLibrary.itemOverlayType = ppt.itemOverlayType; - themeLibrary.albumArtLetter = ppt.albumArtLetter; - themeLibrary.albumArtLetterNo = ppt.albumArtLetterNo; - themeLibrary.artId = ppt.artId; - themeLibrary.albumArtGrpLevel = ppt.albumArtGrpLevel; - themeLibrary.imgStyleFront = ppt.imgStyleFront; - themeLibrary.imgStyleBack = ppt.imgStyleBack; - themeLibrary.imgStyleDisc = ppt.imgStyleDisc; - themeLibrary.imgStyleIcon = ppt.imgStyleIcon; - themeLibrary.imgStyleArtist = ppt.imgStyleArtist; - themeLibrary.albumArtLabelType = ppt.albumArtLabelType; - themeLibrary.albumArtFlipLabels = ppt.albumArtFlipLabels; - themeLibrary.actionMode = ppt.actionMode; - themeLibrary.clickAction = ppt.clickAction; - themeLibrary.dblClickAction = ppt.dblClickAction; - themeLibrary.mbtnClickAction = ppt.mbtnClickAction; - themeLibrary.altClickAction = ppt.altClickAction; - themeLibrary.autoPlay = ppt.autoPlay; - themeLibrary.keyAction = ppt.keyAction; - themeLibrary.rememberTree = ppt.rememberTree; - themeLibrary.artTreeSameView = ppt.artTreeSameView; - themeLibrary.presetLoadCurView = ppt.presetLoadCurView; - themeLibrary.libraryPlaylistSwitch = pref.libraryPlaylistSwitch; - themeLibrary.rootNode = ppt.rootNode; - themeLibrary.nodeCounts = ppt.nodeCounts; - themeLibrary.countsRight = ppt.countsRight; - themeLibrary.autoCollapse = ppt.autoCollapse; - themeLibrary.itemShowStatistics = ppt.itemShowStatistics; - themeLibrary.highLightNowplaying = ppt.highLightNowplaying; - themeLibrary.showTracks = ppt.showTracks; - themeLibrary.rowStripes = ppt.rowStripes; - themeLibrary.fullLineSelection = ppt.fullLineSelection; - themeLibrary.libraryRowHover = pref.libraryRowHover; - themeLibrary.filterBy = ppt.filterBy; - themeLibrary.sortOrder = ppt.sortOrder; - themeLibrary.yearBeforeAlbum = ppt.yearBeforeAlbum; - themeLibrary.albumArtViewBy = ppt.albumArtViewBy; - themeLibrary.treeViewBy = ppt.treeViewBy; - themeLibrary.librarySource = ppt.libSource; - themeLibrary.librarySourceFixedPlaylist = ppt.fixedPlaylist; - themeLibrary.librarySourceFixedPlaylistName = ppt.fixedPlaylistName; - } else { - pref.libraryDesign = custom ? themeLibrary.libraryDesign : 'reborn'; - setLibraryDesign(); - - pref.libraryLayout = pref.libraryDesign === 'flowMode' ? 'full' : custom ? themeLibrary.libraryLayout : 'normal'; - pref.libraryLayoutFullPreset = custom ? themeLibrary.libraryLayoutFullPreset : true; - pref.libraryLayoutSplitPreset = custom ? themeLibrary.libraryLayoutSplitPreset : true; - pref.libraryLayoutSplitPreset2 = custom ? themeLibrary.libraryLayoutSplitPreset2 : false; - pref.libraryLayoutSplitPreset3 = custom ? themeLibrary.libraryLayoutSplitPreset3 : false; - pref.libraryLayoutSplitPreset4 = custom ? themeLibrary.libraryLayoutSplitPreset4 : false; - ppt.theme = pref.libraryTheme = custom ? themeLibrary.libraryTheme : 0; - pref.libraryThumbnailSizeSaved = ppt.thumbNailSize = pref.libraryThumbnailSize = custom ? themeLibrary.libraryThumbnailSize : 'auto'; - pref.libraryThumbnailBorder = custom ? themeLibrary.libraryThumbnailBorder : 'border'; - ppt.albumArtShow = custom ? themeLibrary.albumArtShow : false; - ppt.itemOverlayType = custom ? themeLibrary.itemOverlayType : 0; - ppt.albumArtLetter = custom ? themeLibrary.albumArtLetter : true; - ppt.albumArtLetterNo = custom ? themeLibrary.albumArtLetterNo : 1; - ppt.artId = custom ? themeLibrary.artId : 0; - ppt.albumArtGrpLevel = custom ? themeLibrary.albumArtGrpLevel : 0; - ppt.imgStyleFront = custom ? themeLibrary.imgStyleFront : 1; - ppt.imgStyleBack = custom ? themeLibrary.imgStyleBack : 1; - ppt.imgStyleDisc = custom ? themeLibrary.imgStyleDisc : 1; - ppt.imgStyleIcon = custom ? themeLibrary.imgStyleIcon : 1; - ppt.imgStyleArtist = custom ? themeLibrary.imgStyleArtist : 1; - ppt.albumArtLabelType = custom ? themeLibrary.albumArtLabelType : 1; - ppt.albumArtFlipLabels = custom ? themeLibrary.albumArtFlipLabels : false; - ppt.actionMode = custom ? themeLibrary.actionMode : 0; - ppt.clickAction = custom ? themeLibrary.clickAction : 0; - ppt.dblClickAction = custom ? themeLibrary.dblClickAction : 1; - ppt.mbtnClickAction = custom ? themeLibrary.mbtnClickAction : 1; - ppt.altClickAction = custom ? themeLibrary.altClickAction : 1; - ppt.autoPlay = custom ? themeLibrary.autoPlay : true; - ppt.keyAction = custom ? themeLibrary.keyAction : 0; - ppt.rememberTree = custom ? themeLibrary.rememberTree : false; - ppt.artTreeSameView = custom ? themeLibrary.artTreeSameView : false; - ppt.presetLoadCurView = custom ? themeLibrary.presetLoadCurView : true; - pref.libraryPlaylistSwitch = custom ? themeLibrary.libraryPlaylistSwitch : false; - ppt.rootNode = custom ? themeLibrary.rootNode : 3; - ppt.nodeCounts = custom ? themeLibrary.nodeCounts : 1; - ppt.countsRight = custom ? themeLibrary.countsRight : true; - ppt.autoCollapse = custom ? themeLibrary.autoCollapse : false; - ppt.itemShowStatistics = custom ? themeLibrary.itemShowStatistics : 0; - ppt.highLightNowplaying = custom ? themeLibrary.highLightNowplaying : true; - ppt.showTracks = custom ? themeLibrary.showTracks : true; - ppt.rowStripes = custom ? themeLibrary.rowStripes : false; - ppt.fullLineSelection = custom ? themeLibrary.fullLineSelection : true; - pref.libraryRowHover = custom ? themeLibrary.libraryRowHover : true; - ppt.filterBy = custom ? themeLibrary.filterBy : 0; - ppt.sortOrder = custom ? themeLibrary.sortOrder : 'default'; - ppt.yearBeforeAlbum = custom ? themeLibrary.yearBeforeAlbum : true; - ppt.albumArtViewBy = custom ? themeLibrary.albumArtViewBy : 0; - ppt.treeViewBy = custom ? themeLibrary.treeViewBy : 0; - ppt.libSource = pref.librarySource = custom ? themeLibrary.librarySource : 1; - ppt.fixedPlaylist = pref.librarySourceFixedPlaylist = custom ? themeLibrary.librarySourceFixedPlaylist : false; - ppt.fixedPlaylistName = pref.librarySourceFixedPlaylistName = custom ? themeLibrary.librarySourceFixedPlaylistName : ''; - } + /** + * Sets player controls settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setPlayerControls() { + // * Special cases + if (this.saveCfg) { + grCfg.themeControls.waveformBarPrepaintFront = grSet.waveformBarPrepaintFront === Infinity ? 'Infinity' : grSet.waveformBarPrepaintFront; + } else { + grSet.waveformBarPrepaintFront = this.loadCfg ? grCfg.themeControls.waveformBarPrepaintFront : false; // ! Do not use Infinity here, set to false has same effect + } - // * Biography - if (save) { - themeBiography.biographyLayout = pref.biographyLayout; - themeBiography.biographyLayoutFullPreset = pref.biographyLayoutFullPreset; - themeBiography.style = pptBio.style; - themeBiography.filmStripPos = pptBio.filmStripPos; - themeBiography.filmStripOverlay = pptBio.filmStripOverlay; - themeBiography.biographyTheme = pref.biographyTheme; - themeBiography.biographyDisplay = pref.biographyDisplay; - themeBiography.showFilmStrip = pptBio.showFilmStrip; - themeBiography.imgSeekerShow = pptBio.imgSeekerShow; - themeBiography.heading = pptBio.heading; - themeBiography.summaryShow = pptBio.summaryShow; - themeBiography.summaryCompact = pptBio.summaryCompact; - themeBiography.artistView = pptBio.artistView; - themeBiography.focus = pptBio.focus; - themeBiography.lockBio = pptBio.lockBio; - themeBiography.sourceAll = pptBio.sourceAll; - themeBiography.classicalMusicMode = pptBio.classicalMusicMode; - themeBiography.cycPhotoLocation = pptBio.cycPhotoLocation; - themeBiography.covType = pptBio.covType; - themeBiography.loadCovAllFb = pptBio.loadCovAllFb; - themeBiography.loadCovFolder = pptBio.loadCovFolder; - themeBiography.artStyleDual = pptBio.artStyleDual; - themeBiography.artReflDual = pptBio.artReflDual; - themeBiography.artShadowDual = pptBio.artShadowDual; - themeBiography.covStyleDual = pptBio.covStyleDual; - themeBiography.covReflDual = pptBio.covReflDual; - themeBiography.covShadowDual = pptBio.covShadowDual; - themeBiography.artStyleImgOnly = pptBio.artStyleImgOnly; - themeBiography.artReflImgOnly = pptBio.artReflImgOnly; - themeBiography.artShadowImgOnly = pptBio.artShadowImgOnly; - themeBiography.covStyleImgOnly = pptBio.covStyleImgOnly; - themeBiography.covReflImgOnly = pptBio.covReflImgOnly; - themeBiography.covShadowImgOnly = pptBio.covShadowImgOnly; - themeBiography.filmPhotoStyle = pptBio.filmPhotoStyle; - themeBiography.filmCoverStyle = pptBio.filmCoverStyle; - themeBiography.photoNum = cfg.photoNum; - themeBiography.cycPic = pptBio.cycPic; - themeBiography.imgSmoothTrans = pptBio.imgSmoothTrans; - themeBiography.cycTimePic = pptBio.cycTimePic; - } else { - pref.biographyLayout = custom ? themeBiography.biographyLayout : 'normal'; - pref.biographyLayoutFullPreset = custom ? themeBiography.biographyLayoutFullPreset : true; - pptBio.style = custom ? themeBiography.style : 0; - pptBio.filmStripPos = custom ? themeBiography.filmStripPos : 3; - pptBio.filmStripOverlay = custom ? themeBiography.filmStripOverlay : false; - pptBio.theme = pref.biographyTheme = custom ? themeBiography.biographyTheme : 0; - - pref.biographyDisplay = custom ? themeBiography.biographyDisplay : 'Image+text'; - setBiographyDisplay(); - - pptBio.showFilmStrip = custom ? themeBiography.showFilmStrip : false; - pptBio.imgSeekerShow = custom ? themeBiography.imgSeekerShow : 0; - pptBio.heading = custom ? themeBiography.heading : 1; - pptBio.summaryShow = custom ? themeBiography.summaryShow : true; - pptBio.summaryCompact = custom ? themeBiography.summaryCompact : true; - pptBio.artistView = custom ? themeBiography.artistView : true; - pptBio.focus = custom ? themeBiography.focus : false; - pptBio.lockBio = custom ? themeBiography.lockBio : false; - pptBio.sourceAll = custom ? themeBiography.sourceAll : false; - pptBio.classicalMusicMode = custom ? themeBiography.classicalMusicMode : false; - pptBio.cycPhotoLocation = custom ? themeBiography.cycPhotoLocation : 0; - pptBio.covType = custom ? themeBiography.covType : 0; - pptBio.loadCovAllFb = custom ? themeBiography.loadCovAllFb : false; - pptBio.loadCovFolder = custom ? themeBiography.loadCovFolder : false; - pptBio.artStyleDual = custom ? themeBiography.artStyleDual : 1; - pptBio.artReflDual = custom ? themeBiography.artReflDual : false; - pptBio.artShadowDual = custom ? themeBiography.artShadowDual : false; - pptBio.covStyleDual = custom ? themeBiography.covStyleDual : 1; - pptBio.covReflDual = custom ? themeBiography.covReflDual : false; - pptBio.covShadowDual = custom ? themeBiography.covShadowDual : false; - pptBio.artStyleImgOnly = custom ? themeBiography.artStyleImgOnly : 1; - pptBio.artReflImgOnly = custom ? themeBiography.artReflImgOnly : false; - pptBio.artShadowImgOnly = custom ? themeBiography.artShadowImgOnly : false; - pptBio.covStyleImgOnly = custom ? themeBiography.covStyleImgOnly : 1; - pptBio.covReflImgOnly = custom ? themeBiography.covReflImgOnly : false; - pptBio.covShadowImgOnly = custom ? themeBiography.covShadowImgOnly : false; - pptBio.filmPhotoStyle = custom ? themeBiography.filmPhotoStyle : 1; - pptBio.filmCoverStyle = custom ? themeBiography.filmCoverStyle : 1; - cfg.photoNum = custom ? themeBiography.photoNum : 10; - pptBio.cycPic = custom ? themeBiography.cycPic : true; - pptBio.imgSmoothTrans = custom ? themeBiography.imgSmoothTrans : false; - pptBio.cycTimePic = custom ? themeBiography.cycTimePic : 15; + this._setSetting(grSet, 'showPanelDetails_default', grCfg.themeControls, 'showPanelDetails_default', true); + this._setSetting(grSet, 'showPanelDetails_artwork', grCfg.themeControls, 'showPanelDetails_artwork', true); + this._setSetting(grSet, 'showPanelLibrary_default', grCfg.themeControls, 'showPanelLibrary_default', true); + this._setSetting(grSet, 'showPanelLibrary_artwork', grCfg.themeControls, 'showPanelLibrary_artwork', true); + this._setSetting(grSet, 'showPanelBiography_default', grCfg.themeControls, 'showPanelBiography_default', true); + this._setSetting(grSet, 'showPanelBiography_artwork', grCfg.themeControls, 'showPanelBiography_artwork', true); + this._setSetting(grSet, 'showPanelLyrics_default', grCfg.themeControls, 'showPanelLyrics_default', true); + this._setSetting(grSet, 'showPanelLyrics_artwork', grCfg.themeControls, 'showPanelLyrics_artwork', true); + this._setSetting(grSet, 'showPanelRating_default', grCfg.themeControls, 'showPanelRating_default', true); + this._setSetting(grSet, 'showPanelRating_artwork', grCfg.themeControls, 'showPanelRating_artwork', true); + this._setSetting(grSet, 'topMenuAlignment', grCfg.themeControls, 'topMenuAlignment', 'center'); + this._setSetting(grSet, 'topMenuCompact', grCfg.themeControls, 'topMenuCompact', true); + this._setSetting(grSet, 'albumArtAlign', grCfg.themeControls, 'albumArtAlign', 'right'); + this._setSetting(grSet, 'albumArtBg', grCfg.themeControls, 'albumArtBg', 'left'); + this._setSetting(grSet, 'albumArtScale', grCfg.themeControls, 'albumArtScale', 'cropped'); + this._setSetting(grSet, 'albumArtAspectRatioLimit', grCfg.themeControls, 'albumArtAspectRatioLimit', true); + this._setSetting(grSet, 'cycleArt', grCfg.themeControls, 'cycleArt', false); + this._setSetting(grSet, 'cycleArtMWheel', grCfg.themeControls, 'cycleArtMWheel', true); + this._setSetting(grSet, 'loadEmbeddedAlbumArtFirst', grCfg.themeControls, 'loadEmbeddedAlbumArtFirst', false); + this._setSetting(grSet, 'showHiResAudioBadge', grCfg.themeControls, 'showHiResAudioBadge', false); + this._setSetting(grSet, 'hiResAudioBadgeRound', grCfg.themeControls, 'hiResAudioBadgeRound', false); + this._setSetting(grSet, 'hiResAudioBadgeSize', grCfg.themeControls, 'hiResAudioBadgeSize', 'normal'); + this._setSetting(grSet, 'hiResAudioBadgePos', grCfg.themeControls, 'hiResAudioBadgePos', 'bottomright'); + this._setSetting(grSet, 'showPause', grCfg.themeControls, 'showPause', true); + this._setSetting(grSet, 'jumpSearchIncludeLibrary', grCfg.themeControls, 'jumpSearchIncludeLibrary', true); + this._setSetting(grSet, 'jumpSearchIncludePlaylist', grCfg.themeControls, 'jumpSearchIncludePlaylist', true); + this._setSetting(grSet, 'jumpSearchComposerOnly', grCfg.themeControls, 'jumpSearchComposerOnly', false); + this._setSetting(grSet, 'playlistWheelScrollSteps', grCfg.themeControls, 'playlistWheelScrollSteps', 3); + this._setSetting(grSet, 'playlistWheelScrollDuration', grCfg.themeControls, 'playlistWheelScrollDuration', 300); + this._setSetting(grSet, 'playlistAutoScrollNowPlaying', grCfg.themeControls, 'playlistAutoScrollNowPlaying', false); + this._setSetting(grSet, 'playlistAutoHideScrollbar', grCfg.themeControls, 'playlistAutoHideScrollbar', true); + this._setSetting(grSet, 'playlistSmoothScrolling', grCfg.themeControls, 'playlistSmoothScrolling', true); + this._setSetting(libSet, 'scrollStep', grCfg.themeControls, 'scrollStepLib', 3); + this._setSetting(libSet, 'durationScroll', grCfg.themeControls, 'durationScrollLib', 500); + this._setSetting(grSet, 'libraryAutoScrollNowPlaying', grCfg.themeControls, 'libraryAutoScrollNowPlaying', false); + this._setSetting(grSet, 'libraryAutoHideScrollbar', grCfg.themeControls, 'libraryAutoHideScrollbar', true); + this._setSetting(libSet, 'sbarShow', false, false, grSet.libraryAutoHideScrollbar ? 1 : 2); + this._setSetting(libSet, 'smooth', grCfg.themeControls, 'smoothLib', true); + this._setSetting(bioSet, 'scrollStep', grCfg.themeControls, 'scrollStepBio', 3); + this._setSetting(bioSet, 'durationScroll', grCfg.themeControls, 'durationScrollBio', 500); + this._setSetting(grSet, 'biographyAutoHideScrollbar', grCfg.themeControls, 'biographyAutoHideScrollbar', true); + this._setSetting(bioSet, 'sbarShow', false, false, grSet.biographyAutoHideScrollbar ? 1 : 2); + this._setSetting(bioSet, 'smooth', grCfg.themeControls, 'smoothBio', true); + this._setSetting(grSet, 'showTooltipTruncated', grCfg.themeControls, 'showTooltipTruncated', true); + this._setSetting(grSet, 'showTooltipTimeline', grCfg.themeControls, 'showTooltipTimeline', true); + this._setSetting(grSet, 'showTooltipVolume', grCfg.themeControls, 'showTooltipVolume', false); + this._setSetting(grSet, 'showTooltipVolumeInPercent', grCfg.themeControls, 'showTooltipVolumeInPercent', false); + this._setSetting(grSet, 'showTooltipMain', grCfg.themeControls, 'showTooltipMain', false); + this._setSetting(grSet, 'showTooltipLibrary', grCfg.themeControls, 'showTooltipLibrary', false); + this._setSetting(grSet, 'showTooltipBiography', grCfg.themeControls, 'showTooltipBiography', false); + this._setSetting(grSet, 'showStyledTooltips', grCfg.themeControls, 'showStyledTooltips', true); + this._setSetting(grSet, 'panelWidthAuto', grCfg.themeControls, 'panelWidthAuto', false); + this._setSetting(grSet, 'showPanelOnStartup', grCfg.themeControls, 'showPanelOnStartup', 'playlist'); + this._setSetting(grSet, 'showPreloaderLogo', grCfg.themeControls, 'showPreloaderLogo', true); + this._setSetting(grSet, 'returnToHomeOnPlaybackStop', grCfg.themeControls, 'returnToHomeOnPlaybackStop', true); + this._setSetting(grSet, 'addTracksPlaylistSwitch', grCfg.themeControls, 'addTracksPlaylistSwitch', false); + this._setSetting(grSet, 'hideMiddlePanelShadow', grCfg.themeControls, 'hideMiddlePanelShadow', false); + this._setSetting(grSet, 'lockPlayerSize', grCfg.themeControls, 'lockPlayerSize', false); + this._setSetting(grSet, 'transportButtonSize_default', grCfg.themeControls, 'transportButtonSize_default', 32); + this._setSetting(grSet, 'transportButtonSize_artwork', grCfg.themeControls, 'transportButtonSize_artwork', 32); + this._setSetting(grSet, 'transportButtonSize_compact', grCfg.themeControls, 'transportButtonSize_compact', 32); + this._setSetting(grSet, 'transportButtonSpacing_default', grCfg.themeControls, 'transportButtonSpacing_default', 5); + this._setSetting(grSet, 'transportButtonSpacing_artwork', grCfg.themeControls, 'transportButtonSpacing_artwork', 5); + this._setSetting(grSet, 'transportButtonSpacing_compact', grCfg.themeControls, 'transportButtonSpacing_compact', 5); + this._setSetting(grSet, 'showTransportControls_default', grCfg.themeControls, 'showTransportControls_default', true); + this._setSetting(grSet, 'showTransportControls_artwork', grCfg.themeControls, 'showTransportControls_artwork', true); + this._setSetting(grSet, 'showTransportControls_compact', grCfg.themeControls, 'showTransportControls_compact', true); + this._setSetting(grSet, 'showPlaybackOrderBtn_default', grCfg.themeControls, 'showPlaybackOrderBtn_default', true); + this._setSetting(grSet, 'showPlaybackOrderBtn_artwork', grCfg.themeControls, 'showPlaybackOrderBtn_artwork', true); + this._setSetting(grSet, 'showPlaybackOrderBtn_compact', grCfg.themeControls, 'showPlaybackOrderBtn_compact', true); + this._setSetting(grSet, 'showReloadBtn_default', grCfg.themeControls, 'showReloadBtn_default', false); + this._setSetting(grSet, 'showReloadBtn_artwork', grCfg.themeControls, 'showReloadBtn_artwork', false); + this._setSetting(grSet, 'showReloadBtn_compact', grCfg.themeControls, 'showReloadBtn_compact', false); + this._setSetting(grSet, 'showAddTracksBtn_default', grCfg.themeControls, 'showAddTracksBtn_default', false); + this._setSetting(grSet, 'showAddTracksBtn_artwork', grCfg.themeControls, 'showAddTracksBtn_artwork', false); + this._setSetting(grSet, 'showAddTracksBtn_compact', grCfg.themeControls, 'showAddTracksBtn_compact', false); + this._setSetting(grSet, 'showVolumeBtn_default', grCfg.themeControls, 'showVolumeBtn_default', true); + this._setSetting(grSet, 'showVolumeBtn_artwork', grCfg.themeControls, 'showVolumeBtn_artwork', true); + this._setSetting(grSet, 'showVolumeBtn_compact', grCfg.themeControls, 'showVolumeBtn_compact', true); + this._setSetting(grSet, 'autoHideVolumeBar', grCfg.themeControls, 'autoHideVolumeBar', true); + this._setSetting(grSet, 'showPlaybackTime_default', grCfg.themeControls, 'showPlaybackTime_default', true); + this._setSetting(grSet, 'showPlaybackTime_artwork', grCfg.themeControls, 'showPlaybackTime_artwork', true); + this._setSetting(grSet, 'showPlaybackTime_compact', grCfg.themeControls, 'showPlaybackTime_compact', true); + this._setSetting(grSet, 'showLowerBarArtist_default', grCfg.themeControls, 'showLowerBarArtist_default', true); + this._setSetting(grSet, 'showLowerBarArtist_artwork', grCfg.themeControls, 'showLowerBarArtist_artwork', true); + this._setSetting(grSet, 'showLowerBarArtist_compact', grCfg.themeControls, 'showLowerBarArtist_compact', true); + this._setSetting(grSet, 'showLowerBarTrackNum_default', grCfg.themeControls, 'showLowerBarTrackNum_default', true); + this._setSetting(grSet, 'showLowerBarTrackNum_artwork', grCfg.themeControls, 'showLowerBarTrackNum_artwork', true); + this._setSetting(grSet, 'showLowerBarTrackNum_compact', grCfg.themeControls, 'showLowerBarTrackNum_compact', true); + this._setSetting(grSet, 'showLowerBarTitle_default', grCfg.themeControls, 'showLowerBarTitle_default', true); + this._setSetting(grSet, 'showLowerBarTitle_artwork', grCfg.themeControls, 'showLowerBarTitle_artwork', true); + this._setSetting(grSet, 'showLowerBarTitle_compact', grCfg.themeControls, 'showLowerBarTitle_compact', true); + this._setSetting(grSet, 'showLowerBarComposer_default', grCfg.themeControls, 'showLowerBarComposer_default', false); + this._setSetting(grSet, 'showLowerBarComposer_artwork', grCfg.themeControls, 'showLowerBarComposer_artwork', false); + this._setSetting(grSet, 'showLowerBarComposer_compact', grCfg.themeControls, 'showLowerBarComposer_compact', false); + this._setSetting(grSet, 'showLowerBarArtistFlags_default', grCfg.themeControls, 'showLowerBarArtistFlags_default', true); + this._setSetting(grSet, 'showLowerBarArtistFlags_artwork', grCfg.themeControls, 'showLowerBarArtistFlags_artwork', true); + this._setSetting(grSet, 'showLowerBarArtistFlags_compact', grCfg.themeControls, 'showLowerBarArtistFlags_compact', true); + this._setSetting(grSet, 'showLowerBarVersion_default', grCfg.themeControls, 'showLowerBarVersion_default', true); + this._setSetting(grSet, 'showLowerBarVersion_artwork', grCfg.themeControls, 'showLowerBarVersion_artwork', true); + this._setSetting(grSet, 'showLowerBarVersion_compact', grCfg.themeControls, 'showLowerBarVersion_compact', true); + this._setSetting(grSet, 'showProgressBar_default', grCfg.themeControls, 'showProgressBar_default', true); + this._setSetting(grSet, 'showProgressBar_artwork', grCfg.themeControls, 'showProgressBar_artwork', true); + this._setSetting(grSet, 'showProgressBar_compact', grCfg.themeControls, 'showProgressBar_compact', true); + this._setSetting(grSet, 'showPeakmeterBar_default', grCfg.themeControls, 'showPeakmeterBar_default', true); + this._setSetting(grSet, 'showPeakmeterBar_artwork', grCfg.themeControls, 'showPeakmeterBar_artwork', true); + this._setSetting(grSet, 'showPeakmeterBar_compact', grCfg.themeControls, 'showPeakmeterBar_compact', true); + this._setSetting(grSet, 'showWaveformBar_default', grCfg.themeControls, 'showWaveformBar_default', true); + this._setSetting(grSet, 'showWaveformBar_artwork', grCfg.themeControls, 'showWaveformBar_artwork', true); + this._setSetting(grSet, 'showWaveformBar_compact', grCfg.themeControls, 'showWaveformBar_compact', true); + this._setSetting(grSet, 'addTracksPlaylist', grCfg.themeControls, 'addTracksPlaylist', 'Favorites'); + this._setSetting(grSet, 'seekbar', grCfg.themeControls, 'seekbar', 'progressbar'); + this._setSetting(grSet, 'progressBarWheelSeekSpeed', grCfg.themeControls, 'progressBarWheelSeekSpeed', 5); + this._setSetting(grSet, 'progressBarRefreshRate', grCfg.themeControls, 'progressBarRefreshRate', 'variable'); + this._setSetting(grSet, 'peakmeterBarDesign', grCfg.themeControls, 'peakmeterBarDesign', 'horizontal'); + this._setSetting(grSet, 'peakmeterBarVertSize', grCfg.themeControls, 'peakmeterBarVertSize', 20); + this._setSetting(grSet, 'peakmeterBarVertDbRange', grCfg.themeControls, 'peakmeterBarVertDbRange', 220); + this._setSetting(grSet, 'peakmeterBarOverBars', grCfg.themeControls, 'peakmeterBarOverBars', true); + this._setSetting(grSet, 'peakmeterBarOuterBars', grCfg.themeControls, 'peakmeterBarOuterBars', true); + this._setSetting(grSet, 'peakmeterBarOuterPeaks', grCfg.themeControls, 'peakmeterBarOuterPeaks', true); + this._setSetting(grSet, 'peakmeterBarMainBars', grCfg.themeControls, 'peakmeterBarMainBars', true); + this._setSetting(grSet, 'peakmeterBarMainPeaks', grCfg.themeControls, 'peakmeterBarMainPeaks', true); + this._setSetting(grSet, 'peakmeterBarMiddleBars', grCfg.themeControls, 'peakmeterBarMiddleBars', true); + this._setSetting(grSet, 'peakmeterBarProgBar', grCfg.themeControls, 'peakmeterBarProgBar', true); + this._setSetting(grSet, 'peakmeterBarGaps', grCfg.themeControls, 'peakmeterBarGaps', false); + this._setSetting(grSet, 'peakmeterBarGrid', grCfg.themeControls, 'peakmeterBarGrid', false); + this._setSetting(grSet, 'peakmeterBarInfo', grCfg.themeControls, 'peakmeterBarInfo', false); + this._setSetting(grSet, 'peakmeterBarVertPeaks', grCfg.themeControls, 'peakmeterBarVertPeaks', true); + this._setSetting(grSet, 'peakmeterBarVertBaseline', grCfg.themeControls, 'peakmeterBarVertBaseline', true); + this._setSetting(grSet, 'peakmeterBarRefreshRate', grCfg.themeControls, 'peakmeterBarRefreshRate', 80); + this._setSetting(grSet, 'waveformBarMode', grCfg.themeControls, 'waveformBarMode', 'audiowaveform'); + this._setSetting(grSet, 'waveformBarAnalysis', grCfg.themeControls, 'waveformBarAnalysis', 'rms_level'); + this._setSetting(grSet, 'waveformBarDesign', grCfg.themeControls, 'waveformBarDesign', 'halfbars'); + this._setSetting(grSet, 'waveformBarSizeWave', grCfg.themeControls, 'waveformBarSizeWave', 3); + this._setSetting(grSet, 'waveformBarSizeBars', grCfg.themeControls, 'waveformBarSizeBars', 1); + this._setSetting(grSet, 'waveformBarSizeDots', grCfg.themeControls, 'waveformBarSizeDots', 2); + this._setSetting(grSet, 'waveformBarSizeHalf', grCfg.themeControls, 'waveformBarSizeHalf', 4); + this._setSetting(grSet, 'waveformBarSizeNormalize', grCfg.themeControls, 'waveformBarSizeNormalize', false); + this._setSetting(grSet, 'waveformBarPaint', grCfg.themeControls, 'waveformBarPaint', 'partial'); + this._setSetting(grSet, 'waveformBarPrepaint', grCfg.themeControls, 'waveformBarPrepaint', true); + this._setSetting(grSet, 'waveformBarAnimate', grCfg.themeControls, 'waveformBarAnimate', true); + this._setSetting(grSet, 'waveformBarBPM', grCfg.themeControls, 'waveformBarBPM', true); + this._setSetting(grSet, 'waveformBarInvertHalfbars', grCfg.themeControls, 'waveformBarInvertHalfbars', true); + this._setSetting(grSet, 'waveformBarIndicator', grCfg.themeControls, 'waveformBarIndicator', false); + this._setSetting(grSet, 'waveformBarRefreshRate', grCfg.themeControls, 'waveformBarRefreshRate', 200); + this._setSetting(grSet, 'waveformBarRefreshRateVar', grCfg.themeControls, 'waveformBarRefreshRateVar', false); + this._setSetting(grSet, 'maximizeToFullscreen', grCfg.themeControls, 'maximizeToFullscreen', true); + this._setSetting(grSet, 'switchPlaybackTime', grCfg.themeControls, 'switchPlaybackTime', false); + this._setSetting(grSet, 'playbackOrder', grCfg.themeControls, 'playbackOrder', 'default'); } - // * Lyrics - if (save) { - themeLyrics.lyricsLayout = pref.lyricsLayout; - themeLyrics.lyricsDropShadowLevel = pref.lyricsDropShadowLevel; - themeLyrics.lyricsFadeScroll = pref.lyricsFadeScroll; - themeLyrics.lyricsLargerCurrentSync = pref.lyricsLargerCurrentSync; - themeLyrics.lyricsAlbumArt = pref.lyricsAlbumArt; - themeLyrics.lyricsRememberActiveState = pref.lyricsRememberActiveState; - themeLyrics.lyricsRememberPanelState = pref.lyricsRememberPanelState; - themeLyrics.lyricsScrollSpeed = pref.lyricsScrollSpeed; - themeLyrics.lyricsScrollRateAvg = pref.lyricsScrollRateAvg; - themeLyrics.lyricsScrollRateMax = pref.lyricsScrollRateMax; - themeLyrics.displayLyrics = pref.displayLyrics; - } else { - pref.lyricsLayout = custom ? themeLyrics.lyricsLayout : 'normal'; - pref.lyricsDropShadowLevel = custom ? themeLyrics.lyricsDropShadowLevel : 2; - pref.lyricsFadeScroll = custom ? themeLyrics.lyricsFadeScroll : true; - pref.lyricsLargerCurrentSync = custom ? themeLyrics.lyricsLargerCurrentSync : true; - pref.lyricsAlbumArt = custom ? themeLyrics.lyricsAlbumArt : true; - pref.lyricsRememberActiveState = custom ? themeLyrics.lyricsRememberActiveState : false; - pref.lyricsRememberPanelState = custom ? themeLyrics.lyricsRememberPanelState : false; - pref.lyricsScrollSpeed = custom ? themeLyrics.lyricsScrollSpeed : 'normal'; - pref.lyricsScrollRateAvg = custom ? themeLyrics.lyricsScrollRateAvg : 750; - pref.lyricsScrollRateMax = custom ? themeLyrics.lyricsScrollRateMax : 375; - pref.displayLyrics = custom ? themeLyrics.displayLyrics : false; + /** + * Sets Playlist settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setPlaylist() { + this._setSetting(grSet, 'playlistLayout', grCfg.themePlaylist, 'playlistLayout', 'normal'); + this._setSetting(grSet, 'playlistLayoutNormal', false, false, true); + this._setSetting(grSet, 'showPlaylistManager_default', grCfg.themePlaylist, 'showPlaylistManager_default', true); + this._setSetting(grSet, 'showPlaylistManager_artwork', grCfg.themePlaylist, 'showPlaylistManager_artwork', false); + this._setSetting(grSet, 'showPlaylistManager_compact', grCfg.themePlaylist, 'showPlaylistManager_compact', false); + this._setSetting(grSet, 'showPlaylistHistory', grCfg.themePlaylist, 'showPlaylistHistory', true); + this._setSetting(grSet, 'autoHidePlman', grCfg.themePlaylist, 'autoHidePlman', true); + this._setSetting(plSet, 'show_album_art', grCfg.themePlaylist, 'show_album_art', true); + this._setSetting(plSet, 'auto_album_art', grCfg.themePlaylist, 'auto_album_art', false); + this._setSetting(plSet, 'show_header', grCfg.themePlaylist, 'show_header', true); + this._setSetting(plSet, 'show_rating_header', grCfg.themePlaylist, 'show_rating_header', true); + this._setSetting(plSet, 'show_PLR_header', grCfg.themePlaylist, 'show_PLR_header', false); + this._setSetting(plSet, 'use_compact_header', grCfg.themePlaylist, 'use_compact_header', false); + this._setSetting(plSet, 'auto_collapse', grCfg.themePlaylist, 'auto_collapse', false); + this._setSetting(grSet, 'hyperlinksCtrlClick', grCfg.themePlaylist, 'hyperlinksCtrlClick', false); + this._setSetting(plSet, 'show_disc_header', grCfg.themePlaylist, 'show_disc_header', true); + this._setSetting(plSet, 'show_group_info', grCfg.themePlaylist, 'show_group_info', true); + this._setSetting(grSet, 'showWeblinks', grCfg.themePlaylist, 'showWeblinks', true); + this._setSetting(grSet, 'showPlaylistFullDate', grCfg.themePlaylist, 'showPlaylistFullDate', false); + this._setSetting(plSet, 'show_row_stripes', grCfg.themePlaylist, 'show_row_stripes', false); + this._setSetting(plSet, 'show_playcount', grCfg.themePlaylist, 'show_playcount', true); + this._setSetting(plSet, 'show_queue_position', grCfg.themePlaylist, 'show_queue_position', true); + this._setSetting(plSet, 'show_rating', grCfg.themePlaylist, 'show_rating', true); + this._setSetting(plSet, 'use_rating_from_tags', grCfg.themePlaylist, 'use_rating_from_tags', false); + this._setSetting(plSet, 'show_PLR', grCfg.themePlaylist, 'show_PLR', false); + this._setSetting(grSet, 'showPlaylistRatingGrid', grCfg.themePlaylist, 'showPlaylistRatingGrid', false); + this._setSetting(grSet, 'showPlaylistTrackNumbers', grCfg.themePlaylist, 'showPlaylistTrackNumbers', true); + this._setSetting(grSet, 'showPlaylistIndexNumbers', grCfg.themePlaylist, 'showPlaylistIndexNumbers', false); + this._setSetting(grSet, 'showDifferentArtist', grCfg.themePlaylist, 'showDifferentArtist', false); + this._setSetting(grSet, 'showArtistPlaylistRows', grCfg.themePlaylist, 'showArtistPlaylistRows', false); + this._setSetting(grSet, 'showAlbumPlaylistRows', grCfg.themePlaylist, 'showAlbumPlaylistRows', false); + this._setSetting(grSet, 'playlistTimeRemaining', grCfg.themePlaylist, 'playlistTimeRemaining', false); + this._setSetting(grSet, 'showVinylNums', grCfg.themePlaylist, 'showVinylNums', true); + this._setSetting(grSet, 'lastFmScrobblesFallback', grCfg.themePlaylist, 'lastFmScrobblesFallback', true); + this._setSetting(grSet, 'playlistRowHover', grCfg.themePlaylist, 'playlistRowHover', true); + this._setSetting(grSet, 'playlistSortOrderAuto', grCfg.themePlaylist, 'playlistSortOrderAuto', false); + this._setSetting(grSet, 'playlistSortOrder', grCfg.themePlaylist, 'playlistSortOrder', ''); + this._setSetting(grSet, 'playlistSortOrderDirection', grCfg.themePlaylist, 'playlistSortOrderDirection', '_asc'); + this._setSetting(plSet, 'playlist_stats_include_artist', grCfg.themePlaylist, 'playlist_stats_include_artist', true); + this._setSetting(plSet, 'playlist_stats_include_album', grCfg.themePlaylist, 'playlist_stats_include_album', true); + this._setSetting(plSet, 'playlist_stats_include_track', grCfg.themePlaylist, 'playlist_stats_include_track', true); + this._setSetting(plSet, 'playlist_stats_include_year', grCfg.themePlaylist, 'playlist_stats_include_year', false); + this._setSetting(plSet, 'playlist_stats_include_genre', grCfg.themePlaylist, 'playlist_stats_include_genre', false); + this._setSetting(plSet, 'playlist_stats_include_label', grCfg.themePlaylist, 'playlist_stats_include_label', false); + this._setSetting(plSet, 'playlist_stats_include_country', grCfg.themePlaylist, 'playlist_stats_include_country', false); + this._setSetting(plSet, 'playlist_stats_include_stats', grCfg.themePlaylist, 'playlist_stats_include_stats', true); + this._setSetting(plSet, 'playlist_stats_sort_by', grCfg.themePlaylist, 'playlist_stats_sort_by', ''); + this._setSetting(plSet, 'playlist_stats_sort_direction', grCfg.themePlaylist, 'playlist_stats_sort_direction', '_dsc'); + + // ! System settings not configurable + plSet.list_left_pad = 0; + plSet.list_top_pad = 0; + plSet.list_right_pad = 0; + plSet.list_bottom_pad = 15; + plSet.show_scrollbar = false; + plSet.scrollbar_right_pad = 0; + plSet.scrollbar_top_pad = 0; + plSet.scrollbar_bottom_pad = 3; + plSet.scrollbar_w = ''; + plSet.row_h = 20; + plSet.scrollbar_pos = 0; + plSet.scrollbar_wheel_scroll_page = false; + plSet.rows_in_header = 4; + plSet.rows_in_compact_header = 3; + plSet.show_plman = true; + plSet.collapse_on_playlist_switch = false; + plSet.collapse_on_start = false; } - // * Settings - if (save) { - themeSettings.themeDayNightMode = pref.themeDayNightMode; - themeSettings.customThemeFonts = pref.customThemeFonts; - themeSettings.customPreloaderLogo = pref.customPreloaderLogo; - themeSettings.customThemeImages = pref.customThemeImages; - themeSettings.albumArtDiskCache = ppt.albumArtDiskCache; - themeSettings.albumArtPreLoad = ppt.albumArtPreLoad; - themeSettings.customLibraryDir = pref.customLibraryDir; - themeSettings.libraryAutoDelete = pref.libraryAutoDelete; - themeSettings.customBiographyDir = pref.customBiographyDir; - themeSettings.biographyAutoDelete = pref.biographyAutoDelete; - themeSettings.customLyricsDir = pref.customLyricsDir; - themeSettings.lyricsAutoDelete = pref.lyricsAutoDelete; - themeSettings.customWaveformBarDir = pref.customWaveformBarDir; - themeSettings.waveformBarAutoDelete = pref.waveformBarAutoDelete; - themeSettings.themePerformance = pref.themePerformance; - themeSettings.devTools = pref.devTools; - themeSettings.disableRightClick = pref.disableRightClick; - } else { - pref.themeDayNightMode = custom ? themeSettings.themeDayNightMode : false; - pref.customThemeFonts = custom ? themeSettings.customThemeFonts : false; - pref.customPreloaderLogo = custom ? themeSettings.customPreloaderLogo : false; - pref.customThemeImages = custom ? themeSettings.customThemeImages : false; - ppt.albumArtDiskCache = custom ? themeSettings.albumArtDiskCache : true; - ppt.albumArtPreLoad = custom ? themeSettings.albumArtPreLoad : false; - pref.customLibraryDir = custom ? themeSettings.customLibraryDir : false; - pref.libraryAutoDelete = custom ? themeSettings.libraryAutoDelete : false; - pref.customBiographyDir = custom ? themeSettings.customBiographyDir : false; - pref.biographyAutoDelete = custom ? themeSettings.biographyAutoDelete : false; - pref.customLyricsDir = custom ? themeSettings.customLyricsDir : false; - pref.lyricsAutoDelete = custom ? themeSettings.lyricsAutoDelete : false; - pref.customWaveformBarDir = custom ? themeSettings.customWaveformBarDir : false; - pref.waveformBarAutoDelete = custom ? themeSettings.waveformBarAutoDelete : false; - pref.themePerformance = custom ? themeSettings.themePerformance : 'balanced'; - pref.devTools = custom ? themeSettings.devTools : false; - pref.disableRightClick = custom ? themeSettings.disableRightClick : true; + /** + * Sets Details settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setDetails() { + this._setSetting(grSet, 'showDiscArtStub', grCfg.themeDetails, 'showDiscArtStub', true); + this._setSetting(grSet, 'noDiscArtStub', grCfg.themeDetails, 'noDiscArtStub', false); + this._setSetting(grSet, 'discArtStub', grCfg.themeDetails, 'discArtStub', 'cdAlbumCover'); + this._setSetting(grSet, 'displayDiscArt', grCfg.themeDetails, 'displayDiscArt', true); + this._setSetting(grSet, 'discArtOnTop', grCfg.themeDetails, 'discArtOnTop', false); + this._setSetting(grSet, 'filterDiscJpgsFromAlbumArt', grCfg.themeDetails, 'filterDiscJpgsFromAlbumArt', true); + this._setSetting(grSet, 'spinDiscArt', grCfg.themeDetails, 'spinDiscArt', false); + this._setSetting(grSet, 'spinDiscArtImageCount', grCfg.themeDetails, 'spinDiscArtImageCount', 72); + this._setSetting(grSet, 'spinDiscArtRedrawInterval', grCfg.themeDetails, 'spinDiscArtRedrawInterval', 75); + this._setSetting(grSet, 'rotateDiscArt', grCfg.themeDetails, 'rotateDiscArt', true); + this._setSetting(grSet, 'rotationAmt', grCfg.themeDetails, 'rotationAmt', 3); + this._setSetting(grSet, 'artRotateDelay', grCfg.themeDetails, 'artRotateDelay', 30); + this._setSetting(grSet, 'discArtDisplayAmount', grCfg.themeDetails, 'discArtDisplayAmount', 0.5); + this._setSetting(grSet, 'detailsAlbumArtOpacity', grCfg.themeDetails, 'detailsAlbumArtOpacity', 255); + this._setSetting(grSet, 'detailsAlbumArtDiscAreaOpacity', grCfg.themeDetails, 'detailsAlbumArtDiscAreaOpacity', 255); + this._setSetting(grSet, 'showGridArtist_default', grCfg.themeDetails, 'showGridArtist_default', false); + this._setSetting(grSet, 'showGridArtist_artwork', grCfg.themeDetails, 'showGridArtist_artwork', false); + this._setSetting(grSet, 'showGridTrackNum_default', grCfg.themeDetails, 'showGridTrackNum_default', false); + this._setSetting(grSet, 'showGridTrackNum_artwork', grCfg.themeDetails, 'showGridTrackNum_artwork', false); + this._setSetting(grSet, 'showGridTitle_default', grCfg.themeDetails, 'showGridTitle_default', false); + this._setSetting(grSet, 'showGridTitle_artwork', grCfg.themeDetails, 'showGridTitle_artwork', false); + this._setSetting(grSet, 'showGridPlayingPlaylist', grCfg.themeDetails, 'showGridPlayingPlaylist', false); + this._setSetting(grSet, 'showGridTimeline_default', grCfg.themeDetails, 'showGridTimeline_default', true); + this._setSetting(grSet, 'showGridTimeline_artwork', grCfg.themeDetails, 'showGridTimeline_artwork', true); + this._setSetting(grSet, 'showGridArtistFlags_default', grCfg.themeDetails, 'showGridArtistFlags_default', true); + this._setSetting(grSet, 'showGridArtistFlags_artwork', grCfg.themeDetails, 'showGridArtistFlags_artwork', true); + this._setSetting(grSet, 'showGridReleaseFlags_default', grCfg.themeDetails, 'showGridReleaseFlags_default', 'logo'); + this._setSetting(grSet, 'showGridReleaseFlags_artwork', grCfg.themeDetails, 'showGridReleaseFlags_artwork', 'logo'); + this._setSetting(grSet, 'showGridCodecLogo_default', grCfg.themeDetails, 'showGridCodecLogo_default', 'logo'); + this._setSetting(grSet, 'showGridCodecLogo_artwork', grCfg.themeDetails, 'showGridCodecLogo_artwork', 'logo'); + this._setSetting(grSet, 'showGridChannelLogo_default', grCfg.themeDetails, 'showGridChannelLogo_default', 'logo'); + this._setSetting(grSet, 'showGridChannelLogo_artwork', grCfg.themeDetails, 'showGridChannelLogo_artwork', 'logo'); + this._setSetting(grSet, 'autoHideGridMetadata', grCfg.themeDetails, 'autoHideGridMetadata', true); + this._setSetting(grSet, 'noDiscArtBg', grCfg.themeDetails, 'noDiscArtBg', true); + this._setSetting(grSet, 'labelArtOnBg', grCfg.themeDetails, 'labelArtOnBg', false); } - // * Not in the config nor in the Options menu - pref.savedAlbumArtShow = ppt.albumArtShow; - ppt.albumArtDropShadow = pref.libraryThumbnailBorder === 'shadow'; - pptBio.largerSyncLyricLine = pref.lyricsLargerCurrentSync; - - // * Set variable blendedImg when switching from default settings to config settings that has style Blend or Blend2 activated - if ((pref.styleBlend || pref.styleBlend2 || pref.styleProgressBarFill === 'blend') && albumArt) setStyleBlend(); - - // * Reinitialize theme presets when user has reset style settings by clicking on "Default" and reloading the config file - initThemePresetState(); - - // * Reinitialize the saved player size - await display.initPlayerSize(); - - libraryCanReload = true; -} - + /** + * Sets Library settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. + */ + setLibrary() { + this._setSetting(grSet, 'libraryDesign', grCfg.themeLibrary, 'libraryDesign', 'reborn'); + if (!this.saveCfg) grm.ui.setLibraryDesign(); + + // * Special cases + if (this.saveCfg) { + grCfg.themeLibrary.libraryLayout = grSet.libraryLayout; + grCfg.themeLibrary.libraryTheme = grSet.libraryTheme; + grCfg.themeLibrary.librarySource = libSet.libSource; + grCfg.themeLibrary.librarySourceFixedPlaylist = libSet.fixedPlaylist; + grCfg.themeLibrary.librarySourceFixedPlaylistName = libSet.fixedPlaylistName; + } else { + grSet.libraryLayout = grSet.libraryDesign === 'flowMode' ? 'full' : this.loadCfg ? grCfg.themeLibrary.libraryLayout : 'normal'; + libSet.theme = grSet.libraryTheme = this.loadCfg ? grCfg.themeLibrary.libraryTheme : 0; + grSet.libraryThumbnailSizeSaved = libSet.thumbNailSize = grSet.libraryThumbnailSize = this.loadCfg ? grCfg.themeLibrary.libraryThumbnailSize : 'auto'; + libSet.libSource = grSet.librarySource = this.loadCfg ? grCfg.themeLibrary.librarySource : 1; + libSet.fixedPlaylist = grSet.librarySourceFixedPlaylist = this.loadCfg ? grCfg.themeLibrary.librarySourceFixedPlaylist : false; + libSet.fixedPlaylistName = grSet.librarySourceFixedPlaylistName = this.loadCfg ? grCfg.themeLibrary.librarySourceFixedPlaylistName : ''; + } -/** - * Loads theme performance presets with various theme settings that affect overall performance. - * The 'balanced' preset settings will be also applied in 'highQuality' and 'highestQuality'. - * Used in Options > Settings > Theme performance. - * @param {string} preset The theme performance preset to load. - */ -function setThemePerformance(preset) { - switch (preset) { - case 'balanced': // Default - pref.playerSize = 'small'; - display.autoDetectRes(); - pref.styleDefault = true; - pref.playlistAutoScrollNowPlaying = false; - pref.playlistSmoothScrolling = true; - pref.libraryAutoScrollNowPlaying = false; - ppt.smooth = true; - pptBio.smooth = true; - pref.showStyledTooltips = true; - pref.showPreloaderLogo = true; - pref.showHiResAudioBadge = false; - pref.showPause = true; - pref.seekbar = 'progressbar'; - pref.progressBarRefreshRate = 'variable'; - pref.peakmeterBarRefreshRate = 80; - pref.waveformBarPaint = 'partial'; - pref.waveformBarPrepaint = true; - pref.waveformBarPrepaintFront = Infinity; - pref.waveformBarAnimate = true; - pref.waveformBarBPM = true; - pref.waveformBarRefreshRate = 200; - pref.playlistLayout = 'normal'; - g_properties.show_album_art = true; - pref.playlistTimeRemaining = false; - pref.playlistRowHover = true; - pref.showDiscArtStub = true; - pref.noDiscArtStub = false; - pref.discArtStub = 'cdAlbumCover'; - pref.displayDiscArt = true; - pref.spinDiscArt = false; - pref.spinDiscArtImageCount = 72; - pref.spinDiscArtRedrawInterval = 75; - clearInterval(discArtRotationTimer); - discArtArray = []; - pref.detailsAlbumArtOpacity = 255; - pref.detailsAlbumArtDiscAreaOpacity = 255; - pref.showGridTimeline_default = true; - pref.showGridTimeline_artwork = true; - pref.libraryLayout = 'normal'; - pref.libraryDesign = 'reborn'; - pref.libraryTheme = 0; - ppt.albumArtShow = false; - pref.libraryRowHover = true; - pref.biographyLayout = 'normal'; - pref.biographyTheme = 0; - pptBio.showFilmStrip = false; - cfg.photoNum = 10; - ppt.albumArtDiskCache = true; - ppt.albumArtPreLoad = false; - pref.libraryAutoDelete = false; - pref.biographyAutoDelete = false; - pref.lyricsAutoDelete = false; - pptBio.focusLoadRate = 1000; - pptBio.focusLoadImmediate = false; - pref.lyricsDropShadowLevel = 2; - pref.lyricsFadeScroll = true; - pref.lyricsAlbumArt = true; - pref.lyricsRememberActiveState = false; - pref.lyricsScrollSpeed = 'normal'; - pref.lyricsScrollRateAvg = 750; - pref.lyricsScrollRateMax = 375; - break; - - case 'lowestQuality': - pref.playerSize = 'small'; - pref.playerSize_HD_small = true; - display.playerSize_HD_small(); - pref.styleDefault = true; - pref.displayRes = 'HD'; - pref.playlistAutoScrollNowPlaying = false; - pref.playlistSmoothScrolling = false; - pref.libraryAutoScrollNowPlaying = false; - ppt.smooth = false; - pptBio.smooth = false; - pref.showStyledTooltips = false; - pref.showPreloaderLogo = false; - pref.showHiResAudioBadge = false; - pref.showPause = false; - pref.seekbar = 'progressbar'; - pref.progressBarRefreshRate = 1000; - pref.peakmeterBarRefreshRate = 200; - pref.waveformBarPaint = 'full'; - pref.waveformBarPrepaint = false; - pref.waveformBarPrepaintFront = 2; - pref.waveformBarAnimate = false; - pref.waveformBarBPM = false; - pref.waveformBarRefreshRate = 1000; - pref.playlistLayout = 'normal'; - g_properties.show_album_art = false; - pref.playlistTimeRemaining = false; - pref.playlistRowHover = false; - pref.showDiscArtStub = false; - pref.noDiscArtStub = true; - pref.displayDiscArt = false; - pref.spinDiscArt = false; - pref.spinDiscArtImageCount = 36; - pref.spinDiscArtRedrawInterval = 250; - pref.showGridTimeline_default = false; - pref.showGridTimeline_artwork = false; - pref.libraryLayout = 'normal'; - pref.libraryDesign = 'reborn'; - pref.libraryTheme = 0; - ppt.albumArtShow = false; - pref.libraryRowHover = false; - pref.biographyLayout = 'normal'; - pref.biographyTheme = 0; - pptBio.showFilmStrip = false; - cfg.photoNum = 1; - ppt.albumArtDiskCache = true; - ppt.albumArtPreLoad = false; - pptBio.focusLoadRate = 3000; - pref.lyricsDropShadowLevel = 0; - pref.lyricsFadeScroll = false; - pref.lyricsAlbumArt = false; - pref.lyricsRememberActiveState = false; - pref.lyricsScrollSpeed = 'fastest'; - pref.lyricsScrollRateAvg = 300; - pref.lyricsScrollRateMax = 150; - break; - - case 'lowQuality': - pref.playerSize = 'small'; - pref.styleDefault = true; - pref.displayRes = 'HD'; - pref.showStyledTooltips = false; - pref.seekbar = 'progressbar'; - pref.progressBarRefreshRate = 500; - pref.peakmeterBarRefreshRate = 120; - pref.waveformBarPaint = 'full'; - pref.waveformBarPrepaint = false; - pref.waveformBarPrepaintFront = 2; - pref.waveformBarAnimate = false; - pref.waveformBarBPM = false; - pref.waveformBarRefreshRate = 500; - pref.playlistTimeRemaining = false; - pref.showDiscArtStub = false; - pref.noDiscArtStub = true; - pref.displayDiscArt = false; - pref.spinDiscArt = false; - pref.spinDiscArtImageCount = 45; - pref.spinDiscArtRedrawInterval = 125; - pref.libraryTheme = 0; - ppt.albumArtShow = false; - pref.biographyTheme = 0; - pptBio.showFilmStrip = false; - cfg.photoNum = 5; - ppt.albumArtDiskCache = true; - ppt.albumArtPreLoad = false; - pptBio.focusLoadRate = 2000; - pref.lyricsDropShadowLevel = 0; - pref.lyricsScrollSpeed = 'fast'; - pref.lyricsScrollRateAvg = 500; - pref.lyricsScrollRateMax = 250; - break; - - case 'highQuality': - pref.playerSize = 'normal'; - pref.progressBarRefreshRate = 100; - pref.peakmeterBarRefreshRate = 60; - pref.waveformBarPaint = 'partial'; - pref.waveformBarPrepaint = true; - pref.waveformBarPrepaintFront = Infinity; - pref.waveformBarRefreshRate = 100; - pref.waveformBarRefreshRateVar = false; - pref.spinDiscArt = true; - pref.spinDiscArtImageCount = 120; - pref.spinDiscArtRedrawInterval = 40; - setDiscArtRotationTimer(); - pref.libraryLayout = 'full'; - ppt.albumArtShow = true; - pref.biographyLayout = 'full'; - cfg.photoNum = 15; - ppt.albumArtDiskCache = true; - ppt.albumArtPreLoad = true; - pptBio.focusLoadRate = 750; - pref.lyricsScrollSpeed = 'slow'; - pref.lyricsScrollRateAvg = 1000; - pref.lyricsScrollRateMax = 500; - break; - - case 'highestQuality': - pref.playerSize = 'large'; - pref.progressBarRefreshRate = 30; - pref.peakmeterBarRefreshRate = 30; - pref.waveformBarPaint = 'partial'; - pref.waveformBarPrepaint = true; - pref.waveformBarPrepaintFront = Infinity; - pref.waveformBarRefreshRate = 30; - pref.waveformBarRefreshRateVar = false; - pref.spinDiscArt = true; - pref.spinDiscArtImageCount = 180; - pref.spinDiscArtRedrawInterval = 10; - setDiscArtRotationTimer(); - pref.detailsAlbumArtDiscAreaOpacity = 178; - pref.libraryLayout = 'full'; - ppt.albumArtShow = true; - pref.biographyLayout = 'full'; - cfg.photoNum = 20; - ppt.albumArtDiskCache = true; - ppt.albumArtPreLoad = true; - pptBio.focusLoadRate = 500; - pref.lyricsScrollSpeed = 'slowest'; - pref.lyricsScrollRateAvg = 1500; - pref.lyricsScrollRateMax = 725; - break; + this._setSetting(grSet, 'libraryLayoutFullPreset', grCfg.themeLibrary, 'libraryLayoutFullPreset', true); + this._setSetting(grSet, 'libraryLayoutSplitPreset', grCfg.themeLibrary, 'libraryLayoutSplitPreset', true); + this._setSetting(grSet, 'libraryLayoutSplitPreset2', grCfg.themeLibrary, 'libraryLayoutSplitPreset2', false); + this._setSetting(grSet, 'libraryLayoutSplitPreset3', grCfg.themeLibrary, 'libraryLayoutSplitPreset3', false); + this._setSetting(grSet, 'libraryLayoutSplitPreset4', grCfg.themeLibrary, 'libraryLayoutSplitPreset4', false); + this._setSetting(grSet, 'libraryThumbnailBorder', grCfg.themeLibrary, 'libraryThumbnailBorder', 'border'); + this._setSetting(libSet, 'albumArtShow', grCfg.themeLibrary, 'albumArtShow', false); + this._setSetting(libSet, 'itemOverlayType', grCfg.themeLibrary, 'itemOverlayType', 0); + this._setSetting(libSet, 'albumArtLetter', grCfg.themeLibrary, 'albumArtLetter', true); + this._setSetting(libSet, 'albumArtLetterNo', grCfg.themeLibrary, 'albumArtLetterNo', 1); + this._setSetting(libSet, 'artId', grCfg.themeLibrary, 'artId', 0); + this._setSetting(libSet, 'albumArtGrpLevel', grCfg.themeLibrary, 'albumArtGrpLevel', 0); + this._setSetting(libSet, 'imgStyleFront', grCfg.themeLibrary, 'imgStyleFront', 1); + this._setSetting(libSet, 'imgStyleBack', grCfg.themeLibrary, 'imgStyleBack', 1); + this._setSetting(libSet, 'imgStyleDisc', grCfg.themeLibrary, 'imgStyleDisc', 1); + this._setSetting(libSet, 'imgStyleIcon', grCfg.themeLibrary, 'imgStyleIcon', 1); + this._setSetting(libSet, 'imgStyleArtist', grCfg.themeLibrary, 'imgStyleArtist', 1); + this._setSetting(libSet, 'albumArtLabelType', grCfg.themeLibrary, 'albumArtLabelType', 1); + this._setSetting(libSet, 'albumArtFlipLabels', grCfg.themeLibrary, 'albumArtFlipLabels', false); + this._setSetting(libSet, 'actionMode', grCfg.themeLibrary, 'actionMode', 0); + this._setSetting(libSet, 'clickAction', grCfg.themeLibrary, 'clickAction', 0); + this._setSetting(libSet, 'dblClickAction', grCfg.themeLibrary, 'dblClickAction', 1); + this._setSetting(libSet, 'mbtnClickAction', grCfg.themeLibrary, 'mbtnClickAction', 1); + this._setSetting(libSet, 'altClickAction', grCfg.themeLibrary, 'altClickAction', 1); + this._setSetting(libSet, 'autoPlay', grCfg.themeLibrary, 'autoPlay', true); + this._setSetting(libSet, 'keyAction', grCfg.themeLibrary, 'keyAction', 0); + this._setSetting(libSet, 'rememberTree', grCfg.themeLibrary, 'rememberTree', false); + this._setSetting(libSet, 'artTreeSameView', grCfg.themeLibrary, 'artTreeSameView', false); + this._setSetting(libSet, 'presetLoadCurView', grCfg.themeLibrary, 'presetLoadCurView', true); + this._setSetting(libSet, 'rootNode', grCfg.themeLibrary, 'rootNode', 3); + this._setSetting(libSet, 'nodeCounts', grCfg.themeLibrary, 'nodeCounts', 1); + this._setSetting(libSet, 'countsRight', grCfg.themeLibrary, 'countsRight', true); + this._setSetting(libSet, 'autoCollapse', grCfg.themeLibrary, 'autoCollapse', false); + this._setSetting(libSet, 'itemShowStatistics', grCfg.themeLibrary, 'itemShowStatistics', 0); + this._setSetting(libSet, 'highLightNowplaying', grCfg.themeLibrary, 'highLightNowplaying', true); + this._setSetting(libSet, 'showTracks', grCfg.themeLibrary, 'showTracks', true); + this._setSetting(libSet, 'rowStripes', grCfg.themeLibrary, 'rowStripes', false); + this._setSetting(libSet, 'fullLineSelection', grCfg.themeLibrary, 'fullLineSelection', true); + this._setSetting(grSet, 'libraryRowHover', grCfg.themeLibrary, 'libraryRowHover', true); + this._setSetting(libSet, 'filterBy', grCfg.themeLibrary, 'filterBy', 0); + this._setSetting(libSet, 'sortOrder', grCfg.themeLibrary, 'sortOrder', 'default'); + this._setSetting(libSet, 'yearBeforeAlbum', grCfg.themeLibrary, 'yearBeforeAlbum', true); + this._setSetting(libSet, 'albumArtViewBy', grCfg.themeLibrary, 'albumArtViewBy', 0); + this._setSetting(libSet, 'treeViewBy', grCfg.themeLibrary, 'treeViewBy', 0); } -} - -///////////////////// -// * CONFIG FILE * // -///////////////////// -if (!config.fileExists) { - tf = config.addConfigurationObject(titleFormatSchema, defaultTitleFormatStrings, titleFormatComments); - config.addConfigurationObject(imgPathSchema, imgPathDefaults); - - theme = config.addConfigurationObject(themesSchema, themeDefaults, themesComments); - style = config.addConfigurationObject(stylesSchema, stylesDefaults, stylesComments); - preset = config.addConfigurationObject(presetSchema, presetDefaults, presetComments); - themePlayerSize = config.addConfigurationObject(themePlayerSizeSchema, themePlayerSizeDefaults, themePlayerSizeComments); - themeLayout = config.addConfigurationObject(themeLayoutSchema, themeLayoutDefaults, themeLayoutComments); - themeDisplay = config.addConfigurationObject(themeDisplaySchema, themeDisplayDefaults, themeDisplayComments); - themeBrightness = config.addConfigurationObject(themeBrightnessSchema, themeBrightnessDefaults, themeBrightnessComments); - themeFontSize = config.addConfigurationObject(themeFontSizesSchema, themeFontSizesDefaults, themeFontSizesComments); - themeControls = config.addConfigurationObject(themePlayerControlsSchema, themePlayerControlsDefaults, themePlayerControlsComments); - - themePlaylist = config.addConfigurationObject(themePlaylistSchema, themePlaylistDefaults, themePlaylistComments); - config.addConfigurationObject(themePlaylistGroupingPresetsSchema, themePlaylistGroupingPresets); - - themeDetails = config.addConfigurationObject(themeDetailsSchema, themeDetailsDefaults, themeDetailsComments); - config.addConfigurationObject(gridSchema, defaultMetadataGrid); // We don't assign an object here because these aren't key/value pairs and thus can't use the get/setters - - themeLibrary = config.addConfigurationObject(themeLibrarySchema, themeLibraryDefaults, themeLibraryComments); - themeBiography = config.addConfigurationObject(themeBiographySchema, themeBiographyDefaults, themeBiographyComments); - themeLyrics = config.addConfigurationObject(themeLyricsSchema, themeLyricsDefaults, themeLyricsComments); - config.addConfigurationObject(lyricFilenamesSchema, lyricFilenamesDefaults); - themeSettings = config.addConfigurationObject(themeSettingsSchema, themeSettingsDefaults, themeSettingsComments); - settings = config.addConfigurationObject(settingsSchema, settingsDefaults, settingsComments); - - console.log('> Writing', configPath); - config.writeConfiguration(); -} - -if (!configCustom.fileExists) { - configCustom.addConfigurationObject(customLibraryDirSchema, customLibraryDirDefaults); - configCustom.addConfigurationObject(customBiographyDirSchema, customBiographyDirDefaults); - configCustom.addConfigurationObject(customLyricsDirSchema, customLyricsDirDefaults); - configCustom.addConfigurationObject(customWaveformBarDirSchema, customWaveformBarDirDefaults); - - customFont = configCustom.addConfigurationObject(customFontsSchema, customFontsDefaults, customFontsComments); - customStylePreset = configCustom.addConfigurationObject(customStylePresetSchema, customStylePresetDefaults, customStylePresetComments); - customDiscArtStub = configCustom.addConfigurationObject(customDiscArtStubSchema, customDiscArtStubDefaults, customDiscArtStubComments); - customTheme01 = configCustom.addConfigurationObject(customTheme01Schema, customThemeDefaults, customThemeComments); - customTheme02 = configCustom.addConfigurationObject(customTheme02Schema, customThemeDefaults, customThemeComments); - customTheme03 = configCustom.addConfigurationObject(customTheme03Schema, customThemeDefaults, customThemeComments); - customTheme04 = configCustom.addConfigurationObject(customTheme04Schema, customThemeDefaults, customThemeComments); - customTheme05 = configCustom.addConfigurationObject(customTheme05Schema, customThemeDefaults, customThemeComments); - customTheme06 = configCustom.addConfigurationObject(customTheme06Schema, customThemeDefaults, customThemeComments); - customTheme07 = configCustom.addConfigurationObject(customTheme07Schema, customThemeDefaults, customThemeComments); - customTheme08 = configCustom.addConfigurationObject(customTheme08Schema, customThemeDefaults, customThemeComments); - customTheme09 = configCustom.addConfigurationObject(customTheme09Schema, customThemeDefaults, customThemeComments); - customTheme10 = configCustom.addConfigurationObject(customTheme10Schema, customThemeDefaults, customThemeComments); - - console.log('> Writing', configPathCustom); - configCustom.writeConfiguration(); -} - -if (config.fileExists) { - const prefs = config.readConfiguration(); /** - * While we've read all the values in, we still need to call addConfigurationObject to add the getters/setters - * for the objects so that the file gets automatically written when a setting is changed. + * Sets Biography settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. */ - tf = config.addConfigurationObject(titleFormatSchema, Object.assign({}, defaultTitleFormatStrings, prefs.title_format_strings), titleFormatComments); - config.addConfigurationObject(imgPathSchema, prefs.imgPaths); - - theme = config.addConfigurationObject(themesSchema, Object.assign({}, themeDefaults, prefs.theme), themesComments); - style = config.addConfigurationObject(stylesSchema, Object.assign({}, stylesDefaults, prefs.style), stylesComments); - preset = config.addConfigurationObject(presetSchema, Object.assign({}, presetDefaults, prefs.preset), presetComments); - themePlayerSize = config.addConfigurationObject(themePlayerSizeSchema, Object.assign({}, themePlayerSizeDefaults, prefs.themePlayerSize), themePlayerSizeComments); - themeLayout = config.addConfigurationObject(themeLayoutSchema, Object.assign({}, themeLayoutDefaults, prefs.themeLayout), themeLayoutComments); - themeDisplay = config.addConfigurationObject(themeDisplaySchema, Object.assign({}, themeDisplayDefaults, prefs.themeDisplay), themeDisplayComments); - themeBrightness = config.addConfigurationObject(themeBrightnessSchema, Object.assign({}, themeBrightnessDefaults, prefs.themeBrightness), themeBrightnessComments); - themeFontSize = config.addConfigurationObject(themeFontSizesSchema, Object.assign({}, themeFontSizesDefaults, prefs.themeFontSize), themeFontSizesComments); - themeControls = config.addConfigurationObject(themePlayerControlsSchema, Object.assign({}, themePlayerControlsDefaults, prefs.themeControls), themePlayerControlsComments); - - themePlaylist = config.addConfigurationObject(themePlaylistSchema, Object.assign({}, themePlaylistDefaults, prefs.themePlaylist), themePlaylistComments); - config.addConfigurationObject(themePlaylistGroupingPresetsSchema, prefs.themePlaylistGroupingPresets || themePlaylistGroupingPresets); - - themeDetails = config.addConfigurationObject(themeDetailsSchema, Object.assign({}, themeDetailsDefaults, prefs.themeDetails), themeDetailsComments); - if (prefs.metadataGrid) { - for (const entry of prefs.metadataGrid) { - // Copy comments over to existing object so they aren't lost - const gridEntryDefinition = defaultMetadataGrid.find(gridDefItem => gridDefItem.label === entry.label); - if (gridEntryDefinition && gridEntryDefinition.comment) { - entry.comment = gridEntryDefinition.comment; - } + setBiography() { + // * Special cases + if (this.saveCfg) { + grCfg.themeBiography.biographyTheme = grSet.biographyTheme; + } else { + bioSet.theme = grSet.biographyTheme = this.loadCfg ? grCfg.themeBiography.biographyTheme : 0; } - } - config.addConfigurationObject(gridSchema, prefs.metadataGrid || defaultMetadataGrid); // Can't Object.assign here to add new fields. Add new fields in the upgrade section of migrateCheck - - themeLibrary = config.addConfigurationObject(themeLibrarySchema, Object.assign({}, themeLibraryDefaults, prefs.themeLibrary), themeLibraryComments); - themeBiography = config.addConfigurationObject(themeBiographySchema, Object.assign({}, themeBiographyDefaults, prefs.themeBiography), themeBiographyComments); - themeLyrics = config.addConfigurationObject(themeLyricsSchema, Object.assign({}, themeLyricsDefaults, prefs.themeLyrics), themeLyricsComments); - config.addConfigurationObject(lyricFilenamesSchema, prefs.lyricFilenamePatterns || lyricFilenamesDefaults); - themeSettings = config.addConfigurationObject(themeSettingsSchema, Object.assign({}, themeSettingsDefaults, prefs.themeSettings), themeSettingsComments); - settings = config.addConfigurationObject(settingsSchema, Object.assign({}, settingsDefaults, prefs.settings), settingsComments); - - // Safety checks. Fix up potentially bad vals from config - settings.discArtBasename = settings.discArtBasename && settings.discArtBasename.trim().length ? settings.discArtBasename.trim() : 'cd'; - settings.artworkDisplayTime = Math.min(Math.max(settings.artworkDisplayTime, 5), 120); // Ensure min of 5sec and max of 120sec - - globals.imgPaths = prefs.imgPaths; - globals.lyricFilenamePatterns = prefs.lyricFilenamePatterns; - metadataGrid = prefs.metadataGrid; - configVersion = prefs.configVersion || prefs.version; - // When adding new objects to the config file, add them in the version check below - - // Safe guard when playlist grouping presets or metadata grid do not exist in the config - if (!prefs.themePlaylistGroupingPresets || !prefs.metadataGrid) { - const fileName = 'georgia-reborn\\configs\\georgia-reborn-config-backup.jsonc'; - fso.CopyFile(configPath, fb.ProfilePath + fileName); - config.writeConfiguration(); - } -} - -if (configCustom.fileExists) { - const prefs = configCustom.readConfiguration(); - - configCustom.addConfigurationObject(customLibraryDirSchema, prefs.customLibraryDir || customLibraryDirDefaults); - configCustom.addConfigurationObject(customBiographyDirSchema, prefs.customBiographyDir || customBiographyDirDefaults); - configCustom.addConfigurationObject(customLyricsDirSchema, prefs.customLyricsDir || customLyricsDirDefaults); - configCustom.addConfigurationObject(customWaveformBarDirSchema, prefs.customWaveformBarDir || customWaveformBarDirDefaults); - - customFont = configCustom.addConfigurationObject(customFontsSchema, Object.assign({}, customFontsDefaults, prefs.customFont), customFontsComments); - customStylePreset = configCustom.addConfigurationObject(customStylePresetSchema, Object.assign({}, customStylePresetDefaults, prefs.customStylePreset), customStylePresetComments); - customDiscArtStub = configCustom.addConfigurationObject(customDiscArtStubSchema, Object.assign({}, customDiscArtStubDefaults, prefs.customDiscArtStub), customDiscArtStubComments); - customTheme01 = configCustom.addConfigurationObject(customTheme01Schema, Object.assign({}, customThemeDefaults, prefs.customTheme01), customThemeComments); - customTheme02 = configCustom.addConfigurationObject(customTheme02Schema, Object.assign({}, customThemeDefaults, prefs.customTheme02), customThemeComments); - customTheme03 = configCustom.addConfigurationObject(customTheme03Schema, Object.assign({}, customThemeDefaults, prefs.customTheme03), customThemeComments); - customTheme04 = configCustom.addConfigurationObject(customTheme04Schema, Object.assign({}, customThemeDefaults, prefs.customTheme04), customThemeComments); - customTheme05 = configCustom.addConfigurationObject(customTheme05Schema, Object.assign({}, customThemeDefaults, prefs.customTheme05), customThemeComments); - customTheme06 = configCustom.addConfigurationObject(customTheme06Schema, Object.assign({}, customThemeDefaults, prefs.customTheme06), customThemeComments); - customTheme07 = configCustom.addConfigurationObject(customTheme07Schema, Object.assign({}, customThemeDefaults, prefs.customTheme07), customThemeComments); - customTheme08 = configCustom.addConfigurationObject(customTheme08Schema, Object.assign({}, customThemeDefaults, prefs.customTheme08), customThemeComments); - customTheme09 = configCustom.addConfigurationObject(customTheme09Schema, Object.assign({}, customThemeDefaults, prefs.customTheme09), customThemeComments); - customTheme10 = configCustom.addConfigurationObject(customTheme10Schema, Object.assign({}, customThemeDefaults, prefs.customTheme10), customThemeComments); - - globals.customLibraryDir = prefs.customLibraryDir; - globals.customBiographyDir = prefs.customBiographyDir; - globals.customLyricsDir = prefs.customLyricsDir; - globals.customWaveformBarDir = prefs.customWaveformBarDir; - customFont = prefs.customFont; - customStylePreset = prefs.customStylePreset; - customDiscArtStub = prefs.customDiscArtStub; - configVersion = prefs.configVersion || prefs.version; -} - -/////////////////////////// -// * DISC ART SETTINGS * // -/////////////////////////// -{ - let count = Number(pref.spinDiscArtImageCount); - if (Number.isNaN(count)) { // Check if NaN - count = 72; - } - pref.spinDiscArtImageCount = count; - let interval = Number(pref.spinDiscArtRedrawInterval); - if (Number.isNaN(interval)) { - interval = 200; + this._setSetting(grSet, 'biographyLayout', grCfg.themeBiography, 'biographyLayout', 'normal'); + this._setSetting(grSet, 'biographyLayoutFullPreset', grCfg.themeBiography, 'biographyLayoutFullPreset', true); + this._setSetting(bioSet, 'style', grCfg.themeBiography, 'style', 0); + this._setSetting(bioSet, 'filmStripPos', grCfg.themeBiography, 'filmStripPos', 3); + this._setSetting(bioSet, 'filmStripOverlay', grCfg.themeBiography, 'filmStripOverlay', false); + + this._setSetting(grSet, 'biographyDisplay', grCfg.themeBiography, 'biographyDisplay', 'Image+text'); + if (!this.saveCfg) grm.ui.setBiographyDisplay(); + + this._setSetting(bioSet, 'showFilmStrip', grCfg.themeBiography, 'showFilmStrip', false); + this._setSetting(bioSet, 'imgSeekerShow', grCfg.themeBiography, 'imgSeekerShow', 0); + this._setSetting(bioSet, 'heading', grCfg.themeBiography, 'heading', 1); + this._setSetting(bioSet, 'summaryShow', grCfg.themeBiography, 'summaryShow', true); + this._setSetting(bioSet, 'summaryCompact', grCfg.themeBiography, 'summaryCompact', true); + this._setSetting(bioSet, 'artistView', grCfg.themeBiography, 'artistView', true); + this._setSetting(bioSet, 'focus', grCfg.themeBiography, 'focus', false); + this._setSetting(bioSet, 'lockBio', grCfg.themeBiography, 'lockBio', false); + this._setSetting(bioSet, 'sourceAll', grCfg.themeBiography, 'sourceAll', false); + this._setSetting(bioSet, 'classicalMusicMode', grCfg.themeBiography, 'classicalMusicMode', false); + this._setSetting(bioSet, 'cycPhotoLocation', grCfg.themeBiography, 'cycPhotoLocation', 0); + this._setSetting(bioSet, 'covType', grCfg.themeBiography, 'covType', 0); + this._setSetting(bioSet, 'loadCovAllFb', grCfg.themeBiography, 'loadCovAllFb', false); + this._setSetting(bioSet, 'loadCovFolder', grCfg.themeBiography, 'loadCovFolder', false); + this._setSetting(bioSet, 'artStyleDual', grCfg.themeBiography, 'artStyleDual', 1); + this._setSetting(bioSet, 'artReflDual', grCfg.themeBiography, 'artReflDual', false); + this._setSetting(bioSet, 'artShadowDual', grCfg.themeBiography, 'artShadowDual', false); + this._setSetting(bioSet, 'covStyleDual', grCfg.themeBiography, 'covStyleDual', 1); + this._setSetting(bioSet, 'covReflDual', grCfg.themeBiography, 'covReflDual', false); + this._setSetting(bioSet, 'covShadowDual', grCfg.themeBiography, 'covShadowDual', false); + this._setSetting(bioSet, 'artStyleImgOnly', grCfg.themeBiography, 'artStyleImgOnly', 1); + this._setSetting(bioSet, 'artReflImgOnly', grCfg.themeBiography, 'artReflImgOnly', false); + this._setSetting(bioSet, 'artShadowImgOnly', grCfg.themeBiography, 'artShadowImgOnly', false); + this._setSetting(bioSet, 'covStyleImgOnly', grCfg.themeBiography, 'covStyleImgOnly', 1); + this._setSetting(bioSet, 'covReflImgOnly', grCfg.themeBiography, 'covReflImgOnly', false); + this._setSetting(bioSet, 'covShadowImgOnly', grCfg.themeBiography, 'covShadowImgOnly', false); + this._setSetting(bioSet, 'filmPhotoStyle', grCfg.themeBiography, 'filmPhotoStyle', 1); + this._setSetting(bioSet, 'filmCoverStyle', grCfg.themeBiography, 'filmCoverStyle', 1); + this._setSetting(bioCfg, 'photoNum', grCfg.themeBiography, 'photoNum', 10); + this._setSetting(bioSet, 'cycPic', grCfg.themeBiography, 'cycPic', true); + this._setSetting(bioSet, 'imgSmoothTrans', grCfg.themeBiography, 'imgSmoothTrans', false); + this._setSetting(bioSet, 'cycTimePic', grCfg.themeBiography, 'cycTimePic', 15); } - pref.spinDiscArtRedrawInterval = Math.max(10, interval); -} - -//////////////////////////// -// * THEME UPDATE CHECK * // -//////////////////////////// -/** - * Compares the latest version with the existing config version. - * @param {string} version The latest version. - * @param {string} storedVersion The config version. - */ -function migrateCheck(version, storedVersion) { /** - * Checks if settings exist in the configuration file. - * @param {object} settings The settings object from the configuration file. - * @param {...string} settingNames The names of the settings to check. - * @returns {boolean} Returns true if all specified settings exist, otherwise false. + * Sets Lyrics settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. */ - const CheckSettings = (settings, ...settingNames) => settingNames.every(settingName => Object.prototype.hasOwnProperty.call(settings, settingName)); + setLyrics() { + this._setSetting(grSet, 'lyricsLayout', grCfg.themeLyrics, 'lyricsLayout', 'normal'); + this._setSetting(grSet, 'lyricsDropShadowLevel', grCfg.themeLyrics, 'lyricsDropShadowLevel', 2); + this._setSetting(grSet, 'lyricsFadeScroll', grCfg.themeLyrics, 'lyricsFadeScroll', true); + this._setSetting(grSet, 'lyricsLargerCurrentSync', grCfg.themeLyrics, 'lyricsLargerCurrentSync', true); + this._setSetting(grSet, 'lyricsAlbumArt', grCfg.themeLyrics, 'lyricsAlbumArt', true); + this._setSetting(grSet, 'lyricsRememberPanelState', grCfg.themeLyrics, 'lyricsRememberPanelState', false); + this._setSetting(grSet, 'lyricsScrollSpeed', grCfg.themeLyrics, 'lyricsScrollSpeed', 'normal'); + this._setSetting(grSet, 'lyricsScrollRateAvg', grCfg.themeLyrics, 'lyricsScrollRateAvg', 750); + this._setSetting(grSet, 'lyricsScrollRateMax', grCfg.themeLyrics, 'lyricsScrollRateMax', 375); + this._setSetting(grSet, 'displayLyrics', grCfg.themeLyrics, 'displayLyrics', false); + } /** - * Deletes settings from the configuration file. - * @param {object} settings The settings object from the configuration file. - * @param {...string} settingNames The names of the settings to remove. + * Sets Settings settings based on the state of `this.saveCfg`, `this.loadCfg`, `this.defaultCfg`. */ - const DeleteSettings = (settings, ...settingNames) => { - for (const settingName of settingNames) { - delete settings[settingName]; - } - }; + setSettings() { + this._setSetting(grSet, 'themeDayNightMode', grCfg.themeSettings, 'themeDayNightMode', false); + this._setSetting(grSet, 'customThemeFonts', grCfg.themeSettings, 'customThemeFonts', false); + this._setSetting(grSet, 'customPreloaderLogo', grCfg.themeSettings, 'customPreloaderLogo', false); + this._setSetting(grSet, 'customThemeImages', grCfg.themeSettings, 'customThemeImages', false); + this._setSetting(libSet, 'albumArtDiskCache', grCfg.themeSettings, 'albumArtDiskCache', true); + this._setSetting(libSet, 'albumArtPreLoad', grCfg.themeSettings, 'albumArtPreLoad', false); + this._setSetting(grSet, 'customLibraryDir', grCfg.themeSettings, 'customLibraryDir', false); + this._setSetting(grSet, 'libraryAutoDelete', grCfg.themeSettings, 'libraryAutoDelete', false); + this._setSetting(grSet, 'customBiographyDir', grCfg.themeSettings, 'customBiographyDir', false); + this._setSetting(grSet, 'biographyAutoDelete', grCfg.themeSettings, 'biographyAutoDelete', false); + this._setSetting(grSet, 'customLyricsDir', grCfg.themeSettings, 'customLyricsDir', false); + this._setSetting(grSet, 'lyricsAutoDelete', grCfg.themeSettings, 'lyricsAutoDelete', false); + this._setSetting(grSet, 'customWaveformBarDir', grCfg.themeSettings, 'customWaveformBarDir', false); + this._setSetting(grSet, 'waveformBarAutoDelete', grCfg.themeSettings, 'waveformBarAutoDelete', false); + this._setSetting(grSet, 'themePerformance', grCfg.themeSettings, 'themePerformance', 'balanced'); + this._setSetting(grSet, 'devTools', grCfg.themeSettings, 'devTools', false); + this._setSetting(grSet, 'disableRightClick', grCfg.themeSettings, 'disableRightClick', true); + } /** - * Checks if specific entry exist in the metadata grid configuration. - * @param {MetadataGridEntry[]} grid Each element in the array is an object with a `label` property. - * @param {...string} labels The labels of the settings to check. - * @returns {boolean} Returns true if all specified labels exist, otherwise false. + * Sets settings not in the config nor in the Options menu. */ - const CheckGridEntry = (grid, ...labels) => - labels.every(label => grid.some(gridEntry => gridEntry.label.toLowerCase() === label.toLowerCase())); + setSettingsNotInConfig() { + grSet.savedAlbumArtShow = libSet.albumArtShow; + libSet.albumArtDropShadow = grSet.libraryThumbnailBorder === 'shadow'; + bioSet.largerSyncLyricLine = grSet.lyricsLargerCurrentSync; + } /** - * Renames an entry in the metadata grid with a new label name. - * @param {MetadataGridEntry[]} grid Each element in the array is an object with a `label` property. - * @param {string} oldLabel The old label name to rename. - * @param {string} newLabel The new label name that will be replaced in the config file. + * Sets variable imgBlended when switching from default settings to config settings that has style Blend or Blend2 activated. */ - const RenameGridEntry = (grid, oldLabel, newLabel) => { - const entryIdx = grid.findIndex(gridEntry => gridEntry && gridEntry.label.toLowerCase() === oldLabel.toLowerCase()); - if (entryIdx >= 0) { - grid[entryIdx].label = newLabel; + setStyleBlend() { + if ((grSet.styleBlend || grSet.styleBlend2 || grSet.styleProgressBarFill === 'blend') && grm.ui.albumArt) { + grm.color.setStyleBlend(); } - }; + } /** - * Adds or Replaces values in the metadata grid with updated string from defaults. - * @param {MetadataGridEntry[]} grid Each element in the array is an object with a `label` property. - * @param {string} label The label of the value to add or replace. - * @param {number} position 0-based index of place to insert new value if existing entry not found. + * Sets theme performance presets with various theme settings that affect overall performance. + * The 'balanced' preset settings will be also applied in 'highQuality' and 'highestQuality'. + * Used in Options > Settings > Theme performance. + * @param {string} preset - The theme performance preset to load. */ - const ReplaceGridEntry = (grid, label, position) => { - const entryIdx = grid.findIndex(gridEntry => gridEntry && gridEntry.label.toLowerCase() === label.toLowerCase()); - const newVal = defaultMetadataGrid[defaultMetadataGrid.findIndex(e => e && e.label.toLowerCase() === label.toLowerCase())]; - if (entryIdx >= 0) { - grid[entryIdx] = newVal; - } else { - grid.splice(position, 0, newVal); - } - }; - - const configFile = config.readConfiguration(); - const configFileCustom = configCustom.readConfiguration(); - const fileName = `georgia-reborn\\configs\\georgia-reborn-config-${storedVersion}.jsonc`; - const fileNameCustom = `georgia-reborn\\configs\\georgia-reborn-custom-${storedVersion}.jsonc`; - - // * Remove old settings from the config file and add the new settings - const oldSettings = ['playlistSortArtistDateAsc', 'playlistSortArtistDateDesc', 'playlistSortAlbum', 'playlistSortTitle', 'playlistSortTracknum', 'playlistSortArtistYearAsc', 'playlistSortArtistYearDesc']; - if (CheckSettings(configFile.settings, ...oldSettings)) { - fso.CopyFile(configPath, fb.ProfilePath + fileName); - config.writeConfiguration(); - DeleteSettings(configFile.settings, ...oldSettings); - config.addConfigurationObject(settingsSchema, configFile.settings); - config.addConfigurationObject(settingsSchema, settingsDefaults, settingsComments); - config.writeConfiguration(configFile.settings); - } - if (!CheckGridEntry(configFile.metadataGrid, 'Channels')) { - fso.CopyFile(configPath, fb.ProfilePath + fileName); - config.addConfigurationObject(gridSchema, defaultMetadataGrid); - config.writeConfiguration(); - window.Reload(); // Reinit new config - } - - // * Update config settings which have changed since last update - if (version !== storedVersion) { - switch (storedVersion) { - /* eslint-disable no-fallthrough */ - case '3.0-RC1': - RenameGridEntry(configFile.metadataGrid, 'Catalog #', 'Catalog'); - config.addConfigurationObject(gridSchema, configFile.metadataGrid); - case '3.0-RC2': - config.addConfigurationObject(themePlaylistGroupingPresetsSchema, themePlaylistGroupingPresets); - case '3.0-DEV': - // This default block ( latest version ) should appear after all previous versions have fallen through - console.log('> Upgrading Georgia-ReBORN theme settings from', storedVersion); - console.log(`> Backing up Georgia-ReBORN configuration file to ${fileName}`); - fso.CopyFile(configPath, fb.ProfilePath + fileName); - config.writeConfiguration(); - fso.CopyFile(configPathCustom, fb.ProfilePath + fileNameCustom); - configCustom.writeConfiguration(); - break; - default: + setThemePerformance(preset) { + switch (preset) { + case 'balanced': // Default + grSet.playerSize = 'small'; + grm.display.autoDetectRes(); + grSet.styleDefault = true; + grSet.playlistAutoScrollNowPlaying = false; + grSet.playlistSmoothScrolling = true; + grSet.libraryAutoScrollNowPlaying = false; + libSet.smooth = true; + bioSet.smooth = true; + grSet.showStyledTooltips = true; + grSet.showPreloaderLogo = true; + grSet.showHiResAudioBadge = false; + grSet.showPause = true; + grSet.seekbar = 'progressbar'; + grSet.progressBarRefreshRate = 'variable'; + grSet.peakmeterBarRefreshRate = 80; + grSet.waveformBarPaint = 'partial'; + grSet.waveformBarPrepaint = true; + grSet.waveformBarPrepaintFront = Infinity; + grSet.waveformBarAnimate = true; + grSet.waveformBarBPM = true; + grSet.waveformBarRefreshRate = 200; + grSet.playlistLayout = 'normal'; + plSet.show_album_art = true; + grSet.playlistTimeRemaining = false; + grSet.playlistRowHover = true; + grSet.showDiscArtStub = true; + grSet.noDiscArtStub = false; + grSet.discArtStub = 'cdAlbumCover'; + grSet.displayDiscArt = true; + grSet.spinDiscArt = false; + grSet.spinDiscArtImageCount = 72; + grSet.spinDiscArtRedrawInterval = 75; + clearInterval(grm.ui.discArtRotationTimer); + grm.ui.discArtArray = []; + grSet.detailsAlbumArtOpacity = 255; + grSet.detailsAlbumArtDiscAreaOpacity = 255; + grSet.showGridTimeline_default = true; + grSet.showGridTimeline_artwork = true; + grSet.libraryLayout = 'normal'; + grSet.libraryDesign = 'reborn'; + grSet.libraryTheme = 0; + libSet.albumArtShow = false; + grSet.libraryRowHover = true; + grSet.biographyLayout = 'normal'; + grSet.biographyTheme = 0; + bioSet.showFilmStrip = false; + bioCfg.photoNum = 10; + libSet.albumArtDiskCache = true; + libSet.albumArtPreLoad = false; + grSet.libraryAutoDelete = false; + grSet.biographyAutoDelete = false; + grSet.lyricsAutoDelete = false; + bioSet.focusLoadRate = 1000; + bioSet.focusLoadImmediate = false; + grSet.lyricsDropShadowLevel = 2; + grSet.lyricsFadeScroll = true; + grSet.lyricsAlbumArt = true; + grSet.lyricsRememberPanelState = false; + grSet.lyricsScrollSpeed = 'normal'; + grSet.lyricsScrollRateAvg = 750; + grSet.lyricsScrollRateMax = 375; break; - } - } - - pref.version = currentVersion; // Always update the version panel property -} + case 'lowestQuality': + grSet.playerSize = 'small'; + grSet.playerSize_HD_small = true; + grm.display.playerSize_HD_small(); + grSet.styleDefault = true; + grSet.displayRes = 'HD'; + grSet.playlistAutoScrollNowPlaying = false; + grSet.playlistSmoothScrolling = false; + grSet.libraryAutoScrollNowPlaying = false; + libSet.smooth = false; + bioSet.smooth = false; + grSet.showStyledTooltips = false; + grSet.showPreloaderLogo = false; + grSet.showHiResAudioBadge = false; + grSet.showPause = false; + grSet.seekbar = 'progressbar'; + grSet.progressBarRefreshRate = 1000; + grSet.peakmeterBarRefreshRate = 200; + grSet.waveformBarPaint = 'full'; + grSet.waveformBarPrepaint = false; + grSet.waveformBarPrepaintFront = 2; + grSet.waveformBarAnimate = false; + grSet.waveformBarBPM = false; + grSet.waveformBarRefreshRate = 1000; + grSet.playlistLayout = 'normal'; + plSet.show_album_art = false; + grSet.playlistTimeRemaining = false; + grSet.playlistRowHover = false; + grSet.showDiscArtStub = false; + grSet.noDiscArtStub = true; + grSet.displayDiscArt = false; + grSet.spinDiscArt = false; + grSet.spinDiscArtImageCount = 36; + grSet.spinDiscArtRedrawInterval = 250; + grSet.showGridTimeline_default = false; + grSet.showGridTimeline_artwork = false; + grSet.libraryLayout = 'normal'; + grSet.libraryDesign = 'reborn'; + grSet.libraryTheme = 0; + libSet.albumArtShow = false; + grSet.libraryRowHover = false; + grSet.biographyLayout = 'normal'; + grSet.biographyTheme = 0; + bioSet.showFilmStrip = false; + bioCfg.photoNum = 1; + libSet.albumArtDiskCache = true; + libSet.albumArtPreLoad = false; + bioSet.focusLoadRate = 3000; + grSet.lyricsDropShadowLevel = 0; + grSet.lyricsFadeScroll = false; + grSet.lyricsAlbumArt = false; + grSet.lyricsRememberPanelState = false; + grSet.lyricsScrollSpeed = 'fastest'; + grSet.lyricsScrollRateAvg = 300; + grSet.lyricsScrollRateMax = 150; + break; -/** - * Checks if there is a new update available, also called in Options > Help > Theme > Updates > Check for latest theme update. - * @param {boolean} openUrl Opens the Georgia-ReBORN Github releases page when hyperlink is available. - */ -function checkForUpdates(openUrl) { - const url = 'https://api.github.com/repos/TT-ReBORN/Georgia-ReBORN/tags'; - MakeHttpRequest('GET', url, (resp) => { - try { - /** @type {boolean} The current Github master version, used to prevent notifying users for new update when in development state. */ - const developVersion = currentVersion.endsWith('DEV'); - const respObj = JSON.parse(resp); - updateAvailable = developVersion ? false : IsNewerVersion(currentVersion, respObj[0].name); - console.log(`Current released version of Georgia-ReBORN: v${respObj[0].name}`); - if (updateAvailable) { - console.log('>>> Georgia-ReBORN new update available. Download it from here: https://github.com/TT-ReBORN/Georgia-ReBORN/releases'); - updateHyperlink = new Hyperlink('New Update Available', ft.lower_bar_title, 'update', 0, 0, window.Width); - if (updateHyperlink) { - lowerBarStoppedTime = ''; - if (!fb.IsPlaying) { - str.time = lowerBarStoppedTime; - RepaintWindow(); - } - if (openUrl) { - updateHyperlink.click(); - } - } - } else { - console.log('You are on the most current version of Georgia-ReBORN'); - } - } catch (e) { - if (!updateHyperlink && updateRetryCount < 3) { - // updateHyperlink failed to be created somehow. Let's check again after 1 minute. - updateRetryCount++; - updateAvailable = false; - scheduleUpdateCheck(61000); - } - } - }); -} + case 'lowQuality': + grSet.playerSize = 'small'; + grSet.styleDefault = true; + grSet.displayRes = 'HD'; + grSet.showStyledTooltips = false; + grSet.seekbar = 'progressbar'; + grSet.progressBarRefreshRate = 500; + grSet.peakmeterBarRefreshRate = 120; + grSet.waveformBarPaint = 'full'; + grSet.waveformBarPrepaint = false; + grSet.waveformBarPrepaintFront = 2; + grSet.waveformBarAnimate = false; + grSet.waveformBarBPM = false; + grSet.waveformBarRefreshRate = 500; + grSet.playlistTimeRemaining = false; + grSet.showDiscArtStub = false; + grSet.noDiscArtStub = true; + grSet.displayDiscArt = false; + grSet.spinDiscArt = false; + grSet.spinDiscArtImageCount = 45; + grSet.spinDiscArtRedrawInterval = 125; + grSet.libraryTheme = 0; + libSet.albumArtShow = false; + grSet.biographyTheme = 0; + bioSet.showFilmStrip = false; + bioCfg.photoNum = 5; + libSet.albumArtDiskCache = true; + libSet.albumArtPreLoad = false; + bioSet.focusLoadRate = 2000; + grSet.lyricsDropShadowLevel = 0; + grSet.lyricsScrollSpeed = 'fast'; + grSet.lyricsScrollRateAvg = 500; + grSet.lyricsScrollRateMax = 250; + break; + case 'highQuality': + grSet.playerSize = 'normal'; + grSet.progressBarRefreshRate = 100; + grSet.peakmeterBarRefreshRate = 60; + grSet.waveformBarPaint = 'partial'; + grSet.waveformBarPrepaint = true; + grSet.waveformBarPrepaintFront = Infinity; + grSet.waveformBarRefreshRate = 100; + grSet.waveformBarRefreshRateVar = false; + grSet.spinDiscArt = true; + grSet.spinDiscArtImageCount = 120; + grSet.spinDiscArtRedrawInterval = 40; + grm.ui.setDiscArtRotationTimer(); + grSet.libraryLayout = 'full'; + libSet.albumArtShow = true; + grSet.biographyLayout = 'full'; + bioCfg.photoNum = 15; + libSet.albumArtDiskCache = true; + libSet.albumArtPreLoad = true; + bioSet.focusLoadRate = 750; + grSet.lyricsScrollSpeed = 'slow'; + grSet.lyricsScrollRateAvg = 1000; + grSet.lyricsScrollRateMax = 500; + break; -/** - * Schedules an update check. Sets at startup and then typically every 24 hours after unless an update is found. - * @param {number} delay In milliseconds. - */ -function scheduleUpdateCheck(delay) { - clearTimeout(updateTimer); - updateTimer = setTimeout(() => { - if (!updateAvailable) { - checkForUpdates(false); - scheduleUpdateCheck(1000 * 60 * 60 * 24); // Check every 24 hours + case 'highestQuality': + grSet.playerSize = 'large'; + grSet.progressBarRefreshRate = 30; + grSet.peakmeterBarRefreshRate = 30; + grSet.waveformBarPaint = 'partial'; + grSet.waveformBarPrepaint = true; + grSet.waveformBarPrepaintFront = Infinity; + grSet.waveformBarRefreshRate = 30; + grSet.waveformBarRefreshRateVar = false; + grSet.spinDiscArt = true; + grSet.spinDiscArtImageCount = 180; + grSet.spinDiscArtRedrawInterval = 10; + grm.ui.setDiscArtRotationTimer(); + grSet.detailsAlbumArtDiscAreaOpacity = 178; + grSet.libraryLayout = 'full'; + libSet.albumArtShow = true; + grSet.biographyLayout = 'full'; + bioCfg.photoNum = 20; + libSet.albumArtDiskCache = true; + libSet.albumArtPreLoad = true; + bioSet.focusLoadRate = 500; + grSet.lyricsScrollSpeed = 'slowest'; + grSet.lyricsScrollRateAvg = 1500; + grSet.lyricsScrollRateMax = 725; + break; } - }, delay); + } + // #endregion } - - -//////////////////////////// -// ! DO MIGRATION CHECK ! // -//////////////////////////// -migrateCheck(currentVersion, configVersion); diff --git a/profile/georgia-reborn/scripts/Base/gr-setup.js b/profile/georgia-reborn/scripts/Base/gr-setup.js index e1d63949..0de059dc 100644 --- a/profile/georgia-reborn/scripts/Base/gr-setup.js +++ b/profile/georgia-reborn/scripts/Base/gr-setup.js @@ -1,924 +1,464 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Setup * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-11 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Setup * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////////// -// * COMPATIBILITY * // -/////////////////////// +////////////// +// * MAIN * // +////////////// /** - * Detects if user has Internet Explorer installed, needed to render HTML popups. - * @returns {boolean} Returns `true` if IE installed, otherwise `false`. - * @type {Function} + * A collection of all Georgia-ReBORN class instances that will be initialized at the end in gr.initialize.js. + * @typedef {object} grm - The Georgia-ReBORN main object. + * @property {Utilities} utils - The instance of `Utilities` class for utility operations. + * @property {MainUI} ui - The instance of `MainUI` class for main user interface operations. + * @property {ThemeSettingsManager} settings - The instance of `ThemeSettingsManager` class for theme settings operations. + * @property {Display} display - The instance of `Display` class for UI display operations. + * @property {ColorMethods} color - The instance of `ColorMethods` class for color-related utility operations. + * @property {ThemeColors} theme - The instance of `ThemeColors` class for theme color operations. + * @property {StyleColors} style - The instance of `StyleColors` class for style color operations. + * @property {ThemePreset} preset - The instance of `ThemePreset` class for theme preset operations. + * @property {TopMenu} topMenu - The instance of `TopMenu` class for top menu interface operations. + * @property {TopMenuOptions} options - The instance of `TopMenuOptions` class for top menu option management operations. + * @property {ContextMenus} ctxMenu - The instance of `ContextMenus` class for context menu operations. + * @property {InputBox} inputBox - The instance of `InputBox` class for input box operations. + * @property {CustomMenu} cusMenu - The instance of `CustomMenu` class for custom menu base control operations. + * @property {CustomThemeMenu} cthMenu - The instance of `CustomThemeMenu` class for custom theme menu operations. + * @property {MetadataGridMenu} gridMenu - The instance of `MetadataGridMenu` class for metadata grid menu operations. + * @property {ArtCache} artCache - The instance of `ArtCache` class for artwork caching operations. + * @property {Button} button - The instance of `Button` class for button operations. + * @property {PauseButton} pseBtn - The instance of `PauseButton` class for pause button operations. + * @property {VolumeButton} volBtn - The instance of `VolumeButton` class for volume button operations. + * @property {TooltipHandler} ttip - The instance of `TooltipHandler` class for tooltip handling operations. + * @property {LowerBarTooltip} lowerTip - The instance of `LowerBarTooltip` class for lower bar tooltip operations. + * @property {MetadataGridTooltip} gridTip - The instance of `MetadataGridTooltip` class for metadata grid tooltip operations. + * @property {Timeline} timeline - The instance of `Timeline` class for timeline operations. + * @property {PlaylistHistory} history - The instance of `PlaylistHistory` class for playlist history operations. + * @property {JumpSearch} jSearch - The instance of `JumpSearch` class for jump search operations. + * @property {ProgressBar} progBar - The instance of `ProgressBar` class for progress bar display operations. + * @property {PeakmeterBar} peakBar - The instance of `PeakmeterBar` class for peak meter bar display operations. + * @property {WaveformBar} waveBar - The instance of `WaveformBar` class for waveform bar display operations. + * @property {Lyrics} lyrics - The instance of `Lyrics` class for lyrics-related operations. */ -const detectIE = DetectIE(); +/** @global @type {grm} */ +const grm = {}; -/** - * Detects if the user's system is running on Windows 64 bit. - * @returns {boolean} Returns `true` if Windows is 64 bit, otherwise `false`. - * @type {Function} - */ -const detectWin64 = DetectWin64(); +////////////////////////// +// * TITLE FORMATTING * // +////////////////////////// /** - * Detects if the user's system is running Wine on Linux or MacOs. - * @returns {boolean} Returns `true` if Wine is running, otherwise `false`. - * @type {Function} + * A collection of title formatting strings used throughout the UI. + * @typedef {object} grTF + * @property {string} album_subtitle - %albumsubtitle%. + * @property {string} album_translation - %albumtranslation%. + * @property {string} artist_country - '%artistcountry%'. + * @property {string} artist - '$if3($meta(artist),%composer%,%performer%,%album artist%)'. + * @property {string} date - '$if3(%original release date%,%originaldate%,%date%,%fy_upload_date%,)'. + * @property {string} disc_subtitle - '%discsubtitle%'. + * @property {string} disc - '$ifgreater(%totaldiscs%,1,CD %discnumber%/%totaldiscs%,)'. + * @property {string} edition - '[$if2($if(%original release date%,$ifequal($year(%original release date%),$year(%date%),,$year(%date%) ))$if2(%edition%,\'release\'),$if(%originaldate%,$ifequal($year(%originaldate%),$year(%date%),,$year(%date%) ))$if2(%edition%,\'release\'))]'. + * @property {string} last_played - '[$if2(%last_played_enhanced%,%last_played%)]'. + * @property {string} lyrics - '[$if3(%synced lyrics%,%syncedlyrics%,%lyrics%,%lyric%,%unsyncedlyrics%,%unsynced lyrics%,)]'. + * @property {string} original_artist - '[ \'(\'%original artist%\' cover)\']'. + * @property {string} composer - '[\' -\' %composer% \' \']'. + * @property {string} releaseCountry - '$replace($if3(%releasecountry%,%discogs_country%,),AF,XW)'. + * @property {string} title - '%title%[ \'[\'%translation%\']\']'. + * @property {string} tracknum - '[%tracknumber%.]'. + * @property {string} vinyl_side - '%vinyl side%'. + * @property {string} vinyl_tracknum - '%vinyl tracknumber%'. + * @property {string} vinyl_track - '$if2(%vinyl side%[%vinyl tracknumber%]. ,[%tracknumber%. ])'. + * @property {string} year - '[$year($if3(%original release date%,%originaldate%,%date%,%fy_upload_date%,))]'. + * @property {string} playing_playlist - 'Do not change this value as it is handled by the theme itself'. */ -const detectWine = DetectWine(); +/** @global @type {grTF} */ +const grTF = { + album_subtitle:'%albumsubtitle%', + album_translation:'%albumtranslation%', + artist_country:'%artistcountry%', + artist:'$if3($meta(artist),%composer%,%performer%,%album artist%)', + date:'$if3(%original release date%,%originaldate%,%date%,%fy_upload_date%,)', + disc_subtitle:'%discsubtitle%', + disc:'$ifgreater(%totaldiscs%,1,CD %discnumber%/%totaldiscs%,)', + edition:'[$if2($if(%original release date%,$ifequal($year(%original release date%),$year(%date%),,$year(%date%) ))$if2(%edition%,\'release\'),$if(%originaldate%,$ifequal($year(%originaldate%),$year(%date%),,$year(%date%) ))$if2(%edition%,\'release\'))]', + last_played:'[$if2(%last_played_enhanced%,%last_played%)]', + lyrics:'[$if3(%synced lyrics%,%syncedlyrics%,%lyrics%,%lyric%,%unsyncedlyrics%,%unsynced lyrics%,)]', + original_artist:'[ \'(\'%original artist%\' cover)\']', + composer:'[\' -\' %composer% \' \']', + releaseCountry:'$replace($if3(%releasecountry%,%discogs_country%,),AF,XW)', + title:'%title%[ \'[\'%translation%\']\']', + tracknum:'[%tracknumber%.]', + vinyl_side:'%vinyl side%', + vinyl_tracknum:'%vinyl tracknumber%', + vinyl_track:'$if2(%vinyl side%[%vinyl tracknumber%]. ,[%tracknumber%. ])', + year:'[$year($if3(%original release date%,%originaldate%,%date%,%fy_upload_date%,))]', + playing_playlist:'Do not change this value as it is handled by the theme itself' +}; //////////////// -// * COLORS * // +// * CONFIG * // //////////////// /** - * @typedef {Object} ColorsObj - * @property {number=} darkAccent The primary color shaded by 30%. - * @property {number=} darkAccent_alt The secondary primary color shaded by 30%. - * @property {number=} accent The primary color shaded by 15%. - * @property {number=} accent_alt The secondary primary color shaded by 15%. - * @property {number=} primary The primary theme color generated from artwork. - * @property {number=} primary_alt The secondary primary theme color generated from artwork. - * @property {number=} lightAccent The primary color tinted by 20%. - * @property {number=} lightAccent_alt The secondary primary color tinted by 20%. - * @property {number=} artist The color of artist text on background. - * @property {number=} now_playing The color of the lower bar text, including tracknum, title, elapsed and remaining time. - * @property {number=} info_text The default color of text in metadata grid. - * @property {number=} bg The background of the entire panel. - * @property {number=} rating The color of rating stars in metadata grid. - * @property {number=} hotness The color of hotness text in metadat agrid. - * @property {number=} timelineAdded The background color for timeline block in Details from added to first played. - * @property {number=} timelinePlayed The background color for timeline block in Details from first played to last played. - * @property {number=} timelineUnplayed The background color for timeline block in Details from last played to present time. - * @property {number=} progressBar The background of the progress bar. Fill will be col.primary. - * @property {number=} shadow The color of the shadow. - */ - -/** @type {ColorsObj} Colors */ -const col = {}; - -/** - * Calculated primary color brightness used in: - * - pref.theme === 'white' - * - pref.theme === 'black' - * - pref.theme === 'reborn' - * - pref.theme === 'random' - * @type {number} + * The instance of `ConfigDefaults` class for default config setting operations. + * @typedef {ConfigDefaults} + * @global */ -let colBrightness; +const grDef = new ConfigDefaults(); /** - * Calculated secondary color brightness used in: - * - pref.styleRebornFusion - * - pref.styleRebornFusion2 - * - pref.styleRebornFusionAccent - * @type {number} + * The instance of `ConfigurationManager` class for config operations. + * @typedef {ConfigurationManager} + * @global */ -let colBrightness2; - -/** - * Calculated image brightness used in: - * - pref.styleBlend - * - pref.styleBlend2 - * - pref.styleBlackAndWhite - * - pref.styleBlackAndWhite2 - * - pref.styleBlackAndWhiteReborn - * @type {number} - */ -let imgBrightness; - -/** - * Blended image from setStyleBlend(). - * @type {GdiBitmap} - */ -let blendedImg; - -/** - * Checks if background color is not full white RGB(255, 255, 255). - * Used in Reborn/Random theme when init on start or when noAlbumArtStub displayed. - * @type {boolean} - */ -let isColored; - -/** - * Color definition when to switch text and logos to white or black, used in: - * - pref.theme === 'white' - * - pref.theme === 'black' - * - pref.theme === 'reborn' - * - pref.theme === 'random' - * - pref.styleBlend - * - pref.styleBlend2 - * @type {boolean} - */ -let lightBg; - -/** - * Color definition when to switch text and logos to white or black, used in: - * - ppt.theme === 1 - * - ppt.theme === 2 - * - ppt.theme === 3 - * - ppt.theme === 4 - * - ppt.theme === 5 - * @type {boolean} - */ -let lightBgLib; - -/** - * Color definition when to switch text and logos to white or black, used in: - * - pptBio.theme === 1 - * - pptBio.theme === 2 - * - pptBio.theme === 3 - * - pptBio.theme === 4 - * @type {boolean} - */ -let lightBgBio; - -/** - * Color definition for col.bg when to lighten or darken custom theme colors: - * - pref.theme === 'custom01' - 'custom10' - * @type {boolean} - */ -let lightBgMain; - -/** - * Color definition for g_pl_colors.bg when to lighten or darken custom theme colors: - * - pref.theme === 'custom01' - 'custom10' - * @type {boolean} - */ -let lightBgPlaylist; - -/** - * Color definition for col.detailsBg) when to lighten or darken custom theme colors: - * - pref.theme === 'custom01' - 'custom10' - * @type {boolean} - */ -let lightBgDetails; - -/** - * Color definition for ui.col.bg when to lighten or darken custom theme colors: - * - pref.theme === 'custom01' - 'custom10' - * @type {boolean} - */ -let lightBgLibrary; - -/** - * Color definition for uiBio.col.bg when to lighten or darken custom theme colors: - * - pref.theme === 'custom01' - 'custom10' - * @type {boolean} - */ -let lightBgBiography; - - -////////////////// -// * GEOMETRY * // -////////////////// -/** - * @typedef {Object} GeometryObj - * @property {number} topMenuHeight The height of the top menu. - * @property {number} lowerBarHeight The height of the song title and time + progress bar area. - * @property {number} progBarHeight The height of the progress bar. - * @property {number} waveformBarHeight The height of the waveform bar. - * @property {number} peakmeterBarHeight The height of the peakmeter bar. - * @property {number} timelineHeight The height of the timeline. - * @property {number} metadataGridTooltipHeight The height of the metadata grid tooltip area. - * @property {number} pauseSize The width and height of the pause button. - * @property {number} discArtShadow The size of the disc art shadow. - */ - -/** @type {GeometryObj} */ -const geo = {}; - -/** - * Sets the sizes for various UI elements. - */ -function setGeometry() { - geo.topMenuHeight = SCALE(40); - geo.lowerBarHeight = SCALE(120); - geo.progBarHeight = SCALE(pref.layout !== 'default' && (pref.styleProgressBarDesign === 'default' || pref.styleProgressBarDesign === 'rounded') ? 10 : 12) + (ww > 1920 ? 2 : 0); - geo.peakmeterBarHeight = SCALE(pref.layout !== 'default' ? 16 : 26) + (ww > 1920 ? 2 : 0); - geo.waveformBarHeight = SCALE(pref.layout !== 'default' ? 16 : 26) + (ww > 1920 ? 2 : 0); - geo.timelineHeight = Math.round(geo.progBarHeight * 0.66); - geo.metadataGridTooltipHeight = SCALE(100); - geo.pauseSize = SCALE(100); - geo.discArtShadow = SCALE(6); -} - - -////////////////// -// * POSITION * // -////////////////// -/** @type {number} The width of time string in the lower bar. */ -let lowerBarTimeW; -/** @type {number} The height of time string in the lower bar. */ -let lowerBarTimeH; -/** @type {number} The x-position of the time string in the lower bar. */ -let lowerBarTimeX; -/** @type {number} The y-position of the time string in the lower bar. */ -let lowerBarTimeY; -/** @type {number} The y-position of the progress bar in the lower bar. */ -let progressBarY; -/** @type {number} The y-position of the peakmeter bar in the lower bar. */ -let peakmeterBarY; -/** @type {number} The y-position of the waveform bar in the lower bar. */ -let waveformBarY; - - -/////////////////////// -// * STRING OBJECT * // -/////////////////////// -/** - * @typedef {Object} MetadataGridObj - * @property {boolean=} age Should the age of the field also be calculated (i.e. add the "(3y 5m 11d)" to `val`). - * @property {string} label The metadata grid label. - * @property {string} val The metadata grid value. If `val.trim().length === 0`. The grid entry will not be shown. - */ - -/** - * @typedef {Object} StringsObj A collection of strings and other objects to be displayed throughout UI. - * @property {string=} artist The artist will be shown in Details and in the lower bar. - * @property {string=} composer The composer will be shown in the lower bar if it exist and enabled. - * @property {string=} album The album will be shown in Details. - * @property {string=} album_subtitle Currently not used in the theme. - * @property {string=} disc By default, this string is displayed in the lower bar if there is more than one total disc. Formatted like: "CD1/2". - * @property {Array=} grid - * @property {string=} length The length of the song in MM:SS format. - * @property {string=} original_artist If %original artist% exist it will be displayed by the right side of the title in the lower bar. - * @property {string=} time The current time of the song in MM:SS format in the lower bar. - * @property {string=} title The title of the song. - * @property {string=} title_lower The title of the song to be displayed above the progress bar. Can include more information such as translation, original artist, etc. - * @property {string=} tracknum The Tracknumber of the song. - * @property {*=} trackInfo Currently not used in the theme. - * @property {string=} year Currently not used in the theme. - * @property {Timeline=} timeline The timeline object. - * @property {MetadataGridTooltip=} metadata_grid_tt The metadata grid tooltip object. - * @property {LowerBarTooltip=} lowerBar_tt The lower bar tooltip object. - */ - -/** @type {StringsObj} */ -let str = {}; +const grCfg = new ConfigurationManager(); /////////////// -// * FONTS * // +// * PATHS * // /////////////// /** - * @typedef {Object} GdiFont - * @property {GdiFont} top_menu Theme font 'Segoe UI Semibold' used for top menu buttons. - * @property {GdiFont} top_menu_caption Theme font 'Marlett' used for top menu 🗕 🗖 ✖ caption buttons. - * @property {GdiFont} top_menu_compact Theme font 'FontAwesome' used for the top menu compact button. - * @property {GdiFont} lower_bar_artist Theme artist font 'HelveticaNeueLT Pro 65 Md' used in lower bar. - * @property {GdiFont} lower_bar_title Theme title font 'HelveticaNeueLT Pro 45 Lt' used in lower bar. - * @property {GdiFont} lower_bar_disc Theme disc font 'HelveticaNeueLT Pro 45 Lt' used in lower bar. - * @property {GdiFont} lower_bar_time Theme time font 'HelveticaNeueLT Pro 65 Md' used in lower bar. - * @property {GdiFont} lower_bar_length Theme length font 'HelveticaNeueLT Pro 45 Lt' used in lower bar. - * @property {GdiFont} lower_bar_wave Theme waveform bar font 'HelveticaNeueLT Pro 65 Md' used in lower bar. - * @property {GdiFont} guifx Theme font 'Guifx v2 Transports' used for the lower bar transport/playback buttons. - * @property {GdiFont} pbo_default Theme font 'Guifx v2 Transports' used for the lower bar transport playback order button. - * @property {GdiFont} pbo_repeat_playlist Theme font 'FontAwesome' used for the lower bar transport playback order button. - * @property {GdiFont} pbo_repeat_track Theme font 'FontAwesome' used for the lower bar transport playback order button. - * @property {GdiFont} pbo_shuffle Theme font 'Guifx v2 Transports' used for the lower bar transport playback order button. - * @property {GdiFont} guifx_reload Theme font 'Guifx v2 Transports' used for the lower bar transport reload button. - * @property {GdiFont} guifx_volume Theme font 'Guifx v2 Transports' used for the lower bar transport volume button. - * @property {GdiFont} no_album_art_stub Theme font 'FontAwesome' used for no album art music note symbol. - * @property {GdiFont} symbol Panel font 'Segoe UI Symbol' used for special chars, scrollbar buttons, etc. - * @property {GdiFont} notification Theme font 'HelveticaNeueLT Pro 65 Md' used for notifications. - * @property {GdiFont} popup Theme font 'Segoe UI' used for popups. - * @property {GdiFont} tooltip Theme font 'HelveticaNeueLT Pro 65 Md' used for styled tooltips. - * @property {GdiFont} grd_artist Theme font 'HelveticaNeueLT Pro 65 Md' used for metadata grid artist. - * @property {GdiFont} grd_tracknum Theme font 'HelveticaNeueLT Pro 45 Lt' or 'HelveticaNeueLT Pro 65 Md' used for metadata grid track number. - * @property {GdiFont} grd_title Theme font 'HelveticaNeueLT Pro 45 Lt' or 'HelveticaNeueLT Pro 65 Md' used for metadata grid title. - * @property {GdiFont} grd_album Theme font 'HelveticaNeueLT Pro 65 Md' used for metadata grid album. - * @property {GdiFont} grd_key Theme font 'HelveticaNeueLT Pro 75 Bd' or 'HelveticaNeueLT Pro 65 Md' used for metadata grid key. - * @property {GdiFont} grd_val Theme font 'HelveticaNeueLT Pro 45 Lt' used for metadata grid value. - * @property {GdiFont} library Panel font 'Segoe UI' used in the Library. - * @property {GdiFont} biography Panel font 'Segoe UI' used in the Biography. - * @property {GdiFont} lyrics Panel font 'Segoe UI' used in the Lyrics. - * @property {GdiFont} lyricsHighlight Panel font 'Segoe UI' used in Lyrics for synced lines. + * A collection of image and various other paths. + * @typedef {object} grPath - The Georgia-ReBORN path object. + * @property {string} base - The theme path shortcut. + * @property {string} images - The images+custom path shortcut. + * @property {string} artistlogos - The artist logos filepath. + * @property {string} artistlogosColor - The artist logos colored filepath. + * @property {string} labelsBase - The record label filepath. + * @property {string} flagsBase - The the flags filepath. + * @property {string} lastFmImageRed - The last.fm red logo filepath. + * @property {string} lastFmImageWhite - The last.fm white logo filepath. + * @property {Function} discArtImagePaths - The collection of all disc art paths to search in. + * @property {Function} discArtStubPaths - The collection of all disc art stub paths to search in. + * @property {string} discArtCustomStub - The disc art custom stub filepath. + * @property {string} hiResAudioLogoPath - The Hi-Res audio logo filepath. + * @property {string[]} lyricsPath - The array of paths for locating cached lyrics files in the user's system. */ +/** @global @type {grPath} */ +const grPath = { + // * THEME BASE PATH * // + base: `${fb.ProfilePath}georgia-reborn\\`, -/** @type {GdiFont} */ -const ft = {}; - -/** Panel font used as default and panel related elements in Playlist, Library and Biography. */ -const fontDefault = pref.customThemeFonts ? customFont.fontDefault : 'Segoe UI'; -const fontSegoeUISymbol = 'Segoe UI Symbol'; -const fontTopMenu = pref.customThemeFonts ? customFont.fontTopMenu : 'Segoe UI Semibold'; -const fontTopMenuCaption = 'Marlett'; - -const fontGuiFx = 'Guifx v2 Transports'; -const fontAwesome = 'FontAwesome'; -const fontLowerBarArtist = pref.customThemeFonts ? customFont.fontLowerBarArtist : 'HelveticaNeueLT Pro 65 Md'; -const fontLowerBarTitle = pref.customThemeFonts ? customFont.fontLowerBarTitle : 'HelveticaNeueLT Pro 45 Lt'; -const fontLowerBarDisc = pref.customThemeFonts ? customFont.fontLowerBarDisc : 'HelveticaNeueLT Pro 45 Lt'; -const fontLowerBarTime = pref.customThemeFonts ? customFont.fontLowerBarTime : 'HelveticaNeueLT Pro 65 Md'; -const fontLowerBarLength = pref.customThemeFonts ? customFont.fontLowerBarLength : 'HelveticaNeueLT Pro 45 Lt'; -const fontLowerBarWave = pref.customThemeFonts ? customFont.fontLowerBarWave : 'HelveticaNeueLT Pro 65 Md'; - -const fontNotification = pref.customThemeFonts ? customFont.fontNotification : 'HelveticaNeueLT Pro 65 Md'; -const fontPopup = pref.customThemeFonts ? customFont.fontPopup : 'Segoe UI'; -const fontTooltip = pref.customThemeFonts ? customFont.fontTooltip : 'HelveticaNeueLT Pro 65 Md'; - -const fontGridArtist = pref.customThemeFonts ? customFont.fontGridArtist : 'HelveticaNeueLT Pro 65 Md'; -const fontGridTitle = pref.customThemeFonts ? customFont.fontGridTitle : 'HelveticaNeueLT Pro 45 Lt'; -const fontGridTitleBold = pref.customThemeFonts ? customFont.fontGridTitleBold : 'HelveticaNeueLT Pro 65 Md'; -const fontGridAlbum = pref.customThemeFonts ? customFont.fontGridAlbum : 'HelveticaNeueLT Pro 65 Md'; -const fontGridKey = pref.customThemeFonts ? customFont.fontGridKey : - detectWine ? 'HelveticaNeueLT Pro 65 Md' : 'HelveticaNeueLT Pro 75 Bd'; -const fontGridValue = pref.customThemeFonts ? customFont.fontGridValue : 'HelveticaNeueLT Pro 45 Lt'; - -const fontLibrary = pref.customThemeFonts ? customFont.fontLibrary : 'Segoe UI'; -const fontBiography = pref.customThemeFonts ? customFont.fontBiography : 'Segoe UI'; -const fontLyrics = pref.customThemeFonts ? customFont.fontLyrics : 'Segoe UI'; - -const fontList = [ - fontDefault, fontSegoeUISymbol, fontTopMenu, fontTopMenuCaption, - fontGuiFx, fontAwesome, fontLowerBarArtist, fontLowerBarTitle, fontLowerBarDisc, fontLowerBarTime, fontLowerBarLength, fontLowerBarWave, - fontNotification, fontPopup, fontTooltip, - fontGridArtist, fontGridTitle, fontGridTitleBold, fontGridAlbum, fontGridKey, fontGridValue, - fontLibrary, fontBiography, fontLyrics -]; - -/** @type {boolean} The state of installed fonts on system, will return false if one is missing. */ -let fontsInstalled = true; - -if (!fontList.every((fontName) => TestFont(fontName))) { - fontsInstalled = false; - fb.ShowPopupMessage('Georgia-ReBORN WAS UNABLE TO LOAD SOME FONTS\n\n' + - 'Be sure all fonts from\nfoobar2000\\profile\\georgia-reborn\\fonts\nare correctly installed in these directories:\n\n' + - 'For Windows: C:\\Windows\\Fonts\\\nFor Linux: /usr/share/fonts or ~/.local/share/fonts\n\n' + - 'If you use custom fonts, all your custom fonts need to have\nthe exact font name / font family name in your\n' + - 'foobar\\profile\\georgia-reborn\\configs\\georgia-reborn-config.jsonc config file.\n\n' + - 'You can also check foobar\'s console ( Top menu > View > Console ),\nit will show font errors with its wrong font names.', 'FONT ERROR WARNING'); -} - -if (pref.customThemeFonts) { - console.log('\nUser\'s set custom fonts are being used:\n\n' - + `Panel default: ${customFont.fontDefault}\n` - + `Top menu: ${customFont.fontTopMenu}\n` - + `Lower bar artist: ${customFont.fontLowerBarArtist}\n` - + `Lower bar title: ${customFont.fontLowerBarTitle}\n` - + `Lower bar disc: ${customFont.fontLowerBarDisc}\n` - + `Lower bar time: ${customFont.fontLowerBarTime}\n` - + `Lower bar length: ${customFont.fontLowerBarLength}\n` - + `Lower bar waveform bar: ${customFont.fontLowerBarWave}\n` - + `Notification: ${customFont.fontNotification}\n` - + `Popup: ${customFont.fontPopup}\n` - + `Tooltip: ${customFont.fontTooltip}\n` - + `Grid artist: ${customFont.fontGridArtist}\n` - + `Grid title: ${customFont.fontGridTitle}\n` - + `Grid title bold: ${customFont.fontGridTitleBold}\n` - + `Grid album: ${customFont.fontGridAlbum}\n` - + `Grid key: ${customFont.fontGridKey}\n` - + `Grid value: ${customFont.fontGridValue}\n` - + `Playlist artist normal: ${customFont.playlistArtistNormal}\n` - + `Playlist artist playing: ${customFont.playlistArtistPlaying}\n` - + `Playlist artist normal compact: ${customFont.playlistArtistNormalCompact}\n` - + `Playlist artist playing compact: ${customFont.playlistArtistPlayingCompact}\n` - + `Playlist title normal: ${customFont.playlistTitleNormal}\n` - + `Playlist title selected: ${customFont.playlistTitleSelected}\n` - + `Playlist title playing: ${customFont.playlistTitlePlaying}\n` - + `Playlist album: ${customFont.playlistAlbum}\n` - + `Playlist date: ${customFont.playlistDate}\n` - + `Playlist date compact: ${customFont.playlistDateCompact}\n` - + `Playlist info: ${customFont.playlistInfo}\n` - + `Playlist cover: ${customFont.playlistCover}\n` - + `Playlist playcount: ${customFont.playlistPlaycount}\n` - + `Library: ${customFont.fontLibrary}\n` - + `Biography: ${customFont.fontBiography}\n` - + `Lyrics: ${customFont.fontLyrics}\n\n` - ); -} + // * IMAGES BASE PATH * // + images: `${fb.ProfilePath}georgia-reborn\\${grSet.customThemeImages ? 'images\\custom' : 'images'}\\`, -/** - * Creates and sets all theme fonts. - */ -function createFonts() { - g_tooltip = window.Tooltip; - if (g_tooltip) { - g_tooltip.Text = ''; // Just in case - g_tooltip.SetFont(fontDefault, SCALE(15)); - g_tooltip.SetMaxWidth(SCALE(pref.layout !== 'default' ? 600 : 800)); - } - - // * FONT SIZES * // - const menuFontSize = pref[`menuFontSize_${pref.layout}`]; - const menuCaptionFontSize = pref[`menuFontSize_${pref.layout}`] + 1; - const lowerBarFontSize = pref[`lowerBarFontSize_${pref.layout}`]; - const notificationFontSize = pref[`notificationFontSize_${pref.layout}`]; - const popupFontSize = pref[`popupFontSize_${pref.layout}`]; - const tooltipFontSize = pref[`tooltipFontSize_${pref.layout}`]; - - const guiFxBtnFontSize = pref[`transportButtonSize_${pref.layout}`] / 2; - const pboDefaultBtnFontSize = pref[`transportButtonSize_${pref.layout}`] / 1.6; - const pboReplayBtnFontSize = pref[`transportButtonSize_${pref.layout}`] / 2; - const pboShuffleBtnFontSize = pref[`transportButtonSize_${pref.layout}`] / 1.65; - const reloadBtnFontSize = pref[`transportButtonSize_${pref.layout}`] / 1.5; - const volumeBtnFontSize = pref[`transportButtonSize_${pref.layout}`] / 1.33; - - const gridArtistFontSize = pref[`gridArtistFontSize_${pref.layout}`]; - const gridTrackNumFontSize = pref[`gridTrackNumFontSize_${pref.layout}`]; - const gridTitleFontSize = pref[`gridTitleFontSize_${pref.layout}`]; - const gridAlbumFontSize = pref[`gridAlbumFontSize_${pref.layout}`]; - const gridKeyFontSize = pref[`gridKeyFontSize_${pref.layout}`]; - const gridValueFontSize = pref[`gridValueFontSize_${pref.layout}`] + 1; - - const playlistFontSize = pref[`playlistFontSize_${pref.layout}`]; - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`]; - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`]; - const lyricsFontSize = pref[`lyricsFontSize_${pref.layout}`]; - - // * STYLE CHANGE * // - const artistTitle = pref.showGridArtist_default && pref.showGridTitle_default || pref.showGridArtist_artwork && pref.showGridTitle_artwork; - - // * TOP MENU BUTTONS * // - ft.top_menu = Font(fontTopMenu, menuFontSize, 0); - ft.top_menu_caption = Font(fontTopMenuCaption, menuCaptionFontSize, 0); - ft.top_menu_compact = Font(fontAwesome, menuFontSize, 0); - - // * LOWER BAR * // - ft.lower_bar_artist = Font(fontLowerBarArtist, lowerBarFontSize, pref.customThemeFonts ? g_font_style.bold : 0); - ft.lower_bar_title = Font(fontLowerBarTitle, lowerBarFontSize, 0); - ft.lower_bar_disc = Font(fontLowerBarDisc, lowerBarFontSize, 0); - ft.lower_bar_time = Font(fontLowerBarTime, lowerBarFontSize, pref.customThemeFonts ? g_font_style.bold : 0); - ft.lower_bar_length = Font(fontLowerBarLength, lowerBarFontSize, 0); - ft.lower_bar_wave = Font(fontLowerBarWave, lowerBarFontSize - 6, pref.customThemeFonts ? g_font_style.bold : 0); - - if (updateHyperlink) updateHyperlink.setFont(ft.lower_bar_title); - - // * LOWER BAR TRANSPORT BUTTONS * // - ft.guifx = Font(fontGuiFx, Math.floor(guiFxBtnFontSize), 0); - ft.pbo_default = Font(fontGuiFx, Math.floor(pboDefaultBtnFontSize), 0); - ft.pbo_repeat_playlist = Font(fontAwesome, Math.floor(pboReplayBtnFontSize), 0); - ft.pbo_repeat_track = Font(fontAwesome, Math.floor(pboReplayBtnFontSize), 0); - ft.pbo_shuffle = Font(fontGuiFx, Math.floor(pboShuffleBtnFontSize), 0); - ft.guifx_reload = Font(fontGuiFx, Math.floor(reloadBtnFontSize), 0); - ft.guifx_volume = Font(fontGuiFx, Math.floor(volumeBtnFontSize), 0); + // * ARTIST & LABEL LOGOS * // + artistlogos: `${fb.ProfilePath}georgia-reborn\\images\\artistlogos\\`, + artistlogosColor: `${fb.ProfilePath}georgia-reborn\\images\\artistlogos color\\`, + labelsBase: `${fb.ProfilePath}georgia-reborn\\images\\recordlabel\\`, // * MISC * // - ft.no_album_art_stub = Font(fontAwesome, 160, 0); - ft.symbol = Font(fontSegoeUISymbol, playlistFontSize, 0); - ft.notification = Font(fontNotification, notificationFontSize, 0); - ft.popup = Font(fontPopup, popupFontSize, 0); - ft.tooltip = Font(fontTooltip, tooltipFontSize, 0); - - if (pref.layout === 'compact') return; // These fonts below are not available in Compact layout, so skip these to prevent errors - - // * DETAILS METADATA GRID * // - ft.grd_artist = Font(fontGridArtist, gridArtistFontSize, pref.customThemeFonts ? g_font_style.bold : 0); - ft.grd_tracknum = Font(artistTitle ? fontGridTitle : fontGridTitleBold, gridTrackNumFontSize, 0); - ft.grd_title = Font(artistTitle ? fontGridTitle : fontGridTitleBold, gridTitleFontSize, 0); - ft.grd_album = Font(fontGridAlbum, gridAlbumFontSize, pref.customThemeFonts ? g_font_style.bold : 0); - ft.grd_key = Font(fontGridKey, gridKeyFontSize, 0); - ft.grd_val = Font(fontGridValue, gridValueFontSize, 0); - - // * LIBRARY * // - ft.library = Font(fontLibrary, libraryFontSize, 0); - - // * BIOGRAPHY * // - ft.biography = Font(fontBiography, biographyFontSize, 0); + flagsBase: `${fb.ProfilePath}georgia-reborn\\${grSet.customThemeImages ? 'images\\custom' : 'images'}\\flags\\`, + lastFmImageRed: `${fb.ProfilePath}georgia-reborn\\${grSet.customThemeImages ? 'images\\custom' : 'images'}\\misc\\last-fm-red-36.png`, + lastFmImageWhite: `${fb.ProfilePath}georgia-reborn\\${grSet.customThemeImages ? 'images\\custom' : 'images'}\\misc\\last-fm-36.png`, + + // * DISC ART * // + /** + * A collection of all disc art paths to search in. + * This method returns an array of paths with expressions evaluated by the title format helper. + * We expect disc art will be in .png with transparent background, best found at fanart.tv. + * @type {() => string[]} A function returning an array of string paths. + */ + discArtImagePaths() { + return [ + // * CD ART ( named cd1.png, cd2.png, etc. ) * // + $(`$directory_path(%path%)\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Root -> cd%discnumber%.png + $(`$directory_path(%path%)\\..\\Artwork\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Root Artwork -> cd%discnumber%.png + $(`$directory_path(%path%)\\..\\Images\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Root Images -> cd%discnumber%.png + $(`$directory_path(%path%)\\..\\Scans\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Root Scans -> cd%discnumber%.png + $(`$directory_path(%path%)\\Artwork\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Subfolder Artwork -> cd%discnumber%.png + $(`$directory_path(%path%)\\Images\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Subfolder Images -> cd%discnumber%.png + $(`$directory_path(%path%)\\Scans\\${grCfg.settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`), // Subfolder Scans -> cd%discnumber%.png + + // * CD ART ( named cd.png (or whatever custom value was specified). This is the most common single disc case. ) * // + $(`$directory_path(%path%)\\${grCfg.settings.discArtBasename}.png`), // Root -> cd.png + $(`$directory_path(%path%)\\..\\Artwork\\${grCfg.settings.discArtBasename}.png`), // Root Artwork -> cd.png + $(`$directory_path(%path%)\\..\\Images\\${grCfg.settings.discArtBasename}.png`), // Root Images -> cd.png + $(`$directory_path(%path%)\\..\\Scans\\${grCfg.settings.discArtBasename}.png`), // Root Scans -> cd.png + $(`$directory_path(%path%)\\Artwork\\${grCfg.settings.discArtBasename}.png`), // Subfolder Artwork -> cd.png + $(`$directory_path(%path%)\\Images\\${grCfg.settings.discArtBasename}.png`), // Subfolder Images -> cd.png + $(`$directory_path(%path%)\\Scans\\${grCfg.settings.discArtBasename}.png`), // Subfolder Scans -> cd.png + + // * VINYL DISC ART ( named vinylA.png, vinylB.png, etc. ) * // + $(`$directory_path(%path%)\\vinyl$if2(${grTF.vinyl_side},).png`), // Root -> vinyl side + $(`$directory_path(%path%)\\..\\Artwork\\vinyl$if2(${grTF.vinyl_side},).png`), // Root Artwork -> vinyl%vinyl disc%.png + $(`$directory_path(%path%)\\..\\Images\\vinyl$if2(${grTF.vinyl_side},).png`), // Root Images -> vinyl%vinyl disc%.png + $(`$directory_path(%path%)\\..\\Scans\\vinyl$if2(${grTF.vinyl_side},).png`), // Root Scans -> vinyl%vinyl disc%.png + $(`$directory_path(%path%)\\Artwork\\vinyl$if2(${grTF.vinyl_side},).png`), // Subfolder Artwork -> vinyl%vinyl disc%.png + $(`$directory_path(%path%)\\Images\\vinyl$if2(${grTF.vinyl_side},).png`), // Subfolder Images -> vinyl%vinyl disc%.png + $(`$directory_path(%path%)\\Scans\\vinyl$if2(${grTF.vinyl_side},).png`), // Subfolder Scans -> vinyl%vinyl disc%.png + + // * VINYL DISC ART ( named vinylA.png, vinylB.png, etc. ) * // + $('$directory_path(%path%)\\vinyl.png'), // Root -> vinyl.png + $('$directory_path(%path%)\\..\\Artwork\\vinyl.png'), // Root Artwork -> vinyl.png + $('$directory_path(%path%)\\..\\Images\\vinyl.png'), // Root Images -> vinyl.png + $('$directory_path(%path%)\\..\\Scans\\vinyl.png'), // Root Scans -> vinyl.png + $('$directory_path(%path%)\\Artwork\\vinyl.png'), // Subfolder Artwork -> vinyl.png + $('$directory_path(%path%)\\Images\\vinyl.png'), // Subfolder Images -> vinyl.png + $('$directory_path(%path%)\\Scans\\vinyl.png') // Subfolder Scans -> vinyl.png + ]; + }, + + /** + * A collection of all disc art stub paths to search in. + * This method returns an object where the key is the disc art stub name and the value is the path to the image. + * @type {() => { [key: string]: string }} A function returning an object with string keys and string values. + */ + discArtStubPaths() { + return { + cdAlbumCover: `${fb.ProfilePath}georgia-reborn\\images\\discart\\cd-transparent.png`, + cdWhite: `${fb.ProfilePath}georgia-reborn\\images\\discart\\cd-white.png`, + cdBlack: `${fb.ProfilePath}georgia-reborn\\images\\discart\\cd-black.png`, + cdBlank: `${fb.ProfilePath}georgia-reborn\\images\\discart\\cd-blank.png`, + cdTrans: `${fb.ProfilePath}georgia-reborn\\images\\discart\\cd-transparent.png`, + vinylAlbumCover: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-black-hole.png`, + vinylWhite: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-white.png`, + vinylVoid: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-void.png`, + vinylColdFusion: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-cold-fusion.png`, + vinylRingOfFire: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-ring-of-fire.png`, + vinylMaple: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-maple.png`, + vinylBlack: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-black.png`, + vinylBlackHole: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-black-hole.png`, + vinylEbony: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-ebony.png`, + vinylTrans: `${fb.ProfilePath}georgia-reborn\\images\\discart\\vinyl-transparent.png` + }; + }, + + // * CUSTOM DISC ART STUBS * // + discArtCustomStub: `${fb.ProfilePath}georgia-reborn\\images\\custom\\discart\\${grSet.discArtStub}.png`, + + /** + * Generates the path to the Hi-Res audio logo based on the resolution and shape preference. + * @returns {string} The path to the Hi-Res audio logo image. + */ + hiResAudioLogoPath() { + const plus4k = RES._4K ? '4K-' : ''; + const plusRound = grSet.hiResAudioBadgeRound ? '-round' : ''; + const imagePath = `${fb.ProfilePath}georgia-reborn\\${grSet.customThemeImages ? 'images\\custom' : 'images'}\\misc\\`; + + if (grSet.hiResAudioBadgeSize === 'small') { + return `${imagePath}${plus4k}hi-res-audio-small${plusRound}.png`; + } else if (grSet.hiResAudioBadgeSize === 'normal') { + return `${imagePath}${plus4k}hi-res-audio-normal${plusRound}.png`; + } else if (grSet.hiResAudioBadgeSize === 'large') { + return `${imagePath}${plus4k}hi-res-audio-large${plusRound}.png`; + } + + return `${imagePath}${plus4k}hi-res-audio-normal${plusRound}.png`; + }, // * LYRICS * // - ft.lyrics = Font(fontLyrics, lyricsFontSize, 1); - ft.lyricsHighlight = Font(fontLyrics, lyricsFontSize * 1.5, 1); -} - - -/////////////// -// * PATHS * // -/////////////// -/** @type {Object} The Georgia-ReBORN images path object. */ -const paths = {}; -/** @type {string} The Georgia-ReBORN custom/original images dir. */ -const images = pref.customThemeImages ? 'images\\custom' : 'images'; -/** @type {string} The Georgia-ReBORN images+custom path shortcut. */ -const imagesPath = `${fb.ProfilePath}georgia-reborn\\${images}\\`; -/** @type {string} The Georgia-ReBORN images base path shortcut. */ -const imagesPathBase = `${fb.ProfilePath}georgia-reborn\\images\\`; - -// We expect disc art will be in .png with transparent background, best found at fanart.tv. - -// * CD ART ( named cd1.png, cd2.png, etc. ) * // -pref.cdartdisc_path = `$directory_path(%path%)\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Root -> cd%discnumber%.png -pref.cdartdisc_path_artwork_root = `$directory_path(%path%)\\..\\Artwork\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Root Artwork -> cd%discnumber%.png -pref.cdartdisc_path_images_root = `$directory_path(%path%)\\..\\Images\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Root Images -> cd%discnumber%.png -pref.cdartdisc_path_scans_root = `$directory_path(%path%)\\..\\Scans\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Root Scans -> cd%discnumber%.png -pref.cdartdisc_path_artwork = `$directory_path(%path%)\\Artwork\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Subfolder Artwork -> cd%discnumber%.png -pref.cdartdisc_path_images = `$directory_path(%path%)\\Images\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Subfolder Images -> cd%discnumber%.png -pref.cdartdisc_path_scans = `$directory_path(%path%)\\Scans\\${settings.discArtBasename}$ifgreater(%totaldiscs%,1,%discnumber%,).png`; // Subfolder Scans -> cd%discnumber%.png - -// * CD ART ( named cd.png (or whatever custom value was specified). This is the most common single disc case. ) * // -pref.cdart_path = `$directory_path(%path%)\\${settings.discArtBasename}.png`; // Root -> cd.png -pref.cdart_path_artwork_root = `$directory_path(%path%)\\..\\Artwork\\${settings.discArtBasename}.png`; // Root Artwork -> cd.png -pref.cdart_path_images_root = `$directory_path(%path%)\\..\\Images\\${settings.discArtBasename}.png`; // Root Images -> cd.png -pref.cdart_path_scans_root = `$directory_path(%path%)\\..\\Scans\\${settings.discArtBasename}.png`; // Root Scans -> cd.png -pref.cdart_path_artwork = `$directory_path(%path%)\\Artwork\\${settings.discArtBasename}.png`; // Subfolder Artwork -> cd.png -pref.cdart_path_images = `$directory_path(%path%)\\Images\\${settings.discArtBasename}.png`; // Subfolder Images -> cd.png -pref.cdart_path_scans = `$directory_path(%path%)\\Scans\\${settings.discArtBasename}.png`; // Subfolder Scans -> cd.png - -// * VINYL DISC ART ( named vinylA.png, vinylB.png, etc. ) * // -pref.vinylside_path = `$directory_path(%path%)\\vinyl$if2(${tf.vinyl_side},).png`; // Root -> vinyl side -pref.vinylside_path_artwork_root = `$directory_path(%path%)\\..\\Artwork\\vinyl$if2(${tf.vinyl_side},).png`; // Root Artwork -> vinyl%vinyl disc%.png -pref.vinylside_path_images_root = `$directory_path(%path%)\\..\\Images\\vinyl$if2(${tf.vinyl_side},).png`; // Root Images -> vinyl%vinyl disc%.png -pref.vinylside_path_scans_root = `$directory_path(%path%)\\..\\Scans\\vinyl$if2(${tf.vinyl_side},).png`; // Root Scans -> vinyl%vinyl disc%.png -pref.vinylside_path_artwork = `$directory_path(%path%)\\Artwork\\vinyl$if2(${tf.vinyl_side},).png`; // Subfolder Artwork -> vinyl%vinyl disc%.png -pref.vinylside_path_images = `$directory_path(%path%)\\Images\\vinyl$if2(${tf.vinyl_side},).png`; // Subfolder Images -> vinyl%vinyl disc%.png -pref.vinylside_path_scans = `$directory_path(%path%)\\Scans\\vinyl$if2(${tf.vinyl_side},).png`; // Subfolder Scans -> vinyl%vinyl disc%.png - -// * VINYL DISC ART ( named vinylA.png, vinylB.png, etc. ) * // -pref.vinyl_path = '$directory_path(%path%)\\vinyl.png'; // Root -> vinyl.png -pref.vinyl_path_artwork_root = '$directory_path(%path%)\\..\\Artwork\\vinyl.png'; // Root Artwork -> vinyl.png -pref.vinyl_path_images_root = '$directory_path(%path%)\\..\\Images\\vinyl.png'; // Root Images -> vinyl.png -pref.vinyl_path_scans_root = '$directory_path(%path%)\\..\\Scans\\vinyl.png'; // Root Scans -> vinyl.png -pref.vinyl_path_artwork = '$directory_path(%path%)\\Artwork\\vinyl.png'; // Subfolder Artwork -> vinyl.png -pref.vinyl_path_images = '$directory_path(%path%)\\Images\\vinyl.png'; // Subfolder Images -> vinyl.png -pref.vinyl_path_scans = '$directory_path(%path%)\\Scans\\vinyl.png'; // Subfolder Scans -> vinyl.png - -// * CD ART STUBS * // -paths.cdArtWhiteStub = `${imagesPathBase}discart\\cd-white.png`; -paths.cdArtBlackStub = `${imagesPathBase}discart\\cd-black.png`; -paths.cdArtBlankStub = `${imagesPathBase}discart\\cd-blank.png`; -paths.cdArtTransStub = `${imagesPathBase}discart\\cd-transparent.png`; - -// * VINYL ART STUBS * // -paths.vinylArtWhiteStub = `${imagesPathBase}discart\\vinyl-white.png`; -paths.vinylArtVoidStub = `${imagesPathBase}discart\\vinyl-void.png`; -paths.vinylArtColdFusionStub = `${imagesPathBase}discart\\vinyl-cold-fusion.png`; -paths.vinylArtRingOfFireStub = `${imagesPathBase}discart\\vinyl-ring-of-fire.png`; -paths.vinylArtMapleStub = `${imagesPathBase}discart\\vinyl-maple.png`; -paths.vinylArtBlackStub = `${imagesPathBase}discart\\vinyl-black.png`; -paths.vinylArtBlackHoleStub = `${imagesPathBase}discart\\vinyl-black-hole.png`; -paths.vinylArtEbonyStub = `${imagesPathBase}discart\\vinyl-ebony.png`; -paths.vinylArtTransStub = `${imagesPathBase}discart\\vinyl-transparent.png`; - -// * CUSTOM ART STUBS * // -paths.discArtCustomStub = `${imagesPathBase}custom\\discart\\${pref.discArtStub}.png`; - -// * ARTIST & LABEL LOGOS * // -paths.artistlogos = `${imagesPathBase}artistlogos\\`; -paths.artistlogosColor = `${imagesPathBase}artistlogos color\\`; -paths.labelsBase = `${imagesPathBase}recordlabel\\`; - -// * MISC * // -paths.flagsBase = `${imagesPath}flags\\`; -paths.lastFmImageRed = `${imagesPath}misc\\last-fm-red-36.png`; -paths.lastFmImageWhite = `${imagesPath}misc\\last-fm-36.png`; - - -///////////////// -// * GLOBALS * // -///////////////// -/** @type {string} The string template for the vinyl track, with a fallback to track number if vinyl track information is not available. */ -globals.vinyl_track = `$if2(${tf.vinyl_side}[${tf.vinyl_tracknum}]. ,[%tracknumber%. ])`; - -/** @type {string[]} The array of label information fields, utilized in $meta() calls without '%' delimiters. */ -globals.labels = ['label', 'publisher', 'discogs_label']; - -/** @type {string[]} The array of paths for locating cached lyrics files in the user's system. */ -globals.lyr_path = [ - '$replace($replace(%path%,%filename_ext%,),,\\)', - `${fb.ProfilePath}cache\\lyrics\\`, - `${fb.FoobarPath}cache\\lyrics\\` -]; - - -///////////////// -// * ARTWORK * // -///////////////// -/** @type {ArtCache} The cached images from album art. */ -let artCache; -/** @type {GdiBitmap} The big album art image displayed on the left side. */ -let albumArt = null; -/** @type {array} The album art list array containing album and disc art images. */ -let albumArtList = []; -/** @type {number} The index of currently displayed album art if more than 1. */ -let albumArtIndex = 0; -/** @type {Object} The album art position ( big image ). */ -let albumArtSize = new ImageSize(0, 0, 0, 0); -/** @type {GdiBitmap} The pre-scaled album art to speed up drawing considerably. */ -let albumArtScaled = null; -/** @type {GdiBitmap} The copy of the original album art image, used for cropping. */ -let albumArtCopy = null; -/** @type {boolean} The off-center position of the album art, if true, it will shift 40 pixels to the right. */ -let artOffCenter = false; -/** @type {boolean} The state when artwork displayed is embedded and not loaded from a file. */ -let embeddedArt = false; -/** @type {boolean} The state to always load art from cache unless this is set. */ -let loadFromCache = true; -/** @type {boolean} The state when album art or disc art has artwork loaded. */ -let hasArtwork = false; -/** @type {boolean} The "no album art stub" when no album cover was found. */ -let noAlbumArtStub = false; -/** @type {GdiBitmap} The disc art image used in Details. */ -let discArt = null; -/** @type {GdiBitmap} The disc art album cover image used in Details. */ -let discArtCover = null; -/** @type {GdiBitmap[]} The array of disc art images used in Details. */ -let discArtArray = []; -/** @type {GdiBitmap[]} The array of disc art album cover images used in Details. */ -let discArtArrayCover = []; -/** @type {boolean} The state when disc art was found on hard drive used in Details. */ -let discArtFound = false; -/** @type {Object} The disc art position used in Details ( offset from albumArtSize ). */ -let discArtSize = new ImageSize(0, 0, 0, 0); -/** @type {GdiBitmap} The rotated disc art from the RotateImg helper used in Details. */ -let discArtRotation = null; -/** @type {GdiBitmap} The rotated disc art album cover from the RotateImg helper used in Details. */ -let discArtRotationCover = null; -/** @type {number} The global index of current discArtArray img to draw used in Details. */ -let discArtRotationIndex = 0; -/** @type {number} The global index of current discArtArrayCover img to draw used in Details. */ -let discArtRotationIndexCover = 0; -/** @type {GdiBitmap[]} The array of record label images used in Details. */ -let recordLabels = []; -/** @type {GdiBitmap[]} The array of inverted record label images used in Details. */ -let recordLabelsInverted = []; -/** @type {GdiBitmap} The band logo image used in Details. */ -let bandLogo = null; -/** @type {GdiBitmap} The inverted band logo image shown in Details. */ -let invertedBandLogo = null; -/** @type {GdiBitmap[]} The array of flag images shown in Details and in the lower bar. */ -let flagImgs = []; -/** @type {GdiBitmap} The release country flag image shown in the metadata grid in Details. */ -let releaseFlagImg = null; -/** @type {GdiBitmap} The codec logo image shown in the metadata grid in Details. */ -let codecLogo = null; -/** @type {GdiBitmap} The channel logo image shown in the metadata grid in Details. */ -let channelLogo = null; -/** @type {GdiBitmap} The Hi-Res Audio badge logo image shown on album art when enabled. */ -let hiResAudioImg = null; -/** @type {boolean} The last.fm logo image displayed when we %lastfm_play_count% > 0, shown in the metadata grid in Details. */ -let playCountVerifiedByLastFm = false; -/** @type {GdiBitmap} The shadow behind labels used in Details. */ -let labelShadowImg = null; -/** @type {GdiBitmap} The shadow behind the artwork + discart used in Details. */ -let shadowImg = null; + lyricsPath: [ + '$replace($replace(%path%,%filename_ext%,),,\\)', + `${fb.ProfilePath}cache\\lyrics\\`, + `${fb.FoobarPath}cache\\lyrics\\` + ] +}; /////////////// -// * STATE * // +// * FONTS * // /////////////// -/** @param {number} width window.Width. */ -let ww = 0; -/** @param {number} height window.Height. */ -let wh = 0; -/** @param {number} top The metadata grid top position. */ -let gridTop = 0; -/** @type {ProgressBar} Creates the progress bar object. */ -let progressBar = null; -/** @type {WaveformBar} Creates the waveform bar object. */ -let waveformBar = null; -/** @type {PeakmeterBar} Creates the peakmeter bar object. */ -let peakmeterBar = null; -/** @type {JumpSearch} Creates the jump search object. */ -let jumpSearch = null; -/** @type {PlaylistHistory} Creates the playlist history object. */ -let playlistHistory; -/** @type {boolean} The Playlist history state, used for playlist scroll. */ -let playlistHistoryUsed; -/** @type {boolean} The display state of the Playlist panel. */ -let displayPlaylist = false; -/** @type {boolean} The display state of the Playlist panel in Artwork layout. */ -let displayPlaylistArtwork = false; -/** @type {boolean} The display state of the Details panel. */ -let displayDetails = pref.showPanelOnStartup === 'details'; -/** @type {boolean} The display state of the Library panel. */ -let displayLibrary = false; -/** @type {boolean} The display state of the Biography panel. */ -let displayBiography = false; -/** @type {number} The left edge of the record labels in Details. Saved so we don't have to recalculate every on every on_paint unless size has changed. */ -let lastLeftEdge = 0; -/** @type {number} The last label height of the record labels in Details. Saved so we don't have to recalculate every on every on_paint unless size has changed. */ -let lastLabelHeight = 0; -/** @type {number} The first played ratio used on the timeline in Details. */ -let timelineFirstPlayedRatio = 0; -/** @type {number} The last played ratio used on the timeline in Details. */ -let timelineLastPlayedRatio = 0; -/** @type {string} The path of the current playing album directory, used for art caching purposes on_playback_new_track. */ -let currentAlbumFolder; -/** @type {string} The path of the last played album directory, used for art caching purposes on_playback_new_track. */ -let lastAlbumFolder; -/** @type {string} %album% tag of the current playing album, used for art caching purposes on_playback_new_track. */ -let lastAlbumFolderTag; -/** @type {string} The disc number of the last played album directory, used for art caching purposes on_playback_new_track. */ -let lastAlbumDiscNumber; -/** @type {string} The vinyl side of the last played album, used for art caching purposes on_playback_new_track. */ -let lastAlbumVinylSide; -/** @type {string} The date and time of the current last played album, used for art caching purposes on_playback_new_track. */ -let currentLastPlayed = ''; -/** @type {string} Displays the active playlist of the current playing track in the metadata grid in Details. */ -let playingPlaylist = ''; -/** @type {number} Saves last playback order. */ -let lastPlaybackOrder; -/** @type {boolean} Saves active full width lyrics layout via pref.lyricsLayout. */ -let lyricsLayoutFullWidth; -/** @type {boolean} Is the song from a streaming source? */ -let isStreaming = false; -/** @type {boolean} Is the song playing from a CD? */ -let isPlayingCD = false; -/** @type {boolean} Used to restore theme state after custom [%GR_THEME%] or [%GR_STYLE%] or [%GR_PRESET%] usage. */ -let themeRestoreState = false; -/** @type {boolean} When active styles match any theme presets, used for the notification popup in the showThemePresetIndicator. */ -let themePresetMatchMode = false; -/** @type {boolean} Used to hide theme preset indicator under certain conditions. */ -let themePresetIndicator = true; -/** @type {string} The name of the current theme preset. */ -let themePresetName = ''; -/** @type {string} The text of the theme notification. */ -let themeNotification = ''; -/** @type {boolean} When no artwork, don't set themeColor every redraw. */ -let themeColorSet = false; -/** @type {boolean} The state to override condition in getRandomThemeColor() when using "Generate new color" from context menu. */ -let getRandomThemeColorContextMenu = false; -/** @type {boolean} Only use default theme when noArtwork was found. */ -let noArtwork = false; -/** @type {boolean} Only load theme colors when newTrackFetchingArtwork = true. */ -let newTrackFetchingArtwork = false; -/** @type {boolean} The state when new album art / disc art loaded and other things finished, used for smoother Playlist auto-scrolling. */ -let newTrackFetchingDone = false; -/** @type {boolean} The state when Library should not call window.Reload() from panel.set() -> panel.load(), i.e when saving theme settings or restoring theme backup. */ -let libraryCanReload = true; -/** @type {boolean} The state if initTheme() needs to be fully executed to save performance. */ -let initThemeFull = false; -/** @type {boolean} The state to skip most initTheme() and get getThemeColors(albumArt), mostly used for theme presets to prevent double inits. */ -let initThemeSkip = false; -/** @type {boolean} The state when the theme is loading on startup or reload. */ -let loadingTheme = false; -/** @type {boolean} The state when the theme has completely loaded, used for pseudo delay background logo mask on startup or reload. */ -let loadingThemeComplete = false; - - -////////////// -// * MENU * // -////////////// -/** @type {number} The menu start index, can be anything except 0. */ -const menuStartIndex = 100; -/** @type {Menu} The menu item index. */ -let _MenuItemIndex = menuStartIndex; -/** @type {Menu} SMP does not yet have support for "fields" and so we cannot create static members shared across all classes. */ -let _MenuCallbacks = []; -/** @type {Menu} We must use these ugly shared globals instead. */ -let _MenuVariables = []; - - -///////////////////// -// * CUSTOM MENU * // -///////////////////// -/** @type {BaseControl} The customMenu object. */ -let customMenu; -/** @type {boolean} The custom theme menu was called, used only when customThemeMenu Biography button was used and then top menu Details button. */ -let customThemeMenuCall; -/** @type {Object} Used to change prefix and switch colors between custom theme 1-10. */ -let customColor = customTheme01; -/** @type {boolean} The display state of custom theme menu when using Options > Theme > Edit custom theme. */ -let displayCustomThemeMenu = false; -/** @type {boolean} The display state of the metadata grid menu. */ -let displayMetadataGridMenu = false; -/** @type {Object} The control list object used in custom theme menu. */ -let controlList = []; -/** @type {Object} The active control object used in custom theme menu. */ -let activeControl; -/** @type {Object} The hovered control object used in custom theme menu. */ -let hoveredControl; -/** @type {boolean} The mouse button pressed state, used in custom theme menu. */ -let mouseDown = false; - - -///////////////////////// -// * BUTTONS & MOUSE * // -///////////////////////// -/** @type {ButtonEventHandler} The topMenu object. */ -let topMenu; -/** @type {boolean} The top menu compact state, used to cancel top menu compact collapse when mouse is again in top menu area. */ -let topMenuCompactExpanded = false; -/** @type {PauseButton} The album art pause button. */ -let pauseBtn; -/** @type {VolumeBtn} The lower bar volume button. */ -let volumeBtn; -/** @type {Object} The theme button object. */ -let btns = {}; -/** @type {GdiGraphics} The theme button images. */ -let btnImg; -/** @type {boolean} The top menu and contextual menu state, is it open ( active ) or not. */ -let activeMenu = false; -/** @type {boolean} The double click mouse state. */ -let doubleClicked = false; -/** @type {Object} The mouse move position state. */ -let state = {}; +/** + * A collection of theme fonts that are assigned at runtime in the `createFonts` method. + * @typedef {object} grFont - The Georgia-ReBORN font object. + * @property {string} fontDefault - The 'Segoe UI' font name. + * @property {string} fontSegoeUISymbol - The 'Segoe UI Symbol' font name. + * @property {string} fontTopMenu - The 'Segoe UI Semibold' font name. + * @property {string} fontTopMenuCaption - The 'Marlett' font name. + * @property {string} fontGuiFx - The 'Guifx v2 Transports' font name. + * @property {string} fontAwesome - The 'FontAwesome' font name. + * @property {string} fontLowerBarArtist - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontLowerBarTitle - The 'HelveticaNeueLT Pro 45 Lt' font name. + * @property {string} fontLowerBarDisc - The 'HelveticaNeueLT Pro 45 Lt' font name. + * @property {string} fontLowerBarTime - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontLowerBarLength - The 'HelveticaNeueLT Pro 45 Lt' font name. + * @property {string} fontLowerBarWave - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontNotification - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontPopup - The 'Segoe UI' font name. + * @property {string} fontTooltip - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontGridArtist - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontGridTitle - The 'HelveticaNeueLT Pro 45 Lt' font name. + * @property {string} fontGridTitleBold - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontGridAlbum - The 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontGridKey - The 'HelveticaNeueLT Pro 75 Bd' or 'HelveticaNeueLT Pro 65 Md' font name. + * @property {string} fontGridValue - The 'HelveticaNeueLT Pro 45 Lt' font name. + * @property {string} fontLibrary - The 'Segoe UI' font name. + * @property {string} fontBiography - The 'Segoe UI' font name. + * @property {string} fontLyrics - The 'Segoe UI' font name. + * @property {GdiFont} topMenu - Theme font 'Segoe UI Semibold' used for top menu buttons. + * @property {GdiFont} topMenuCaption - Theme font 'Marlett' used for top menu 🗕 🗖 ✖ caption buttons. + * @property {GdiFont} topMenuCompact - Theme font 'FontAwesome' used for the top menu compact button. + * @property {GdiFont} lowerBarArtist - Theme artist font 'HelveticaNeueLT Pro 65 Md' used in lower bar. + * @property {GdiFont} lowerBarTitle - Theme title font 'HelveticaNeueLT Pro 45 Lt' used in lower bar. + * @property {GdiFont} lowerBarDisc - Theme disc font 'HelveticaNeueLT Pro 45 Lt' used in lower bar. + * @property {GdiFont} lowerBarTime - Theme time font 'HelveticaNeueLT Pro 65 Md' used in lower bar. + * @property {GdiFont} lowerBarLength - Theme length font 'HelveticaNeueLT Pro 45 Lt' used in lower bar. + * @property {GdiFont} lowerBarWave - Theme waveform bar font 'HelveticaNeueLT Pro 65 Md' used in lower bar. + * @property {GdiFont} guifx - Theme font 'Guifx v2 Transports' used for the lower bar transport/playback buttons. + * @property {GdiFont} pboDefault - Theme font 'Guifx v2 Transports' used for the lower bar transport playback order button. + * @property {GdiFont} pboRepeatPlaylist - Theme font 'FontAwesome' used for the lower bar transport playback order button. + * @property {GdiFont} pboRepeatTrack - Theme font 'FontAwesome' used for the lower bar transport playback order button. + * @property {GdiFont} pboShuffle - Theme font 'Guifx v2 Transports' used for the lower bar transport playback order button. + * @property {GdiFont} guifxReload - Theme font 'Guifx v2 Transports' used for the lower bar transport reload button. + * @property {GdiFont} guifxAddTrack - Theme font 'Guifx v2 Transports' used for the lower bar transport add tracks button. + * @property {GdiFont} guifxVolume - Theme font 'Guifx v2 Transports' used for the lower bar transport volume button. + * @property {GdiFont} noAlbumArtStub - Theme font 'FontAwesome' used for no album art music note symbol. + * @property {GdiFont} symbol - Panel font 'Segoe UI Symbol' used for special chars, scrollbar buttons, etc. + * @property {GdiFont} notification - Theme font 'HelveticaNeueLT Pro 65 Md' used for notifications. + * @property {GdiFont} popup - Theme font 'Segoe UI' used for popups. + * @property {GdiFont} tooltip - Theme font 'HelveticaNeueLT Pro 65 Md' used for styled tooltips. + * @property {GdiFont} gridArtist - Theme font 'HelveticaNeueLT Pro 65 Md' used for metadata grid artist. + * @property {GdiFont} gridTrackNumber - Theme font 'HelveticaNeueLT Pro 45 Lt' or 'HelveticaNeueLT Pro 65 Md' used for metadata grid track number. + * @property {GdiFont} gridTitle - Theme font 'HelveticaNeueLT Pro 45 Lt' or 'HelveticaNeueLT Pro 65 Md' used for metadata grid title. + * @property {GdiFont} gridAlbum - Theme font 'HelveticaNeueLT Pro 65 Md' used for metadata grid album. + * @property {GdiFont} gridKey - Theme font 'HelveticaNeueLT Pro 75 Bd' or 'HelveticaNeueLT Pro 65 Md' used for metadata grid key. + * @property {GdiFont} gridVal - Theme font 'HelveticaNeueLT Pro 45 Lt' used for metadata grid value. + * @property {GdiFont} library - Panel font 'Segoe UI' used in the Library. + * @property {GdiFont} biography - Panel font 'Segoe UI' used in the Biography. + * @property {GdiFont} lyrics - Panel font 'Segoe UI' used in the Lyrics. + * @property {GdiFont} lyricsHighlight - Panel font 'Segoe UI' used in Lyrics for synced lines. + */ +/** @global @type {grFont} */ +const grFont = { + fontDefault: grSet.customThemeFonts ? grCfg.customFont.fontDefault : 'Segoe UI', + fontSegoeUISymbol: 'Segoe UI Symbol', + fontTopMenu: grSet.customThemeFonts ? grCfg.customFont.fontTopMenu : 'Segoe UI Semibold', + fontTopMenuCaption: 'Marlett', + + fontGuiFx: 'Guifx v2 Transports', + fontAwesome: 'FontAwesome', + fontLowerBarArtist: grSet.customThemeFonts ? grCfg.customFont.fontLowerBarArtist : 'HelveticaNeueLT Pro 65 Md', + fontLowerBarTitle: grSet.customThemeFonts ? grCfg.customFont.fontLowerBarTitle : 'HelveticaNeueLT Pro 45 Lt', + fontLowerBarDisc: grSet.customThemeFonts ? grCfg.customFont.fontLowerBarDisc : 'HelveticaNeueLT Pro 45 Lt', + fontLowerBarTime: grSet.customThemeFonts ? grCfg.customFont.fontLowerBarTime : 'HelveticaNeueLT Pro 65 Md', + fontLowerBarLength: grSet.customThemeFonts ? grCfg.customFont.fontLowerBarLength : 'HelveticaNeueLT Pro 45 Lt', + fontLowerBarWave: grSet.customThemeFonts ? grCfg.customFont.fontLowerBarWave : 'HelveticaNeueLT Pro 65 Md', + + fontNotification: grSet.customThemeFonts ? grCfg.customFont.fontNotification : 'HelveticaNeueLT Pro 65 Md', + fontPopup: grSet.customThemeFonts ? grCfg.customFont.fontPopup : 'Segoe UI', + fontTooltip: grSet.customThemeFonts ? grCfg.customFont.fontTooltip : 'HelveticaNeueLT Pro 65 Md', + + fontGridArtist: grSet.customThemeFonts ? grCfg.customFont.fontGridArtist : 'HelveticaNeueLT Pro 65 Md', + fontGridTitle: grSet.customThemeFonts ? grCfg.customFont.fontGridTitle : 'HelveticaNeueLT Pro 45 Lt', + fontGridTitleBold: grSet.customThemeFonts ? grCfg.customFont.fontGridTitleBold : 'HelveticaNeueLT Pro 65 Md', + fontGridAlbum: grSet.customThemeFonts ? grCfg.customFont.fontGridAlbum : 'HelveticaNeueLT Pro 65 Md', + fontGridKey: grSet.customThemeFonts ? grCfg.customFont.fontGridKey : + Detect.Wine ? 'HelveticaNeueLT Pro 65 Md' : 'HelveticaNeueLT Pro 75 Bd', + fontGridValue: grSet.customThemeFonts ? grCfg.customFont.fontGridValue : 'HelveticaNeueLT Pro 45 Lt', + + fontLibrary: grSet.customThemeFonts ? grCfg.customFont.fontLibrary : 'Segoe UI', + fontBiography: grSet.customThemeFonts ? grCfg.customFont.fontBiography : 'Segoe UI', + fontLyrics: grSet.customThemeFonts ? grCfg.customFont.fontLyrics : 'Segoe UI' +}; ///////////////// -// * UIHACKS * // +// * STRINGS * // ///////////////// -/** @type {boolean} UIHacks pseudo caption state, used in setWindowDrag. */ -let pseudoCaption; -/** @type {number} UIHacks sets pseudo caption width when dragging foobar, used in setWindowDrag. */ -let pseudoCaptionWidth; -/** Preferences > Display > Main Window > Frame style: No border. */ -UIHacks.FrameStyle = 3; -/** Preferences > Display > Main Window > Move with: Any method. */ -UIHacks.MoveStyle = 3; -/** Preferences > Display > Main Window > Aero effects: Glass frame. */ -UIHacks.Aero.Effect = 2; -/** Preferences > Display > Main Window > Aero top: 1 px fix for window drop shadow. */ -UIHacks.Aero.Top = 1; -/** Preferences > Display > Main Window > Constraints: Disable window maximization. */ -UIHacks.BlockMaximize = false; +/** + * A collection of strings and other objects to be displayed throughout UI. + * @typedef {object} grStr - The Georgia-ReBORN string object. + * @property {string} artist - The artist will be shown in Details and in the lower bar. + * @property {string} composer - The composer will be shown in the lower bar if it exist and enabled. + * @property {string} album - The album will be shown in Details. + * @property {string} album_subtitle - Currently not used in the theme. + * @property {string} disc - By default, this string is displayed in the lower bar if there is more than one total disc. Formatted like: "CD1/2". + * @property {Array} grid - The metadata grid strings in Details. + * @property {string} length - The length of the song in MM:SS format. + * @property {string} original_artist - If %original artist% exist it will be displayed by the right side of the title in the lower bar. + * @property {string} time - The current time of the song in MM:SS format in the lower bar. + * @property {string} title - The title of the song. + * @property {string} titleLower - The title of the song to be displayed above the progress bar. Can include more information such as translation, original artist, etc. + * @property {string} trackInfo - Currently not used in the theme. + * @property {string} tracknum - The track number of the song. + * @property {string} year - Currently not used in the theme. + */ +/** @global @type {grStr} */ +const grStr = { + artist: '', + composer: '', + album: '', + album_subtitle: '', + disc: '', + grid: [], + length: '', + original_artist: '', + time: '', + title: '', + titleLower: '', + trackInfo: '', + tracknum: '', + year: '' +}; //////////////// -// * TIMERS * // +// * COLORS * // //////////////// -/** @type {number} The setTimeout ID for cycling album art. */ -let albumArtTimeout; -/** @type {number} The timer when disc art spins while song is playing. */ -let discArtRotationTimer; -/** @type {number} The setTimeout ID for hiding cursor. */ -let hideCursorTimeout; -/** @type {number} The timer of progress bar. */ -let progressBarTimer; -/** @type {number} The timer interval between progress bar updates. */ -let progressBarTimerInterval; -/** @type {number} The timer for style auto random preset. */ -let presetAutoRandomModeTimer; -/** @type {number} The timer for theme preset indicator. */ -let presetIndicatorTimer; -/** @type {number} The timer for style auto color in Random theme. */ -let randomThemeAutoColorTimer; -/** @type {number} The 10 minute timer for theme day/night mode. */ -let themeDayNightModeTimer; - - -///////////////// -// * TOOLTIP * // -///////////////// -/** @type {FbTooltip} The tooltip object. */ -let g_tooltip; -/** @type {TooltipTimer} The tooltip timer. */ -let g_tooltip_timer; -/** @type {TooltipHandler} The tooltip object. */ -let tt; -/** @type {string} The tooltip text handler for styled tooltip. */ -let styledTooltipText; -/** @type {boolean} The draw state of styled tooltip. */ -let styledTooltipReady; - - -/////////////// -// * DEBUG * // -/////////////// -/** @type {number} Shows the image alpha in showThemeDebugOverlay. */ -let blendedImgAlpha; -/** @type {number} Shows the image blur in showThemeDebugOverlay. */ -let blendedImgBlur; -/** @type {string} Shows the col.primary in showThemeDebugOverlay. */ -let selectedPrimaryColor; -/** @type {string} Shows the col.primary_alt in showThemeDebugOverlay. */ -let selectedPrimaryColor2; -/** @type {array} Used in drawDebugRectAreas(). */ -let repaintRects = []; -/** @type {number} Used in RepaintRectAreas(). */ -let repaintRectCount = 0; -/** @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ -let trace_call = false; -/** @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ -let trace_on_paint = false; -/** @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ -let trace_on_move = false; -/** @type {boolean} DO NOT CHANGE, can be activated via Options > Developer tools. */ -let trace_initialize_list_performance = false; - -window.oldRepaintRect = window.RepaintRect; - /** - * @typedef {Object} TimingsObj - * @property {boolean} showDebugTiming Spams the console with debug timings. - * @property {boolean} showDrawTiming Spams the console with draw times. - * @property {boolean} showExtraDrawTiming Spams the console with every section of the draw code to determine bottlenecks. - * @property {boolean} showRamUsage Spams the console with memory statistic. - * @property {boolean} drawRepaintRects Draws all window.RepaintRect as red outlines in the theme. - * @property {boolean} showPanelTraceCall Spams the console with panel trace call. - * @property {boolean} showPanelTraceOnMove Spams the console with panel trace on move. - * @property {boolean} showPlaylistTraceListPerf Spams the console with playlist list performance. + * A collection of main colors and states used throughout the theme. + * @typedef {object} grCol - The Georgia-ReBORN color object. + * @property {number} darkAccent - The primary color shaded by 30%. + * @property {number} darkAccent_alt - The secondary primary color shaded by 30%. + * @property {number} accent - The primary color shaded by 15%. + * @property {number} accent_alt - The secondary primary color shaded by 15%. + * @property {number} primary - The primary theme color generated from artwork. + * @property {number} primary_alt - The secondary primary theme color generated from artwork. + * @property {number} lightAccent - The primary color tinted by 20%. + * @property {number} lightAccent_alt - The secondary primary color tinted by 20%. + * @property {number} artist - The color of artist text on background. + * @property {number} bg - The background of the main panel. + * @property {number} rating - The color of rating stars in metadata grid. + * @property {number} hotness - The color of hotness text in metadata grid. + * @property {number} timelineAdded - The background color for timeline block in Details from added to first played. + * @property {number} timelinePlayed - The background color for timeline block in Details from first played to last played. + * @property {number} timelineUnplayed - The background color for timeline block in Details from last played to present time. + * @property {number} progressBar - The background of the progress bar. Fill will be col.primary. + * @property {number} shadow - The color of the shadow. + * @property {number} colBrightness - Calculated primary color brightness used in pref.theme === 'white, pref.theme === 'black, pref.theme === 'reborn, pref.theme === 'random. + * @property {number} colBrightness2 - Calculated secondary color brightness used in pref.styleRebornFusion, pref.styleRebornFusion2, pref.styleRebornFusionAccent. + * @property {number} imgBrightness - Calculated image brightness used in pref.styleBlend, pref.styleBlend2, pref.styleBlackAndWhite, pref.styleBlackAndWhite2, pref.styleBlackAndWhiteReborn. + * @property {GdiBitmap} imgBlended - Blended image from grMain.color.setStyleBlend(). + * @property {boolean} isColored - Checks if background color is not full white RGB(255, 255, 255), used in Reborn/Random theme when init on start or when noAlbumArtStub displayed. + * @property {boolean} lightBg - Color definition when to switch text and logos to white or black, used in pref.theme === 'white', pref.theme === 'black', pref.theme === 'reborn', pref.theme === 'random', pref.styleBlend, pref.styleBlend2. + * @property {boolean} lightBgLib - Color definition when to switch text and logos to white or black, used in libSet.theme === 1 - 5. + * @property {boolean} lightBgBio - Color definition when to switch text and logos to white or black, used in bioSet.theme === 1 - 4. + * @property {boolean} lightBgMain - Color definition for col.bg when to lighten or darken custom theme colors, used in pref.theme === 'custom01' - 'custom10'. + * @property {boolean} lightBgPlaylist - Color definition for pl.col.bg when to lighten or darken custom theme colors, used in pref.theme === 'custom01' - 'custom10'. + * @property {boolean} lightBgDetails - Color definition for col.detailsBg) when to lighten or darken custom theme colors, used in pref.theme === 'custom01' - 'custom10'. + * @property {boolean} lightBgLibrary - Color definition for ui.col.bg when to lighten or darken custom theme colors, used in pref.theme === 'custom01' - 'custom10'. + * @property {boolean} lightBgBiography - Color definition for bio.ui.col.bg when to lighten or darken custom theme colors, used in pref.theme === 'custom01' - 'custom10'. */ - -/** @type {TimingsObj} */ -const timings = { - showDebugTiming: false, - showDrawTiming: false, - showExtraDrawTiming: false, - showRamUsage: false, - drawRepaintRects: false, - showPanelTraceCall: false, - showPanelTraceOnMove: false, - showPlaylistTraceListPerf: false +/** @global @type {grCol} */ +const grCol = { + colBrightness: 0, + colBrightness2: 0, + imgBrightness: 0, + imgBlended: null, + isColored: false, + lightBg: false, + lightBgLib: false, + lightBgBio: false, + lightBgMain: false, + lightBgPlaylist: false, + lightBgDetails: false, + lightBgLibrary: false, + lightBgBiography: false }; + + +/////////////////////////////// +// ! CONFIG INITIALIZATION ! // +/////////////////////////////// +grCfg.initializeConfigs(); +grCfg.migrateCheck(grCfg.currentVersion, grCfg.configVersion); diff --git a/profile/georgia-reborn/scripts/Base/gr-theme-colors.js b/profile/georgia-reborn/scripts/Base/gr-theme-colors.js new file mode 100644 index 00000000..4e109ae1 --- /dev/null +++ b/profile/georgia-reborn/scripts/Base/gr-theme-colors.js @@ -0,0 +1,6550 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Themes * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +////////////////////// +// * THEME COLORS * // +////////////////////// +/** + * A class that provides the full collection of all theme colors and its methods. + */ +class ThemeColors { + /** + * Creates the `ThemeColors` instance and initializes theme preference values. + */ + constructor() { + /** + * The default colors for White theme used in Options > Theme > White. + * @type {object} + * @public + */ + this.whiteTheme = { + name: 'white', + colors: { + primary: RGB(25, 160, 240), + darkAccent: RGB(12, 144, 245), + accent: RGB(12, 137, 232), + lightAccent: RGB(10, 130, 220) + } + }; + + /** + * The default colors for Black theme used in Options > Theme > Black. + * @type {object} + * @public + */ + this.blackTheme = { + name: 'black', + colors: { + primary: RGB(175, 205, 225), + darkAccent: RGB(160, 160, 160), + accent: RGB(180, 180, 180), + lightAccent: RGB(220, 220, 220) + } + }; + + /** + * The default colors for Reborn theme used in Options > Theme > Reborn. + * @type {object} + * @public + */ + this.rebornTheme = { + name: 'reborn', + colors: { + primary: RGB(90, 90, 90), + primary_alt: RGB(90, 90, 90), + darkAccent: RGB(60, 60, 60), + accent: RGB(80, 80, 80), + lightAccent: RGB(100, 100, 100) + } + }; + + /** + * The default colors for Random theme used in Options > Theme > Random. + * @type {object} + * @public + */ + this.randomTheme = { + name: 'random', + colors: { + primary: RGB(65, 65, 65), + darkAccent: RGB(60, 60, 60), + accent: RGB(80, 80, 80), + lightAccent: RGB(100, 100, 100) + } + }; + + /** + * The default colors for Blue theme used in Options > Theme > Blue. + * @type {object} + * @public + */ + this.blueTheme = { + name: 'blue', + colors: { + primary: RGB(10, 115, 200), + darkAccent: RGB(12, 144, 245), + accent: RGB(12, 137, 232), + lightAccent: RGB(10, 130, 220) + } + }; + + /** + * The default colors for Dark blue theme used in Options > Theme > Dark blue. + * @type {object} + * @public + */ + this.darkblueTheme = { + name: 'darkBlue', + colors: { + primary: RGB(21, 37, 56), + darkAccent: RGB(31, 65, 107), + accent: RGB(27, 58, 94), + lightAccent: RGB(24, 50, 82) + } + }; + + /** + * The default colors for Red theme used in Options > Theme > Red. + * @type {object} + * @public + */ + this.redTheme = { + name: 'red', + colors: { + primary: RGB(110, 20, 20), + darkAccent: RGB(156, 30, 30), + accent: RGB(143, 27, 27), + lightAccent: RGB(130, 25, 25) + } + }; + + /** + * The default colors for Cream theme used in Options > Theme > Cream. + * @type {object} + * @public + */ + this.creamTheme = { + name: 'cream', + colors: { + primary: RGB(255, 247, 240), + darkAccent: RGB(120, 170, 130), + accent: RGB(130, 184, 141), + lightAccent: RGB(139, 196, 151) + } + }; + + /** + * The default colors for Neon blue theme used in Options > Theme > Neon blue. + * @type {object} + * @public + */ + this.nblueTheme = { + name: 'neon blue', + colors: { + primary: RGB(10, 10, 10), + darkAccent: RGB(30, 30, 30), + accent: RGB(40, 40, 40), + lightAccent: RGB(50, 50, 50) + } + }; + + /** + * The default colors for Neon green theme used in Options > Theme > Neon green. + * @type {object} + * @public + */ + this.ngreenTheme = { + name: 'neon green', + colors: { + primary: RGB(10, 10, 10), + darkAccent: RGB(30, 30, 30), + accent: RGB(40, 40, 40), + lightAccent: RGB(50, 50, 50) + } + }; + + /** + * The default colors for Neon red theme used in Options > Theme > Neon red. + * @type {object} + * @public + */ + this.nredTheme = { + name: 'neon red', + colors: { + primary: RGB(10, 10, 10), + darkAccent: RGB(30, 30, 30), + accent: RGB(40, 40, 40), + lightAccent: RGB(50, 50, 50) + } + }; + + /** + * The default colors for Neon gold theme used in Options > Theme > Neon gold. + * @type {object} + * @public + */ + this.ngoldTheme = { + name: 'neon gold', + colors: { + primary: RGB(10, 10, 10), + darkAccent: RGB(30, 30, 30), + accent: RGB(40, 40, 40), + lightAccent: RGB(50, 50, 50) + } + }; + + /** + * The default colors for Custom theme used in Options > Theme > Custom. + * @type {object} + * @public + */ + this.customTheme = { + name: 'custom', + colors: { + primary: RGB(50, 25, 70), + darkAccent: RGB(30, 15, 45), + accent: RGB(65, 35, 95), + lightAccent: RGB(75, 40, 110) + } + }; + + this.initThemePrefVals(); + } + + // * PUBLIC METHODS - WHITE THEME * // + // #region PUBLIC METHODS - WHITE THEME + /** + * The Playlist colors for White theme used in Options > Theme > White. + */ + playlistColorsWhiteTheme() { + // * MAIN COLORS * // + pl.col.bg = RGB(255, 255, 255); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(140, 140, 140); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(120, 120, 120) : RGB(80, 80, 80); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(80, 80, 80) : RGB(140, 140, 140); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = grCol.primary; + pl.col.header_sideMarker = pl.col.header_nowplaying_bg; + pl.col.header_artist_normal = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_artist_playing = this.LAYOUT !== 'default' ? RGB(255, 255, 255) : this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_album_normal = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_album_playing = this.LAYOUT !== 'default' ? RGB(245, 245, 245) : this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_info_normal = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_info_playing = this.LAYOUT !== 'default' ? RGB(245, 245, 245) : pl.col.header_info_normal; + pl.col.header_date_normal = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_date_playing = this.LAYOUT !== 'default' ? RGB(245, 245, 245) : pl.col.header_date_normal; + pl.col.header_line_normal = RGB(200, 200, 200); + pl.col.header_line_playing = RGB(200, 200, 200); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = grCol.primary; + pl.col.row_stripes_bg = this.BLEND ? RGBA(245, 245, 245, 130) : this.ALT ? RGB(255, 255, 255) : RGB(245, 245, 245); + pl.col.row_selection_bg = RGB(200, 200, 200); + pl.col.row_selection_frame = pl.col.row_selection_bg; + pl.col.row_sideMarker = grCol.primary; + pl.col.row_title_normal = this.LAYOUT !== 'compact' ? this.BLEND ? RGB(60, 60, 60) : RGB(100, 100, 100) : this.LAYOUT === 'compact' ? this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120) : ''; + pl.col.row_title_playing = RGB(245, 245, 245); + pl.col.row_title_selected = RGB(0, 0, 0); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = RGB(200, 200, 200); + pl.col.row_drag_line = ShadeColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = RGB(120, 120, 120); + pl.col.sbar_btn_hovered = RGB(0, 0, 0); + pl.col.sbar_thumb_normal = RGB(200, 200, 200); + pl.col.sbar_thumb_hovered = RGB(120, 120, 120); + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for White theme used in Options > Theme > White. + */ + libraryColorsWhiteTheme() { + // * MAIN COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + lib.ui.col.imgOverlaySel = this.BW ? RGBA(230, 230, 230, 175) : RGBtoRGBA(lib.ui.col.bg, 175); + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = this.BW ? RGB(230, 230, 230) : pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = + this.BW ? grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(255, 255, 255) : + this.BW2 ? grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(40, 40, 40) : + lib.ui.col.nowPlayingBg; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = this.BW ? RGB(220, 220, 220) : this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + lib.ui.col.iconPlus_h = this.BW ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.iconPlus_sel = this.BW ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.iconPlusBg = RGB(240, 240, 240); + lib.ui.col.iconMinus_e = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(0, 0, 0); + + // * TEXT COLORS * // + lib.ui.col.text = + libSet.albumArtShow && libImg.labels.overlayDark ? RGB(220, 220, 220) : + this.BW ? RGB(200, 200, 200) : + this.BLEND ? RGB(60, 60, 60) : + RGB(100, 100, 100); + + lib.ui.col.text_h = + libSet.albumArtShow && libImg.labels.overlayDark || + this.BW && (!libSet.albumArtShow || libSet.albumArtShow && libSet.highLightRow !== 2) ? RGB(255, 255, 255) : + RGB(0, 0, 0); + + lib.ui.col.text_nowp = + this.BW || this.BW2 || libSet.albumArtShow && libImg.labels.overlayDark || + grCol.lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); + + lib.ui.col.textSel = + this.BW ? + libSet.albumArtShow && ([1, 3].includes(libSet.albumArtLabelType) || ['coversLabelsBottom', 'coversLabelsBlend'].includes(grSet.libraryDesign)) || + grSet.libraryDesign === 'traditional' ? RGB(0, 0, 0) : RGB(255, 255, 255) : + libSet.albumArtShow && !libImg.labels.overlayDark && libSet.albumArtLabelType !== 2 && !this.BW2 ? grCol.lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255) : + RGB(0, 0, 0); + + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = this.BW ? RGB(200, 200, 200) : this.BLEND ? RGB(40, 40, 40) : RGB(80, 80, 80); + lib.ui.col.count = lib.ui.col.text; + + // * BUTTON COLORS * // + lib.ui.col.search = this.BW ? RGB(200, 200, 200) : this.BLEND ? RGB(60, 60, 60) : RGB(100, 100, 100); + lib.ui.col.searchBtn = this.BW ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.crossBtn = this.BW ? RGB(255, 255, 255) : this.BLEND ? RGB(40, 40, 40) : RGB(80, 80, 80); + lib.ui.col.filterBtn = this.BW ? RGB(220, 220, 220) : this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + lib.ui.col.settingsBtn = this.BW ? RGB(220, 220, 220) : this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + lib.ui.col.line = this.BW ? RGB(45, 45, 45) : RGB(200, 200, 200); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = RGB(120, 120, 120); + lib.ui.col.sbarNormal = RGB(114, 114, 114); + lib.ui.col.sbarHovered = RGB(120, 120, 120); + lib.ui.col.sbarDrag = RGB(120, 120, 120); + } + + /** + * The Biography colors for White theme used in Options > Theme > White. + */ + biographyColorsWhiteTheme() { + // * MAIN COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = + this.LAYOUT === 'artwork' ? this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120) : + this.BW ? RGB(220, 220, 220) : + this.BW2 ? RGB(80, 80, 80) : + pl.col.header_artist_playing; + + bio.ui.col.bottomLine = (bio.ui.blur.blend || bio.ui.blur.light) ? RGB(120, 120, 120) : pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = + this.BW ? RGB(200, 200, 200) : + this.BW2 ? RGB(80, 80, 80) : + pl.col.row_title_normal; + + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = this.BW ? RGB(230, 230, 230) : this.BW2 ? RGB(25, 25, 25) : RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = this.BW ? RGB(0, 0, 0) : this.BW2 ? RGB(255, 255, 255) : grCol.lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = RGB(220, 160, 40); + bio.ui.col.noPhotoStubBg = RGB(245, 245, 245); + bio.ui.col.noPhotoStubText = RGB(120, 120, 120); + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = RGB(120, 120, 120); + bio.ui.col.sbarNormal = RGB(114, 114, 114); + bio.ui.col.sbarHovered = RGB(120, 120, 120); + bio.ui.col.sbarDrag = RGB(120, 120, 120); + } + + /** + * The Main colors for White theme used in Options > Theme > White. + */ + mainColorsWhiteTheme() { + // * MAIN COLORS * // + grCol.bg = this.BEVEL ? RGB(255, 255, 255) : RGB(245, 245, 245); + grCol.loadingThemeBg = this.BW ? RGB(230, 230, 230) : this.BW2 ? RGB(25, 25, 25) : RGB(245, 245, 245); + grCol.uiHacksFrame = + this.BW ? this.BEVEL ? RGB(255, 255, 255) : RGB(230, 230, 230) : + this.BW2 ? this.BEVEL ? RGB(50, 50, 50) : RGB(25, 25, 25) : + RGB(245, 245, 245); + grCol.shadow = this.BW2 ? RGBA(0, 0, 0, 240) : RGBA(0, 0, 0, 25); + grCol.discArtShadow = this.BW2 ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 10); + grCol.noAlbumArtStub = this.BLEND || this.BLEND2 ? RGB(80, 80, 80) : RGB(120, 120, 120); + grCol.lowerBarArtist = this.BLEND || this.BLEND2 ? RGB(80, 80, 80) : RGB(120, 120, 120); + grCol.lowerBarTitle = this.BLEND || this.BLEND2 ? RGB(80, 80, 80) : RGB(120, 120, 120); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = grSet.lyricsAlbumArt ? RGB(255, 240, 150) : RGB(220, 160, 40); + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = grCol.primary !== RGB(25, 160, 240) && grm.ui.albumArt && !grm.ui.isStreaming ? grCol.primary : RGB(255, 255, 255); + grCol.detailsText = grm.ui.isStreaming || grm.ui.isPlayingCD || !grm.ui.albumArt ? RGB(120, 120, 120) : grCol.lightBg ? RGB(55, 55, 55) : RGB(255, 255, 255); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 50 : 40) : grCol.lightAccent_50; + grCol.timelinePlayed = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 35 : 25) : grCol.lightAccent_35; + grCol.timelineUnplayed = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 20 : 10) : grCol.lightAccent; + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = this.BW ? RGB(230, 230, 230) : this.BW2 ? RGB(25, 25, 25) : RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = this.BW ? RGB(0, 0, 0) : this.BW2 ? RGB(255, 255, 255) : grCol.lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(255, 255, 255) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(250, 250, 250) : RGB(255, 255, 255) : + RGB(255, 255, 255); + + grCol.menuStyleBg = + this.TMB === 'emboss' ? this.BEVEL ? RGB(235, 235, 235) : RGB(225, 225, 225) : + this.BEVEL ? RGB(205, 205, 205) : RGB(220, 220, 220); + + grCol.menuRectStyleEmbossTop = RGB(255, 255, 255); + grCol.menuRectStyleEmbossBottom = this.BEVEL ? RGB(200, 200, 200) : RGB(210, 210, 210); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGB(200, 200, 200) : + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(140, 140, 140) : RGB(150, 150, 150) : + this.ALT2 ? RGB(190, 190, 190) : + RGB(200, 200, 200); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGB(200, 200, 200) : + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(200, 200, 200) : RGB(220, 220, 220) : + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(140, 140, 140) : RGB(150, 150, 150) : + this.ALT2 ? RGB(190, 190, 190) : + RGB(200, 200, 200); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? RGB(80, 80, 80) : RGB(120, 120, 120); + grCol.menuTextHovered = RGB(80, 80, 80); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = (this.BLEND || this.BLEND2) && fb.IsPlaying ? RGB(230, 230, 230) : RGB(255, 255, 255); + grCol.transportEllipseNormal = this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(200, 200, 200) : RGB(210, 210, 210) : RGB(220, 220, 220); + grCol.transportEllipseHovered = this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(160, 160, 160) : RGB(170, 170, 170) : RGB(180, 180, 180); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(215, 215, 215) : RGB(220, 220, 220) : + this.TPB === 'emboss' ? RGB(225, 225, 225) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(255, 255, 255) : + this.TPB === 'emboss' ? RGB(255, 255, 255) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(190, 190, 190) : RGB(215, 215, 215) : + this.TPB === 'emboss' ? this.BEVEL ? RGB(210, 210, 210) : RGB(225, 225, 225) : ''; + + grCol.transportIconNormal = this.BLEND || this.BLEND2 || this.TPB === 'minimal' ? RGB(80, 80, 80) : RGB(120, 120, 120); + grCol.transportIconHovered = this.BLEND || this.BLEND2 || this.TPB === 'minimal' ? RGB(0, 0, 0) : RGB(60, 60, 60); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + this.PB === 'bevel' ? this.BEVEL ? RGB(245, 245, 245) : RGB(220, 220, 220) : + this.BEVEL ? this.BLEND || this.BLEND2 ? RGB(235, 235, 235) : RGB(200, 200, 200) : + (this.BLEND || this.BLEND2) && fb.IsPlaying && !grm.ui.noAlbumArtStub ? RGB(240, 240, 240) : + RGB(220, 220, 220); + + grCol.progressBarStreaming = RGB(207, 0, 5); + grCol.progressBarFrame = this.BEVEL ? RGB(180, 180, 180) : grCol.bg; + grCol.progressBarFill = this.BEVEL ? ShadeColor(grCol.primary, 5) : grCol.primary; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness < 75 ? TintColor(grCol.primary, 40) : ShadeColor(grCol.primary, 40); + grCol.peakmeterBarFillTop = TintColor(grCol.primary, 10); + grCol.peakmeterBarFillMiddle = TintColor(grCol.primary, 30); + grCol.peakmeterBarFillBack = TintColor(grCol.primary, 50); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = ShadeColor(grCol.primary, 10); + grCol.peakmeterBarVertFillPeaks = TintColor(grCol.primary, 20); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.primary; + grCol.waveformBarFillBack = ShadeColor(grCol.primary, 20); + grCol.waveformBarFillPreFront = this.BEVEL || this.BLEND ? RGB(140, 140, 140) : RGB(180, 180, 180); + grCol.waveformBarFillPreBack = this.BEVEL || this.BLEND ? RGB(120, 120, 120) : RGB(160, 160, 160); + grCol.waveformBarIndicator = grCol.colBrightness > 200 ? RGB(0, 0, 0) : TintColor(grCol.primary, 30); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(255, 255, 255); + grCol.volumeBarFrame = RGB(220, 220, 220); + grCol.volumeBarFill = grCol.primary; + + // * STYLE COLORS * // + grCol.styleBevel = this.BEVEL && this.BW2 ? RGB(0, 0, 0) : RGB(40, 40, 40); + grCol.styleGradient = ''; + grCol.styleGradient2 = ''; + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 40) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.PBD === 'rounded' ? RGBA(0, 0, 0, 40) : + this.BEVEL ? RGBA(255, 255, 255, 20) : RGBA(0, 0, 0, 0) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, this.ALT || this.ALT2 ? 30 : 50) : RGBA(0, 0, 0, 50) : + this.BEVEL ? RGBA(0, 0, 0, this.ALT || this.ALT2 ? 15 : 20) : RGBA(0, 0, 0, this.ALT || this.ALT2 ? 10 : 20) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 160) : this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 140) : RGBA(255, 255, 255, 255) : + this.BEVEL ? this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 100) : this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 220) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 100) : RGBA(255, 255, 255, 140) : this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 140) : RGBA(255, 255, 255, 255) : + this.BEVEL ? this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 140) : this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 255) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? RGBA(0, 0, 0, 30) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 40) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + } + // #endregion + + // * PUBLIC METHODS - BLACK THEME * // + // #region PUBLIC METHODS - BLACK THEME + /** + * The Playlist colors for Black theme used in Options > Theme > Black. + */ + playlistColorsBlackTheme() { + // * MAIN COLORS * // + pl.col.bg = RGB(20, 20, 20); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(180, 180, 180); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(200, 200, 200) : RGB(240, 240, 240); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(240, 240, 240) : RGB(180, 180, 180); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = grCol.colBrightness < 25 ? grCol.lightAccent : grCol.primary; + pl.col.header_sideMarker = pl.col.header_nowplaying_bg; + pl.col.header_artist_normal = RGB(220, 220, 220); + pl.col.header_artist_playing = grm.ui.noAlbumArtStub && (this.BLEND || this.BLEND2) ? RGB(20, 20, 20) : RGB(255, 255, 255); + pl.col.header_album_normal = RGB(200, 200, 200); + pl.col.header_album_playing = grm.ui.noAlbumArtStub && (this.BLEND || this.BLEND2) ? RGB(20, 20, 20) : RGB(245, 245, 245); + pl.col.header_info_normal = RGB(200, 200, 200); + pl.col.header_info_playing = grm.ui.noAlbumArtStub && (this.BLEND || this.BLEND2) ? RGB(20, 20, 20) : RGB(245, 245, 245); + pl.col.header_date_normal = RGB(220, 220, 220); + pl.col.header_date_playing = grm.ui.noAlbumArtStub && (this.BLEND || this.BLEND2) ? RGB(20, 20, 20) : RGB(245, 245, 245); + pl.col.header_line_normal = this.BLEND ? RGB(65, 65, 65) : RGB(45, 45, 45); + pl.col.header_line_playing = RGB(25, 25, 25); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(25, 25, 25, 130) : this.ALT2 ? RGB(35, 35, 35) : RGB(25, 25, 25); + pl.col.row_selection_bg = this.BLEND ? RGB(65, 65, 65) : RGB(45, 45, 45); + pl.col.row_selection_frame = pl.col.row_selection_bg; + pl.col.row_sideMarker = grCol.colBrightness < 25 ? grCol.lightAccent_35 : grCol.primary; + pl.col.row_title_normal = RGB(200, 200, 200); + pl.col.row_title_playing = grm.ui.noAlbumArtStub && (this.BLEND || this.BLEND2) ? RGB(20, 20, 20) : RGB(245, 245, 245); + pl.col.row_title_selected = RGB(255, 255, 255); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = this.BLEND ? RGB(65, 65, 65) : RGB(45, 45, 45); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = RGB(100, 100, 100); + pl.col.sbar_btn_hovered = RGB(160, 160, 160); + pl.col.sbar_thumb_normal = RGB(100, 100, 100); + pl.col.sbar_thumb_hovered = RGB(160, 160, 160); + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for Black theme used in Options > Theme > Black. + */ + libraryColorsBlackTheme() { + // * MAIN COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = RGB(220, 220, 220); + lib.ui.col.iconPlus_h = RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = RGB(255, 255, 255); + lib.ui.col.iconPlusBg = RGB(45, 45, 45); + lib.ui.col.iconMinus_e = RGB(220, 220, 220); + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(255, 255, 255); + + // * TEXT COLORS * // + lib.ui.col.text = RGB(200, 200, 200); + lib.ui.col.text_h = + this.BR && libSet.albumArtShow && grCol.lightBg ? + libSet.highLightRow === 2 ? RGB(0, 0, 0) : RGB(255, 255, 255) : + RGB(255, 255, 255); + + lib.ui.col.text_nowp = + grCol.lightBg ? libSet.albumArtShow && libImg.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0) : + RGB(255, 255, 255); + + lib.ui.col.textSel = + libSet.albumArtShow ? + grCol.lightBg ? libImg.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0) : RGB(255, 255, 255) : + RGB(255, 255, 255); + + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(200, 200, 200); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(200, 200, 200); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = RGB(255, 255, 255); + lib.ui.col.crossBtn = RGB(255, 255, 255); + lib.ui.col.filterBtn = RGB(220, 220, 220); + lib.ui.col.settingsBtn = RGB(220, 220, 220); + lib.ui.col.line = this.BLEND ? RGB(60, 60, 60) : RGB(45, 45, 45); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = RGB(100, 100, 100); + lib.ui.col.sbarNormal = RGB(226, 226, 226); + lib.ui.col.sbarHovered = RGB(160, 160, 160); + lib.ui.col.sbarDrag = RGB(160, 160, 160); + } + + /** + * The Biography colors for Black theme used in Options > Theme > Black. + */ + biographyColorsBlackTheme() { + // * MAIN COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = + bio.ui.blur.blend ? pl.col.header_artist_playing : + bio.ui.blur.light ? RGB(65, 65, 65) : + pl.col.header_artist_playing; + + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = grCol.lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = RGB(220, 160, 40); + bio.ui.col.noPhotoStubBg = RGB(25, 25, 25); + bio.ui.col.noPhotoStubText = pl.col.header_artist_playing; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = RGB(100, 100, 100); + bio.ui.col.sbarNormal = RGB(226, 226, 226); + bio.ui.col.sbarHovered = RGB(160, 160, 160); + bio.ui.col.sbarDrag = RGB(160, 160, 160); + } + + /** + * The Main colors for Black theme used in Options > Theme > Black. + */ + mainColorsBlackTheme() { + // * MAIN COLORS * // + grCol.bg = this.BEVEL ? RGB(40, 40, 40) : RGB(25, 25, 25); + grCol.loadingThemeBg = RGB(25, 25, 25); + grCol.uiHacksFrame = this.BR && fb.IsPlaying && !grm.ui.isStreaming && !grm.ui.isPlayingCD ? grCol.primary : RGB(35, 35, 35); + grCol.shadow = + this.BEVEL && (this.THEME !== 'black' && !this.BR) ? RGBA(0, 0, 0, 240) : + this.ALT ? RGBA(0, 0, 0, 100) : + this.ALT2 ? RGBA(0, 0, 0, 240) : + RGBA(0, 0, 0, 120); + grCol.discArtShadow = this.BR ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 40); + grCol.noAlbumArtStub = grm.ui.isStreaming ? RGB(240, 240, 240) : RGB(175, 205, 225); + grCol.lowerBarArtist = RGB(240, 240, 240); + grCol.lowerBarTitle = this.BLEND || this.BLEND2 ? RGB(220, 220, 220) : RGB(200, 200, 200); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = grSet.lyricsAlbumArt ? RGB(255, 240, 150) : RGB(220, 160, 40); + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = grCol.primary !== RGB(25, 160, 240) && grm.ui.albumArt && !grm.ui.isStreaming ? grCol.primary : RGB(20, 20, 20); + grCol.detailsText = grm.ui.isStreaming || grm.ui.isPlayingCD || !grm.ui.albumArt ? RGB(255, 255, 255) : grCol.lightBg ? grCol.darkAccent_75 : !grm.ui.albumArt ? RGB(120, 120, 120) : grCol.lightAccent_100; + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 50 : 40) : grCol.lightAccent_50; + grCol.timelinePlayed = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 35 : 25) : grCol.lightAccent_35; + grCol.timelineUnplayed = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 20 : 10) : grCol.lightAccent; + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = grCol.lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? this.BEVEL ? RGB(40, 40, 40) : RGB(50, 50, 50) : + this.TMB === 'inner' ? this.BEVEL ? RGB(55, 55, 55) : RGB(50, 50, 50) : + this.TMB === 'emboss' ? RGB(45, 45, 45) : + RGB(35, 35, 35); + + grCol.menuStyleBg = + this.TMB === 'inner' ? RGB(20, 20, 20) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(45, 45, 45) : RGB(50, 50, 50) : + this.BEVEL ? RGB(30, 30, 30) : RGB(20, 20, 20); + + grCol.menuRectStyleEmbossTop = this.BEVEL ? RGB(60, 60, 60) : RGB(70, 70, 70); + grCol.menuRectStyleEmbossBottom = RGB(0, 0, 0); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGBA(60, 60, 60, 100) : + this.TMB === 'bevel' ? RGB(0, 0, 0) : + RGB(60, 60, 60); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(120, 120, 120, 100) : + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(0, 0, 0) : + RGB(120, 120, 120); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? RGB(200, 200, 200) : RGB(180, 180, 180); + grCol.menuTextHovered = RGB(255, 255, 255); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = + (this.BLEND || this.BLEND2) && fb.IsPlaying ? + this.PB === 'bevel' ? RGB(36, 36, 36) : + this.PB === 'inner' ? RGB(37, 37, 37) : + RGB(35, 35, 35) : RGB(35, 35, 35); + grCol.transportEllipseNormal = RGB(60, 60, 60); + grCol.transportEllipseHovered = RGB(120, 120, 120); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(20, 20, 20) : + this.TPB === 'emboss' ? RGB(50, 50, 50) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(50, 50, 50) : + this.TPB === 'emboss' ? this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(10, 10, 10) : + this.TPB === 'emboss' ? RGB(20, 20, 20) : ''; + + grCol.transportIconNormal = RGB(160, 160, 160); + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + this.PB === 'bevel' ? RGB(36, 36, 36) : + this.PB === 'inner' ? RGB(37, 37, 37) : + RGB(35, 35, 35); + + grCol.progressBarStreaming = RGB(207, 0, 5); + grCol.progressBarFrame = this.BEVEL ? RGB(0, 0, 0) : grCol.bg; + grCol.progressBarFill = grCol.colBrightness < 25 ? TintColor(grCol.primary, 25) : grCol.colBrightness < 50 ? grCol.lightAccent_7 : grCol.primary; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 40) : grCol.colBrightness < 50 ? TintColor(grCol.primary, 50) : TintColor(grCol.primary, 40); + grCol.peakmeterBarFillTop = grCol.colBrightness < 50 ? TintColor(grCol.primary, 20) : TintColor(grCol.primary, 10); + grCol.peakmeterBarFillMiddle = grCol.colBrightness < 50 ? TintColor(grCol.primary, 40) : TintColor(grCol.primary, 30); + grCol.peakmeterBarFillBack = grCol.colBrightness < 50 ? TintColor(grCol.primary, 30) : ShadeColor(grCol.primary, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = grCol.colBrightness < 50 ? TintColor(grCol.primary, 20) : ShadeColor(grCol.primary, 10); + grCol.peakmeterBarVertFillPeaks = grCol.colBrightness < 50 ? TintColor(grCol.primary, 30) : TintColor(grCol.primary, 20); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.colBrightness < 50 ? TintColor(grCol.primary, 40) : grCol.colBrightness < 100 ? TintColor(grCol.primary, 20) : grCol.primary; + grCol.waveformBarFillBack = grCol.colBrightness < 50 ? TintColor(grCol.primary, 20) : grCol.colBrightness < 100 ? grCol.primary : ShadeColor(grCol.primary, 20); + grCol.waveformBarFillPreFront = RGB(100, 100, 100); + grCol.waveformBarFillPreBack = RGB(80, 80, 80); + grCol.waveformBarIndicator = grCol.colBrightness > 200 ? RGB(255, 255, 255) : RGB(220, 220, 220); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(35, 35, 35); + grCol.volumeBarFrame = RGB(60, 60, 60); + grCol.volumeBarFill = grCol.progressBarFill; + + // * STYLE COLORS * // + grCol.styleBevel = RGB(0, 0, 0); + grCol.styleGradient = ''; + grCol.styleGradient2 = ''; + grCol.styleAlternative = this.BEVEL ? RGB(40, 40, 40) : RGB(25, 25, 25); + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : + this.PB === 'inner' ? RGBA(0, 0, 0, 100) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? RGBA(0, 0, 0, 255) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 150) : + this.BEVEL ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 50) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? RGBA(255, 255, 255, 25) : + this.BEVEL ? RGBA(255, 255, 255, 25) : RGBA(255, 255, 255, 20) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 20) : RGBA(255, 255, 255, 10) : + this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 15) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + } + // #endregion + + // * PUBLIC METHODS - REBORN/RANDOM THEME * // + // #region PUBLIC METHODS - REBORN/RANDOM THEME + /** + * The Playlist colors for Reborn/Random theme used in Options > Theme > Reborn/Random. + */ + playlistColorsRebornRandomTheme() { + // * MAIN COLORS * // + pl.col.bg = + // * Need this extra condition to overwrite col.primary when switching themes, no album art loaded i.e on startup and going back to Reborn/Random theme. + // * Reborn/Random theme should stay default white and not the defined col.primary dark gray + !fb.IsPlaying || !grm.ui.albumArt || grCol.primary === RGB(90, 90, 90) && !fb.IsPlaying || grCol.primary === RGB(25, 160, 240) && !fb.IsPlaying ? RGB(255, 255, 255) : + this.LAYOUT !== 'default' ? grCol.lightAccent_2 : grCol.primary; + // * Assigned after background has been initialized + grCol.isColored = pl.col.bg !== RGB(255, 255, 255); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(140, 140, 140); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(120, 120, 120) : RGB(80, 80, 80); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(80, 80, 80) : RGB(140, 140, 140); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = + this.THEME === 'reborn' ? grCol.isColored ? this.BLEND ? RGBtoRGBA(grCol.lightAccent_10, 130) : grCol.lightAccent_10 : + grCol.primary : + this.THEME === 'random' ? grCol.isColored ? this.BLEND ? RGBtoRGBA(grCol.lightAccent_10, 130) : grCol.lightBg ? ShadeColor(grCol.primary, 5) : grCol.lightAccent_10 : + grCol.primary : ''; + + pl.col.header_sideMarker = grCol.isColored ? grCol.lightAccent_50 : grCol.primary; + pl.col.header_artist_normal = RGB(120, 120, 120); + pl.col.header_artist_playing = RGB(120, 120, 120); + pl.col.header_album_normal = RGB(120, 120, 120); + pl.col.header_album_playing = RGB(120, 120, 120); + pl.col.header_info_normal = RGB(120, 120, 120); + pl.col.header_info_playing = pl.col.header_info_normal; + pl.col.header_date_normal = RGB(120, 120, 120); + pl.col.header_date_playing = pl.col.header_date_normal; + pl.col.header_line_normal = grCol.isColored ? this.BLEND ? ShadeColor(grCol.primary, 24) : grCol.accent : RGB(200, 200, 200); + pl.col.header_line_playing = grCol.isColored ? this.BLEND ? ShadeColor(grCol.primary, 24) : grCol.accent : RGB(200, 200, 200); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = grCol.isColored ? this.BLEND ? RGBtoRGBA(grCol.lightAccent_10, 130) : TintColor(grCol.primary, this.ALT2 ? 0 : 5) : RGB(245, 245, 245); + pl.col.row_selection_bg = grCol.isColored ? this.BLEND ? ShadeColor(grCol.primary, 24) : grCol.accent : RGB(200, 200, 200); + pl.col.row_selection_frame = pl.col.row_selection_bg; + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(100, 100, 100); + pl.col.row_title_playing = grm.ui.noAlbumArtStub && this.ALT2 ? RGB(20, 20, 20) : RGB(245, 245, 245); + pl.col.row_title_selected = RGB(0, 0, 0); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = grCol.isColored ? this.BLEND ? ShadeColor(grCol.primary, 24) : grCol.accent : RGB(200, 200, 200); + pl.col.row_drag_line = pl.col.row_sideMarker; + pl.col.row_drag_line_reached = grCol.colBrightness > 210 ? ShadeColor(pl.col.row_sideMarker, 25) : TintColor(pl.col.row_sideMarker, 50); + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = RGB(120, 120, 120); + pl.col.sbar_btn_hovered = RGB(0, 0, 0); + pl.col.sbar_thumb_normal = RGB(200, 200, 200); + pl.col.sbar_thumb_hovered = RGB(120, 120, 120); + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + + // * WHEN PLAYING * // + if (fb.IsPlaying && grCol.isColored) { + if (this.GRAD || this.GRAD2 || this.RF || this.RF2 ? grCol.lightBgPlaylist : grCol.lightBg) { + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : grCol.darkAccent_75; + pl.col.plman_text_hovered = grCol.darkAccent_100; + pl.col.plman_text_pressed = grCol.darkAccent_100; + + // * HEADER COLORS * // + pl.col.header_artist_normal = grCol.darkAccent_75; + pl.col.header_artist_playing = grCol.darkAccent_75; + pl.col.header_album_normal = grCol.darkAccent_75; + pl.col.header_album_playing = grCol.darkAccent_75; + pl.col.header_info_normal = grCol.darkAccent_75; + pl.col.header_info_playing = grCol.darkAccent_75; + pl.col.header_date_normal = grCol.darkAccent_75; + pl.col.header_date_playing = grCol.darkAccent_75; + + // * ROW COLORS * // + pl.col.row_title_normal = grCol.darkAccent_65; + pl.col.row_title_playing = grCol.darkAccent_100; + pl.col.row_title_selected = grCol.darkAccent_100; + pl.col.row_title_hovered = grCol.darkAccent_100; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = grCol.darkAccent_75; + pl.col.sbar_btn_hovered = grCol.darkAccent_100; + pl.col.sbar_thumb_normal = grCol.darkAccent; + pl.col.sbar_thumb_hovered = this.BLEND ? grCol.lightAccent_80 : grCol.lightAccent_50; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + else { + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : grCol.lightAccent_80; + pl.col.plman_text_hovered = grCol.lightAccent_100; + pl.col.plman_text_pressed = grCol.lightAccent_100; + + // * HEADER COLORS * // + pl.col.header_artist_normal = grCol.lightAccent_80; + pl.col.header_artist_playing = grCol.lightAccent_100; + pl.col.header_album_normal = grCol.lightAccent_80; + pl.col.header_album_playing = grCol.lightAccent_100; + pl.col.header_info_normal = grCol.lightAccent_80; + pl.col.header_info_playing = grCol.lightAccent_100; + pl.col.header_date_normal = grCol.lightAccent_80; + pl.col.header_date_playing = grCol.lightAccent_100; + + // * ROW COLORS * // + pl.col.row_title_normal = grCol.lightAccent_80; + pl.col.row_title_playing = grCol.lightAccent_100; + pl.col.row_title_selected = grCol.lightAccent_100; + pl.col.row_title_hovered = grCol.lightAccent_100; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = grCol.lightAccent_80; + pl.col.sbar_btn_hovered = grCol.lightAccent_100; + pl.col.sbar_thumb_normal = grCol.lightAccent_35; + pl.col.sbar_thumb_hovered = this.BLEND ? grCol.lightAccent_80 : grCol.lightAccent_50; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + } + } + + /** + * The Library colors for Reborn/Random theme used in Options > Theme > Reborn/Random. + */ + libraryColorsRebornRandomTheme() { + // * MAIN COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = libSet.albumArtShow ? TintColor(pl.col.row_nowplaying_bg, 7) : pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = RGB(120, 120, 120); + lib.ui.col.iconPlus_h = RGB(0, 0, 0); + lib.ui.col.iconPlus_sel = !fb.IsPlaying && !libSet.albumArtShow && !lib.pop.highlight.nowPlaying ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.iconPlusBg = RGB(240, 240, 240); + lib.ui.col.iconMinus_e = RGB(120, 120, 120); + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(0, 0, 0); + + // * TEXT COLORS * // + lib.ui.col.text = grm.ui.noAlbumArtStub && libSet.albumArtShow && libImg.labels.overlayDark ? RGB(220, 220, 220) : RGB(100, 100, 100); + lib.ui.col.text_h = grm.ui.noAlbumArtStub && libSet.albumArtShow && libImg.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.text_nowp = grm.ui.noAlbumArtStub && libSet.albumArtShow && libImg.labels.overlayDark || grm.ui.noAlbumArtStub && this.ALT2 ? RGB(0, 0, 0) : RGB(255, 255, 255); + + lib.ui.col.textSel = + !['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(grSet.libraryDesign) || ![2, 1].includes(libSet.albumArtLabelType) || + (['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(grSet.libraryDesign) || [2, 1].includes(libSet.albumArtLabelType)) && !lib.pop.highlight.nowPlaying ? + !grCol.isColored && !grm.ui.noAlbumArtStub && !libSet.albumArtShow && lib.pop.highlight.nowPlaying || grm.ui.noAlbumArtStub && !libSet.albumArtShow || grm.ui.noAlbumArtStub && libSet.albumArtShow && libImg.labels.overlayDark || grm.ui.noAlbumArtStub && this.ALT2 ? + RGB(0, 0, 0) : RGB(255, 255, 255) : + lib.ui.col.text_h; + + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(80, 80, 80); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(100, 100, 100); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = RGB(0, 0, 0); + lib.ui.col.crossBtn = RGB(80, 80, 80); + lib.ui.col.filterBtn = RGB(120, 120, 120); + lib.ui.col.settingsBtn = RGB(120, 120, 120); + lib.ui.col.line = grCol.isColored ? this.BLEND ? ShadeColor(grCol.primary, 24) : grCol.accent : RGB(200, 200, 200); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = grCol.isColored ? lib.ui.col.text : RGB(120, 120, 120); + lib.ui.col.sbarNormal = RGB(200, 200, 200); + lib.ui.col.sbarHovered = RGB(120, 120, 120); + lib.ui.col.sbarDrag = RGB(120, 120, 120); + + // * WHEN PLAYING * // + if (fb.IsPlaying && grCol.isColored) { + if (this.GRAD || this.GRAD2 || this.RF || this.RF2 ? grCol.lightBgLibrary : grCol.lightBg) { + // * NODE COLORS * // + lib.ui.col.iconPlus = grCol.darkAccent_75; + lib.ui.col.iconPlus_h = grCol.darkAccent_100; + lib.ui.col.iconPlus_sel = grCol.darkAccent_100; + lib.ui.col.iconPlusBg = grCol.lightAccent_7; + lib.ui.col.iconMinus_e = grCol.darkAccent_75; + lib.ui.col.iconMinus_h = grCol.darkAccent_100; + + // * TEXT COLORS * // + lib.ui.col.text = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(220, 220, 220) : libSet.albumArtShow ? grCol.darkAccent_75 : grCol.darkAccent_65; + lib.ui.col.text_h = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(255, 255, 255) : grCol.darkAccent_100; + lib.ui.col.text_nowp = grCol.darkAccent_100; + lib.ui.col.textSel = grCol.darkAccent_100; + lib.ui.col.txt_box = grCol.darkAccent_75; + lib.ui.col.search = grCol.darkAccent_75; + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = grCol.darkAccent_75; + lib.ui.col.crossBtn = grCol.darkAccent_75; + lib.ui.col.filterBtn = grCol.darkAccent_75; + lib.ui.col.settingsBtn = grCol.darkAccent_75; + lib.ui.col.line = grCol.accent; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = lib.ui.col.text; + lib.ui.col.sbarNormal = grCol.darkAccent; + lib.ui.col.sbarHovered = grCol.lightAccent_50; + lib.ui.col.sbarDrag = grCol.lightAccent_50; + } + else { + // * NODE COLORS * // + lib.ui.col.iconPlus = grCol.lightAccent_80; + lib.ui.col.iconPlus_h = grCol.lightAccent_100; + lib.ui.col.iconPlus_sel = grCol.lightAccent_100; + lib.ui.col.iconPlusBg = grCol.lightAccent_7; + lib.ui.col.iconMinus_e = grCol.lightAccent_80; + lib.ui.col.iconMinus_h = grCol.lightAccent_100; + + // * TEXT COLORS * // + lib.ui.col.text = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(220, 220, 220) : grCol.lightAccent_80; + lib.ui.col.text_h = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(255, 255, 255) : grCol.lightAccent_100; + lib.ui.col.text_nowp = grCol.lightAccent_100; + lib.ui.col.textSel = grCol.lightAccent_100; + lib.ui.col.txt_box = grCol.lightAccent_80; + lib.ui.col.search = grCol.lightAccent_80; + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = grCol.lightAccent_80; + lib.ui.col.crossBtn = grCol.lightAccent_80; + lib.ui.col.filterBtn = grCol.lightAccent_80; + lib.ui.col.settingsBtn = grCol.lightAccent_80; + lib.ui.col.line = grCol.accent; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = lib.ui.col.text; + lib.ui.col.sbarNormal = grCol.lightAccent; + lib.ui.col.sbarHovered = grCol.lightAccent_50; + lib.ui.col.sbarDrag = grCol.lightAccent_50; + } + } + } + + /** + * The Biography colors for Reborn/Random theme used in Options > Theme > Reborn/Random. + */ + biographyColorsRebornRandomTheme() { + // * MAIN COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = pl.col.header_artist_playing; + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = (grCol.lightBgBiography || grCol.lightBg) && !grm.ui.noAlbumArtStub ? grCol.darkAccent_75 : grCol.lightAccent_100; + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = (grCol.lightBgBiography || grCol.lightBg) && ColorDistance(RGB(255, 240, 150), bio.ui.col.bg, true) < 200 ? RGB(220, 160, 40) : RGB(255, 240, 150); + bio.ui.col.noPhotoStubBg = grCol.isColored ? grCol.lightAccent_7 : RGB(245, 245, 245); + bio.ui.col.noPhotoStubText = pl.col.header_artist_playing; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = grCol.isColored ? bio.ui.col.text : RGB(120, 120, 120); + bio.ui.col.sbarNormal = RGB(200, 200, 200); + bio.ui.col.sbarHovered = RGB(120, 120, 120); + bio.ui.col.sbarDrag = RGB(120, 120, 120); + + // * WHEN PLAYING * // + if (fb.IsPlaying && grCol.isColored) { + if (this.GRAD || this.GRAD2 || this.RF || this.RF2 ? grCol.lightBgBiography : grCol.lightBg) { + // * HEADER COLORS * // + bio.ui.col.headingText = grCol.darkAccent_75; + bio.ui.col.source = grCol.darkAccent_75; + bio.ui.col.bottomLine = grCol.darkAccent; + bio.ui.col.centerLine = grCol.darkAccent; + + // * TEXT COLORS * // + bio.ui.col.text = grCol.darkAccent_75; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = + grCol.isColored && !bio.ui.blur.dark && !bio.ui.blur.blend && !bio.ui.blur.light ? grCol.darkAccent_100 : + bio.ui.blur.light ? grCol.darkAccent_100 : + bio.ui.blur.dark ? grCol.lightAccent_100 : + RGB(20, 20, 20); + + bio.ui.col.sbarBtns = bio.ui.col.text; + bio.ui.col.sbarNormal = grCol.darkAccent; + bio.ui.col.sbarHovered = grCol.lightAccent_50; + bio.ui.col.sbarDrag = grCol.lightAccent_50; + } + else { + // * HEADER COLORS * // + bio.ui.col.headingText = bio.ui.blur.light ? grCol.darkAccent_75 : grCol.lightAccent_80; + bio.ui.col.source = bio.ui.blur.light ? grCol.darkAccent_75 : grCol.lightAccent_80; + bio.ui.col.bottomLine = grCol.darkAccent; + bio.ui.col.centerLine = grCol.darkAccent; + + // * TEXT COLORS * // + bio.ui.col.text = bio.ui.blur.light ? grCol.darkAccent_75 : grCol.lightAccent_80; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = + grCol.isColored && !bio.ui.blur.dark && !bio.ui.blur.blend && !bio.ui.blur.light ? grCol.lightAccent_100 : + bio.ui.blur.dark ? grCol.lightAccent_100 : + bio.ui.blur.light ? grCol.darkAccent_100 : + RGB(220, 220, 220); + + bio.ui.col.sbarBtns = bio.ui.col.text; + bio.ui.col.sbarNormal = grCol.lightAccent; + bio.ui.col.sbarHovered = grCol.lightAccent_50; + bio.ui.col.sbarDrag = grCol.lightAccent_50; + } + } + } + + /** + * The Main colors for Reborn/Random theme used in Options > Theme > Reborn/Random. + */ + mainColorsRebornRandomTheme() { + const nighttime = (grSet.styleNighttime || grSet.themeDayNightMode && grSet.themeDayNightTime === 'night') && !this.RW; + + // * MAIN COLORS * // + grCol.bg = grCol.isColored ? grCol.primary : RGB(245, 245, 245); + grCol.loadingThemeBg = nighttime || this.RB ? RGB(25, 25, 25) : RGB(245, 245, 245); + grCol.uiHacksFrame = + this.RW ? RGB(245, 245, 245) : + this.RB ? RGB(25, 25, 25) : + grCol.isColored ? grCol.primary : RGB(245, 245, 245); + grCol.shadow = + this.RB ? RGBA(0, 0, 0, 255) : + grm.ui.isStreaming || grm.ui.isPlayingCD || grm.ui.noAlbumArtStub || !grm.ui.albumArt ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 35); + grCol.discArtShadow = RGBA(0, 0, 0, 30); + grCol.noAlbumArtStub = RGB(120, 120, 120); + grCol.lowerBarArtist = RGB(120, 120, 120); + grCol.lowerBarTitle = RGB(120, 120, 120); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = (grCol.lightBgMain || grCol.lightBg) && ColorDistance(RGB(255, 240, 150), grCol.bg, true) < 200 ? RGB(220, 160, 40) : RGB(255, 240, 150); + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = grCol.primary !== RGB(25, 160, 240) && grm.ui.albumArt && !grm.ui.isStreaming ? this.RF2 ? grCol.primary_alt : grCol.primary : RGB(255, 255, 255); + grCol.detailsText = + grm.ui.isStreaming || grm.ui.isPlayingCD || !grm.ui.albumArt ? RGB(120, 120, 120) : + grCol.lightBg || grCol.lightBgDetails ? RGB(55, 55, 55) : + RGB(255, 255, 255); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 50 : 40) : grCol.lightAccent_50; + grCol.timelinePlayed = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 35 : 25) : grCol.lightAccent_35; + grCol.timelineUnplayed = grm.ui.isStreaming ? RGB(207, 0, 5) : grCol.lightBg ? ShadeColor(grCol.primary, this.BLEND ? 20 : 10) : grCol.lightAccent; + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = (grCol.lightBgBiography || grCol.lightBg) && !grm.ui.noAlbumArtStub ? grCol.darkAccent_75 : grCol.lightAccent_100; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + grCol.isColored ? + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(255, 255, 255) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(250, 250, 250) : RGB(255, 255, 255) : + grCol.lightAccent : + RGB(255, 255, 255); + + grCol.menuStyleBg = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.RW ? this.BEVEL ? RGB(205, 205, 205) : RGB(220, 220, 220) : + this.RB ? RGB(20, 20, 20) : + grCol.isColored ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 70) : RGBtoRGBA(grCol.darkAccent_75, 80) : + this.BEVEL ? RGB(205, 205, 205) : RGB(220, 220, 220) : + this.TMB === 'emboss' ? + this.RW ? this.BEVEL ? RGB(235, 235, 235) : RGB(225, 225, 225) : + this.RB ? this.BEVEL ? RGB(45, 45, 45) : RGB(50, 50, 50) : + grCol.isColored ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 30) : RGBtoRGBA(grCol.darkAccent_75, 40) : + this.BEVEL ? RGB(235, 235, 235) : RGB(225, 225, 225) : + RGB(255, 255, 255); + + grCol.menuRectStyleEmbossTop = + grCol.isColored ? + this.RW ? RGB(255, 255, 255) : + this.RB ? this.BEVEL ? RGB(60, 60, 60) : RGB(70, 70, 70) : + grCol.lightAccent : + RGB(255, 255, 255); + + grCol.menuRectStyleEmbossBottom = + grCol.isColored ? + this.RW ? this.BEVEL ? RGB(200, 200, 200) : RGB(210, 210, 210) : + this.RB ? RGB(0, 0, 0) : + grCol.darkAccent : + this.BEVEL ? RGB(200, 200, 200) : RGB(210, 210, 210); + + grCol.menuRectNormal = RGB(140, 140, 140); + grCol.menuRectHovered = RGB(200, 200, 200); + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = RGB(120, 120, 120); + grCol.menuTextHovered = RGB(80, 80, 80); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = grCol.isColored ? grCol.lightAccent_50 : RGB(255, 255, 255); + grCol.transportEllipseNormal = grCol.isColored ? grCol.lightAccent_7 : RGB(220, 220, 220); + grCol.transportEllipseHovered = RGB(200, 200, 200); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' ? + grCol.isColored ? + this.RW ? this.BEVEL ? RGB(215, 215, 215) : RGB(220, 220, 220) : + this.RB ? RGB(20, 20, 20) : + RGBtoRGBA(grCol.darkAccent_75, 100) : + this.BEVEL ? RGB(215, 215, 215) : RGB(220, 220, 220) : + this.TPB === 'inner' ? + grCol.isColored ? + this.RW ? this.BEVEL ? RGB(215, 215, 215) : RGB(220, 220, 220) : + this.RB ? RGB(20, 20, 20) : + RGBtoRGBA(grCol.darkAccent_75, 120) : + RGB(225, 225, 225) : + this.TPB === 'emboss' ? + grCol.isColored ? + this.RW ? RGB(225, 225, 225) : + this.RB ? RGB(50, 50, 50) : + RGBtoRGBA(grCol.darkAccent_75, 40) : + RGB(225, 225, 225) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? + grCol.isColored ? + this.RW ? RGB(255, 255, 255) : + this.RB ? RGB(50, 50, 50) : + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 220) : RGBtoRGBA(grCol.lightAccent_80, 230) : + RGB(255, 255, 255) : + this.TPB === 'emboss' ? + grCol.isColored ? + this.RW ? RGB(255, 255, 255) : + this.RB ? this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : + RGBtoRGBA(grCol.lightAccent_80, 100) : '' : + RGB(255, 255, 255); + + grCol.transportStyleBottom = + this.TPB === 'bevel' ? + grCol.isColored ? + this.RW ? this.BEVEL ? RGB(190, 190, 190) : RGB(220, 220, 220) : + this.RB ? RGB(10, 10, 10) : + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 30) : RGBtoRGBA(grCol.darkAccent_75, 20) : + this.BEVEL ? RGB(190, 190, 190) : RGB(220, 220, 220) : + this.TPB === 'inner' ? + grCol.isColored ? + this.RW ? this.BEVEL ? RGB(190, 190, 190) : RGB(220, 220, 220) : + this.RB ? RGB(10, 10, 10) : + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 30) : RGBtoRGBA(grCol.darkAccent_75, 40) : + this.BEVEL ? RGB(190, 190, 190) : RGB(220, 220, 220) : + this.TPB === 'emboss' ? + grCol.isColored ? + this.RW ? this.BEVEL ? RGB(210, 210, 210) : RGB(225, 225, 225) : + this.RB ? RGB(20, 20, 20) : + RGBtoRGBA(grCol.darkAccent_75, 60) : + this.BEVEL ? RGB(210, 210, 210) : RGB(225, 225, 225) : ''; + + grCol.transportIconNormal = RGB(120, 120, 120); + grCol.transportIconHovered = RGB(80, 80, 80); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = grCol.isColored ? this.BEVEL ? ShadeColor(grCol.primary, 28) : grCol.accent : RGB(220, 220, 220); + grCol.progressBarStreaming = RGB(207, 0, 5); + grCol.progressBarFrame = grCol.bg; + grCol.progressBarFill = grCol.isColored ? grCol.lightAccent_50 : grCol.primary; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness > 200 || grCol.colBrightness < 75 ? TintColor(grCol.primary, 100) : ShadeColor(grCol.primary, 100); + grCol.peakmeterBarFillTop = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 10) : TintColor(grCol.primary, 40); + grCol.peakmeterBarFillMiddle = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 20) : TintColor(grCol.primary, 60); + grCol.peakmeterBarFillBack = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 40) : TintColor(grCol.primary, 80); + grCol.peakmeterBarVertProgFill = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 50) : grCol.progressBarFill; + grCol.peakmeterBarVertFill = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 50) : TintColor(grCol.primary, 40); + grCol.peakmeterBarVertFillPeaks = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 20) : TintColor(grCol.primary, 60); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.colBrightness > 200 || grm.ui.noAlbumArtStub ? ShadeColor(grCol.primary, 80) : TintColor(grCol.primary, 90); + grCol.waveformBarFillBack = grCol.colBrightness > 200 || grm.ui.noAlbumArtStub ? ShadeColor(grCol.primary, 40) : TintColor(grCol.primary, 45); + grCol.waveformBarFillPreFront = grCol.colBrightness > 150 ? ShadeColor(grCol.primary, this.BEVEL || this.BLEND ? 60 : 40) : TintColor(grCol.primary, 50); + grCol.waveformBarFillPreBack = grCol.colBrightness > 150 ? ShadeColor(grCol.primary, this.BEVEL || this.BLEND ? 10 : 20) : TintColor(grCol.primary, 25); + grCol.waveformBarIndicator = grCol.colBrightness > 200 || grm.ui.noAlbumArtStub ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = grCol.isColored ? grCol.lightAccent_50 : RGB(255, 255, 255); + grCol.volumeBarFrame = grCol.isColored ? grCol.accent : RGB(220, 220, 220); + grCol.volumeBarFill = grCol.isColored ? grCol.accent : grCol.primary; + + // * STYLE COLORS * // + grCol.styleBevel = this.RW ? RGB(40, 40, 40) : this.RB ? RGB(0, 0, 0) : grCol.darkAccent_100; + grCol.styleGradient = grCol.isColored ? grCol.darkAccent : ''; + grCol.styleGradient2 = grCol.isColored ? grCol.darkAccent : ''; + + grCol.styleProgressBar = + this.PB === 'bevel' ? + this.RW ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.RB ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 50) : RGBtoRGBA(grCol.darkAccent_75, 40) : + this.PB === 'inner' ? + this.RW ? this.BEVEL ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 40) : + this.RB ? RGBA(0, 0, 0, 100) : + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 50) : RGBtoRGBA(grCol.darkAccent_75, 60) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.BEVEL ? ShadeColor(grCol.bg, 5) : TintColor(grCol.bg, 100) : + this.PB === 'inner' ? this.BEVEL ? ShadeColor(grCol.bg, 10) : ShadeColor(grCol.bg, 25) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.BEVEL ? ShadeColor(grCol.bg, 10) : ShadeColor(grCol.bg, 25) : + this.PB === 'inner' ? this.BEVEL ? TintColor(grCol.bg, 10) : TintColor(grCol.bg, 100) : ''; + + grCol.styleProgressBarFill = grCol.isColored ? this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 80) : '' : grCol.primary; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 50) : RGBtoRGBA(grCol.darkAccent_75, 40) : + this.VB === 'inner' ? this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 50) : RGBtoRGBA(grCol.darkAccent_75, 60) : ''; + + grCol.styleVolumeBarFill = grCol.isColored ? this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 80) : '' : grCol.primary; + + // * WHEN PLAYING * // + if (fb.IsPlaying && grCol.isColored) { + if (this.GRAD || this.GRAD2 || this.RF || this.RF2 ? grCol.lightBgMain : grCol.lightBg) { + // * MAIN COLORS * // + grCol.noAlbumArtStub = RGB(90, 90, 90); + grCol.lowerBarArtist = grCol.darkAccent_75; + grCol.lowerBarTitle = grCol.darkAccent_75; + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTONS COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 40) : RGBtoRGBA(grCol.lightAccent_80, 50) : + this.TMB !== 'default' ? grCol.lightAccent_10 : grCol.lightAccent_50; + + grCol.menuRectNormal = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 40) : RGBtoRGBA(grCol.lightAccent_80, 30) : + grCol.darkAccent; + + grCol.menuRectHovered = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 40) : RGBtoRGBA(grCol.darkAccent_75, 30) : + grCol.darkAccent; + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = grCol.darkAccent_75; + grCol.menuTextHovered = grCol.darkAccent_100; + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportIconNormal = this.TPB === 'emboss' || this.TPB === 'minimal' ? grCol.darkAccent_75 : grCol.darkAccent_65; + grCol.transportIconHovered = grCol.darkAccent_100; + grCol.transportIconDown = grCol.transportIconHovered; + grCol.transportEllipseNormal = grCol.accent; + grCol.transportEllipseHovered = grCol.darkAccent; + grCol.transportEllipseDown = grCol.transportEllipseHovered; + } + else { + // * MAIN * // + grCol.noAlbumArtStub = RGB(90, 90, 90); + grCol.lowerBarArtist = grCol.lightAccent_100; + grCol.lowerBarTitle = grCol.lightAccent_100; + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 40) : RGBtoRGBA(grCol.lightAccent_80, 50) : + this.TMB !== 'default' ? grCol.lightAccent_10 : grCol.darkAccent_50; + + grCol.menuRectNormal = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 60) : RGBtoRGBA(grCol.darkAccent_75, 50) : + grCol.lightAccent_50; + + grCol.menuRectHovered = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 60) : RGBtoRGBA(grCol.darkAccent_75, 50) : + grCol.lightAccent_50; + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = grCol.lightAccent_80; + grCol.menuTextHovered = grCol.lightAccent_100; + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportIconNormal = this.TPB === 'emboss' ? grCol.darkAccent_75 : this.TPB === 'minimal' ? grCol.lightAccent_80 : grCol.darkAccent_75; + grCol.transportIconHovered = grCol.darkAccent_100; + grCol.transportIconDown = grCol.transportIconHovered; + grCol.transportEllipseNormal = grCol.accent; + grCol.transportEllipseHovered = grCol.darkAccent; + grCol.transportEllipseDown = grCol.transportEllipseHovered; + } + } + } + // #endregion + + // * PUBLIC METHODS - BLUE THEME * // + // #region PUBLIC METHODS - BLUE THEME + /** + * The Playlist colors for Blue theme used in Options > Theme > Blue. + */ + playlistColorsBlueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(242, 230, 170); + pl.col.bg = RGB(10, 115, 200); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(220, 220, 220); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(230, 230, 230) : RGB(255, 255, 255); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(255, 255, 255) : RGB(220, 220, 220); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = RGB(10, 130, 220); + pl.col.header_sideMarker = accentColor; + pl.col.header_artist_normal = RGB(240, 240, 240); + pl.col.header_artist_playing = accentColor; + pl.col.header_album_normal = RGB(230, 230, 230); + pl.col.header_album_playing = RGB(245, 245, 245); + pl.col.header_info_normal = RGB(230, 230, 230); + pl.col.header_info_playing = RGB(245, 245, 245); + pl.col.header_date_normal = RGB(240, 240, 240); + pl.col.header_date_playing = RGB(245, 245, 245); + pl.col.header_line_normal = RGB(17, 100, 182); + pl.col.header_line_playing = RGB(17, 100, 182); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(5, 110, 195, 130) : this.ALT ? RGB(20, 120, 205) : RGB(5, 110, 195); + pl.col.row_selection_bg = RGB(10, 115, 200); + pl.col.row_selection_frame = RGB(10, 135, 230); + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(230, 230, 230); + pl.col.row_title_playing = RGB(255, 255, 255); + pl.col.row_title_selected = RGB(255, 255, 255); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = RGB(17, 100, 182); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = RGB(220, 220, 220); + pl.col.sbar_btn_hovered = RGB(255, 255, 255); + pl.col.sbar_thumb_normal = RGB(10, 135, 225); + pl.col.sbar_thumb_hovered = accentColor; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for Blue theme used in Options > Theme > Blue. + */ + libraryColorsBlueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(242, 230, 170); + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = accentColor; + lib.ui.col.iconPlus_h = RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = RGB(255, 255, 255); + lib.ui.col.iconPlusBg = RGB(10, 130, 220); + lib.ui.col.iconMinus_e = accentColor; + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(255, 255, 255); + + // * TEXT COLORS * // + lib.ui.col.text = RGB(230, 230, 230); + lib.ui.col.text_h = RGB(255, 255, 255); + lib.ui.col.text_nowp = accentColor; + lib.ui.col.textSel = RGB(255, 255, 255); + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(230, 230, 230); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(230, 230, 230); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = accentColor; + lib.ui.col.crossBtn = accentColor; + lib.ui.col.filterBtn = RGB(230, 230, 230); + lib.ui.col.settingsBtn = RGB(230, 230, 230); + lib.ui.col.line = RGB(17, 100, 182); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = RGB(220, 220, 220); + lib.ui.col.sbarNormal = RGB(10, 150, 255); + lib.ui.col.sbarHovered = accentColor; + lib.ui.col.sbarDrag = accentColor; + } + + /** + * The Biography colors for Blue theme used in Options > Theme > Blue. + */ + biographyColorsBlueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(242, 230, 170); + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = pl.col.header_artist_playing; + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = accentColor; + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = accentColor; + bio.ui.col.noPhotoStubBg = RGB(10, 130, 220); + bio.ui.col.noPhotoStubText = pl.col.header_artist_playing; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = RGB(220, 220, 220); + bio.ui.col.sbarNormal = RGB(10, 150, 255); + bio.ui.col.sbarHovered = accentColor; + bio.ui.col.sbarDrag = accentColor; + } + + /** + * The Main colors for Blue theme used in Options > Theme > Blue. + */ + mainColorsBlueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(242, 230, 170); + grCol.bg = RGB(5, 110, 195); + grCol.loadingThemeBg = RGB(5, 110, 195); + grCol.uiHacksFrame = RGB(63, 155, 202); + grCol.shadow = RGBA(0, 0, 0, 25); + grCol.discArtShadow = RGBA(0, 0, 0, 30); + grCol.noAlbumArtStub = accentColor; + grCol.lowerBarArtist = accentColor; + grCol.lowerBarTitle = RGB(245, 245, 245); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = accentColor; + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = RGB(10, 115, 200); + grCol.detailsText = RGB(255, 255, 255); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = accentColor; + grCol.timelinePlayed = RGB(195, 190, 130); + grCol.timelineUnplayed = RGB(155, 150, 130); + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = accentColor; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? this.BEVEL && !this.GRAD2 ? RGB(10, 123, 209) : RGB(10, 130, 220) : + this.TMB === 'inner' ? this.BEVEL && !this.GRAD2 ? RGB(10, 130, 220) : this.GRAD2 ? RGB(10, 130, 220) : RGB(10, 135, 230) : + this.TMB === 'emboss' ? this.BEVEL && !this.GRAD2 ? RGB(10, 123, 209) : RGB(10, 130, 220) : + RGB(10, 130, 220); + + grCol.menuStyleBg = this.TMB === 'emboss' ? RGB(5, 110, 195) : this.BEVEL ? RGB(5, 90, 160) : RGB(5, 100, 175); + grCol.menuRectStyleEmbossTop = RGB(10, 138, 228); + grCol.menuRectStyleEmbossBottom = RGB(6, 95, 160); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGBA(76, 175, 255, 130) : + this.TMB === 'bevel' ? this.BEVEL ? RGB(5, 85, 150) : RGB(5, 100, 180) : + RGB(76, 175, 255); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(76, 175, 255, 130) : + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL && !this.GRAD2 ? RGB(5, 85, 150) : this.GRAD2 ? RGB(4, 68, 120) : RGB(5, 100, 180) : + RGB(76, 175, 255); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = RGB(230, 230, 230); + grCol.menuTextHovered = RGB(255, 255, 255); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = RGB(10, 130, 220); + grCol.transportEllipseNormal = RGB(22, 107, 186); + grCol.transportEllipseHovered = RGB(76, 175, 255); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' ? this.GRAD2 ? RGB(5, 90, 160) : RGB(5, 100, 180) : + this.TPB === 'inner' ? this.GRAD2 ? RGB(10, 110, 190) : this.BEVEL && !this.GRAD2 ? RGB(5, 100, 180) : RGB(17, 100, 180) : + this.TPB === 'emboss' ? this.GRAD2 ? RGB(8, 110, 190) : RGB(11, 132, 224) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(7, 135, 240) : RGB(7, 130, 230) : + this.TPB === 'emboss' ? this.BEVEL && !this.GRAD2 ? RGB(12, 138, 235) : this.GRAD2 ? RGB(10, 115, 200) : RGB(12, 138, 235) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(5, 85, 150) : RGB(5, 100, 180) : + this.TPB === 'emboss' ? this.BEVEL && !this.GRAD2 ? RGB(6, 95, 160) : this.GRAD2 ? RGB(4, 68, 120) : RGB(5, 100, 175) : ''; + + grCol.transportIconNormal = accentColor; + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = RGB(10, 130, 220); + grCol.progressBarStreaming = accentColor; + grCol.progressBarFrame = RGB(22, 107, 186); + grCol.progressBarFill = accentColor; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = TintColor(accentColor, 40); + grCol.peakmeterBarFillTop = TintColor(accentColor, 10); + grCol.peakmeterBarFillMiddle = TintColor(accentColor, 20); + grCol.peakmeterBarFillBack = ShadeColor(accentColor, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = accentColor; + grCol.peakmeterBarVertFillPeaks = TintColor(accentColor, 80); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = accentColor; + grCol.waveformBarFillBack = ShadeColor(accentColor, 20); + grCol.waveformBarFillPreFront = RGB(75, 175, 255); + grCol.waveformBarFillPreBack = RGB(10, 145, 255); + grCol.waveformBarIndicator = RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(10, 130, 220); + grCol.volumeBarFrame = RGB(22, 107, 186); + grCol.volumeBarFill = accentColor; + + // * STYLE COLORS * // + grCol.styleBevel = RGB(0, 0, 0); + grCol.styleGradient = RGBA(0, 0, 0, 90); + grCol.styleGradient2 = RGB(3, 72, 128); + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.PBD === 'rounded' ? RGBA(0, 0, 0, 40) : + this.BEVEL ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 20) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.BEVEL ? RGBA(0, 0, 0, 45) : RGBA(0, 0, 0, 15) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.BEVEL ? RGB(10, 125, 210) : RGB(10, 130, 220) : + this.PB === 'inner' ? this.BEVEL ? RGB(10, 130, 220) : RGB(12, 138, 235) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + } + // #endregion + + // * PUBLIC METHODS - DARK BLUE THEME * // + // #region PUBLIC METHODS - DARK BLUE THEME + /** + * The Playlist colors for Dark blue theme used in Options > Theme > Dark blue. + */ + playlistColorsDarkblueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(255, 202, 128); + pl.col.bg = RGB(21, 37, 56); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(220, 220, 220); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(220, 220, 220) : RGB(255, 255, 255); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(255, 255, 255) : RGB(220, 220, 220); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = RGB(24, 50, 82); + pl.col.header_sideMarker = accentColor; + pl.col.header_artist_normal = RGB(240, 240, 240); + pl.col.header_artist_playing = accentColor; + pl.col.header_album_normal = RGB(220, 220, 220); + pl.col.header_album_playing = RGB(245, 245, 245); + pl.col.header_info_normal = RGB(220, 220, 220); + pl.col.header_info_playing = RGB(245, 245, 245); + pl.col.header_date_normal = RGB(220, 220, 220); + pl.col.header_date_playing = RGB(245, 245, 245); + pl.col.header_line_normal = RGB(12, 21, 31); + pl.col.header_line_playing = RGB(12, 21, 31); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(22, 40, 63, 130) : this.ALT ? RGB(18, 42, 70) : this.ALT2 ? RGB(17, 35, 57) : RGB(22, 40, 63); + pl.col.row_selection_bg = RGB(21, 37, 56); + pl.col.row_selection_frame = RGB(27, 55, 90); + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(230, 230, 230); + pl.col.row_title_playing = RGB(255, 255, 255); + pl.col.row_title_selected = RGB(255, 255, 255); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = RGB(12, 21, 31); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = RGB(220, 220, 220); + pl.col.sbar_btn_hovered = RGB(255, 255, 255); + pl.col.sbar_thumb_normal = RGB(27, 55, 90); + pl.col.sbar_thumb_hovered = accentColor; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for Dark blue theme used in Options > Theme > Dark blue. + */ + libraryColorsDarkblueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(255, 202, 128); + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = accentColor; + lib.ui.col.iconPlus_h = RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = RGB(255, 255, 255); + lib.ui.col.iconPlusBg = RGB(24, 50, 82); + lib.ui.col.iconMinus_e = accentColor; + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(255, 255, 255); + + // * TEXT COLORS * // + lib.ui.col.text = RGB(230, 230, 230); + lib.ui.col.text_h = RGB(255, 255, 255); + lib.ui.col.text_nowp = accentColor; + lib.ui.col.textSel = RGB(255, 255, 255); + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(230, 230, 230); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(230, 230, 230); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = accentColor; + lib.ui.col.crossBtn = accentColor; + lib.ui.col.filterBtn = RGB(230, 230, 230); + lib.ui.col.settingsBtn = RGB(230, 230, 230); + lib.ui.col.line = RGB(12, 21, 31); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = RGB(220, 220, 220); + lib.ui.col.sbarNormal = RGB(36, 84, 143); + lib.ui.col.sbarHovered = accentColor; + lib.ui.col.sbarDrag = accentColor; + } + + /** + * The Biography colors for Dark blue theme used in Options > Theme > Dark blue. + */ + biographyColorsDarkblueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(255, 202, 128); + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = pl.col.header_artist_playing; + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = accentColor; + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = accentColor; + bio.ui.col.noPhotoStubBg = RGB(24, 50, 82); + bio.ui.col.noPhotoStubText = pl.col.header_artist_playing; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = RGB(220, 220, 220); + bio.ui.col.sbarNormal = RGB(36, 84, 143); + bio.ui.col.sbarHovered = accentColor; + bio.ui.col.sbarDrag = accentColor; + } + + /** + * The Main colors for Dark blue theme used in Options > Theme > Dark blue. + */ + mainColorsDarkblueTheme() { + // * MAIN COLORS * // + const accentColor = RGB(255, 202, 128); + grCol.bg = RGB(22, 40, 63); + grCol.loadingThemeBg = RGB(22, 40, 63); + grCol.uiHacksFrame = RGB(27, 55, 90); + grCol.shadow = RGBA(0, 0, 0, 75); + grCol.discArtShadow = RGBA(0, 0, 0, 80); + grCol.noAlbumArtStub = accentColor; + grCol.lowerBarArtist = accentColor; + grCol.lowerBarTitle = RGB(230, 230, 230); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = accentColor; + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = RGB(21, 37, 56); + grCol.detailsText = RGB(255, 255, 255); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = accentColor; + grCol.timelinePlayed = RGB(204, 161, 102); + grCol.timelineUnplayed = RGB(155, 110, 70); + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = accentColor; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? RGB(27, 55, 90) : + this.TMB === 'inner' ? RGB(38, 70, 110) : + RGB(27, 55, 90); + + grCol.menuStyleBg = this.TMB === 'emboss' ? RGB(27, 48, 77) : this.BEVEL ? RGB(22, 40, 60) : RGB(25, 45, 70); + grCol.menuRectStyleEmbossTop = RGB(35, 70, 115); + grCol.menuRectStyleEmbossBottom = RGB(6, 10, 15); + grCol.menuRectNormal = this.TMB === 'filled' ? RGBA(200, 200, 200, 140) : RGB(200, 200, 200); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(50, 90, 150, 140) : + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(15, 25, 40) : RGB(18, 30, 50) : + RGB(50, 90, 150); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = RGB(230, 230, 230); + grCol.menuTextHovered = RGB(255, 255, 255); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = RGB(27, 55, 90); + grCol.transportEllipseNormal = RGB(20, 33, 48); + grCol.transportEllipseHovered = RGB(50, 90, 150); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' ? this.BEVEL ? RGB(22, 40, 60) : RGB(25, 45, 70) : + this.TPB === 'inner' ? this.GRAD2 ? RGB(22, 40, 60) : RGB(25, 45, 70) : + this.TPB === 'emboss' ? RGB(27, 55, 90) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(38, 70, 110) : + this.TPB === 'emboss' ? RGB(35, 70, 115) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(15, 25, 40) : RGB(18, 30, 50) : + this.TPB === 'emboss' ? RGB(20, 36, 50) : ''; + + grCol.transportIconNormal = accentColor; + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = RGB(27, 55, 90); + grCol.progressBarStreaming = accentColor; + grCol.progressBarFrame = RGB(22, 37, 54); + grCol.progressBarFill = accentColor; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = TintColor(accentColor, 40); + grCol.peakmeterBarFillTop = TintColor(accentColor, 10); + grCol.peakmeterBarFillMiddle = TintColor(accentColor, 20); + grCol.peakmeterBarFillBack = ShadeColor(accentColor, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = accentColor; + grCol.peakmeterBarVertFillPeaks = TintColor(accentColor, 80); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = accentColor; + grCol.waveformBarFillBack = ShadeColor(accentColor, 20); + grCol.waveformBarFillPreFront = RGB(65, 110, 180); + grCol.waveformBarFillPreBack = RGB(45, 80, 130); + grCol.waveformBarIndicator = RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(27, 55, 90); + grCol.volumeBarFrame = RGB(20, 33, 48); + grCol.volumeBarFill = accentColor; + + // * STYLE COLORS * // + grCol.styleBevel = RGB(0, 0, 0); + grCol.styleGradient = RGBA(0, 0, 0, 140); + grCol.styleGradient2 = RGB(10, 20, 35); + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 60) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 160) : RGBA(0, 0, 0, 80) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 70) : + this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? RGB(30, 62, 102) : + this.PB === 'inner' ? RGB(30, 62, 102) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 60) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + } + // #endregion + + // * PUBLIC METHODS - RED THEME * // + // #region PUBLIC METHODS - RED THEME + /** + * The Playlist colors for Red theme used in Options > Theme > Red. + */ + playlistColorsRedTheme() { + // * MAIN COLORS * // + const accentColor = RGB(245, 212, 165); + pl.col.bg = RGB(110, 20, 20); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(220, 220, 220); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(220, 220, 220) : RGB(255, 255, 255); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(255, 255, 255) : RGB(220, 220, 220); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = RGB(130, 25, 25); + pl.col.header_sideMarker = accentColor; + pl.col.header_artist_normal = RGB(240, 240, 240); + pl.col.header_artist_playing = accentColor; + pl.col.header_album_normal = RGB(220, 220, 220); + pl.col.header_album_playing = RGB(245, 245, 245); + pl.col.header_info_normal = RGB(220, 220, 220); + pl.col.header_info_playing = RGB(245, 245, 245); + pl.col.header_date_normal = RGB(220, 220, 220); + pl.col.header_date_playing = RGB(245, 245, 245); + pl.col.header_line_normal = RGB(75, 18, 18); + pl.col.header_line_playing = RGB(75, 18, 18); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(100, 20, 20, 130) : this.ALT ? RGB(130, 25, 25) : RGB(100, 20, 20); + pl.col.row_selection_bg = RGB(110, 20, 20); + pl.col.row_selection_frame = RGB(145, 25, 25); + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(220, 220, 220); + pl.col.row_title_playing = RGB(255, 255, 255); + pl.col.row_title_selected = RGB(255, 255, 255); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = RGB(75, 18, 18); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = RGB(220, 220, 220); + pl.col.sbar_btn_hovered = RGB(255, 255, 255); + pl.col.sbar_thumb_normal = RGB(145, 25, 25); + pl.col.sbar_thumb_hovered = accentColor; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for Red theme used in Options > Theme > Red. + */ + libraryColorsRedTheme() { + // * MAIN COLORS * // + const accentColor = RGB(245, 212, 165); + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = accentColor; + lib.ui.col.iconPlus_h = RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = RGB(255, 255, 255); + lib.ui.col.iconPlusBg = RGB(140, 25, 25); + lib.ui.col.iconMinus_e = accentColor; + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(255, 255, 255); + + // * TEXT COLORS * // + lib.ui.col.text = RGB(230, 230, 230); + lib.ui.col.text_h = RGB(255, 255, 255); + lib.ui.col.text_nowp = accentColor; + lib.ui.col.textSel = RGB(255, 255, 255); + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(230, 230, 230); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(230, 230, 230); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = accentColor; + lib.ui.col.crossBtn = accentColor; + lib.ui.col.filterBtn = RGB(230, 230, 230); + lib.ui.col.settingsBtn = RGB(230, 230, 230); + lib.ui.col.line = RGB(75, 18, 18); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = RGB(220, 220, 220); + lib.ui.col.sbarNormal = RGB(198, 32, 32); + lib.ui.col.sbarHovered = accentColor; + lib.ui.col.sbarDrag = accentColor; + } + + /** + * The Biography colors for Red theme used in Options > Theme > Red. + */ + biographyColorsRedTheme() { + // * MAIN COLORS * // + const accentColor = RGB(245, 212, 165); + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = pl.col.header_artist_playing; + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = accentColor; + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = accentColor; + bio.ui.col.noPhotoStubBg = RGB(130, 25, 25); + bio.ui.col.noPhotoStubText = pl.col.header_artist_playing; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = RGB(220, 220, 220); + bio.ui.col.sbarNormal = RGB(198, 32, 32); + bio.ui.col.sbarHovered = accentColor; + bio.ui.col.sbarDrag = accentColor; + } + + /** + * The Main colors for Red theme used in Options > Theme > Red. + */ + mainColorsRedTheme() { + // * MAIN COLORS * // + const accentColor = RGB(245, 212, 165); + grCol.bg = RGB(100, 20, 20); + grCol.loadingThemeBg = RGB(100, 20, 20); + grCol.uiHacksFrame = RGB(125, 0, 0); + grCol.shadow = RGBA(0, 0, 0, 75); + grCol.discArtShadow = RGBA(0, 0, 0, 80); + grCol.noAlbumArtStub = accentColor; + grCol.lowerBarArtist = accentColor; + grCol.lowerBarTitle = RGB(220, 220, 220); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = accentColor; + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = RGB(110, 20, 20); + grCol.detailsText = RGB(255, 255, 255); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = accentColor; + grCol.timelinePlayed = RGB(207, 170, 118); + grCol.timelineUnplayed = RGB(170, 120, 95); + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = accentColor; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? RGB(140, 25, 25) : + this.TMB === 'inner' ? RGB(160, 32, 32) : + RGB(140, 25, 25); + + grCol.menuStyleBg = this.TMB === 'emboss' ? RGB(125, 25, 25) : RGB(100, 20, 20); + grCol.menuRectStyleEmbossTop = RGB(158, 30, 30); + grCol.menuRectStyleEmbossBottom = RGB(54, 10, 10); + grCol.menuRectNormal = this.TMB === 'filled' ? RGBA(200, 200, 200, 140) : RGB(200, 200, 200); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(204, 45, 45, 140) : + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(66, 13, 13) : RGB(77, 15, 15) : + RGB(204, 45, 45); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = RGB(220, 220, 220); + grCol.menuTextHovered = RGB(255, 255, 255); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = RGB(140, 25, 25); + grCol.transportEllipseNormal = RGB(82, 19, 19); + grCol.transportEllipseHovered = RGB(204, 45, 45); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(100, 20, 20) : + this.TPB === 'emboss' ? RGB(140, 25, 25) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(160, 32, 32) : + this.TPB === 'emboss' ? RGB(166, 30, 30) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(66, 13, 13) : RGB(77, 15, 15) : + this.TPB === 'emboss' ? this.BEVEL && !this.GRAD2 ? RGB(80, 15, 15) : this.GRAD2 ? RGB(54, 10, 10) : RGB(80, 15, 15) : ''; + + grCol.transportIconNormal = accentColor; + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = RGB(140, 25, 25); + grCol.progressBarStreaming = accentColor; + grCol.progressBarFrame = RGB(92, 21, 21); + grCol.progressBarFill = accentColor; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = TintColor(accentColor, 40); + grCol.peakmeterBarFillTop = TintColor(accentColor, 10); + grCol.peakmeterBarFillMiddle = TintColor(accentColor, 20); + grCol.peakmeterBarFillBack = ShadeColor(accentColor, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = accentColor; + grCol.peakmeterBarVertFillPeaks = TintColor(accentColor, 80); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = accentColor; + grCol.waveformBarFillBack = ShadeColor(accentColor, 20); + grCol.waveformBarFillPreFront = RGB(230, 45, 45); + grCol.waveformBarFillPreBack = RGB(180, 35, 35); + grCol.waveformBarIndicator = RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(140, 25, 25); + grCol.volumeBarFrame = RGB(82, 19, 19); + grCol.volumeBarFill = accentColor; + + // * STYLE COLORS * // + grCol.styleBevel = RGB(0, 0, 0); + grCol.styleGradient = RGBA(0, 0, 0, 90); + grCol.styleGradient2 = RGB(65, 13, 13); + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 50) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 70) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 80) : RGBA(0, 0, 0, 60) : + this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? RGB(145, 28, 28) : + this.PB === 'inner' ? this.BEVEL ? RGB(158, 30, 30) : RGB(145, 28, 28) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 50) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + } + // #endregion + + // * PUBLIC METHODS - CREAM THEME * // + // #region PUBLIC METHODS - CREAM THEME + /** + * The Playlist colors for Cream theme used in Options > Theme > Cream. + */ + playlistColorsCreamTheme() { + // * MAIN COLORS * // + const accentColor = RGB(120, 170, 130); + pl.col.bg = RGB(255, 247, 245); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(220, 220, 220); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(110, 110, 110) : RGB(80, 80, 80); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(80, 80, 80) : RGB(130, 130, 130); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + pl.col.header_sideMarker = pl.col.header_nowplaying_bg; + pl.col.header_artist_normal = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + pl.col.header_artist_playing = RGB(255, 255, 255); + pl.col.header_album_normal = this.BLEND ? RGB(80, 80, 80) : RGB(110, 110, 110); + pl.col.header_album_playing = RGB(245, 245, 245); + pl.col.header_info_normal = this.BLEND ? RGB(80, 80, 80) : RGB(110, 110, 110); + pl.col.header_info_playing = RGB(245, 245, 245); + pl.col.header_date_normal = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + pl.col.header_date_playing = RGB(245, 245, 245); + pl.col.header_line_normal = RGB(200, 200, 200); + pl.col.header_line_playing = RGB(220, 220, 220); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(255, 255, 255, 130) : this.ALT2 ? RGB(255, 247, 240) : RGB(255, 255, 255); + pl.col.row_selection_bg = RGB(200, 200, 200); + pl.col.row_selection_frame = pl.col.row_selection_bg; + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = this.BLEND ? RGB(60, 60, 60) : RGB(90, 90, 90); + pl.col.row_title_playing = RGB(245, 245, 245); + pl.col.row_title_selected = RGB(0, 0, 0); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = RGB(200, 200, 200); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = accentColor; + pl.col.sbar_btn_hovered = RGB(100, 100, 100); + pl.col.sbar_thumb_normal = RGB(200, 200, 200); + pl.col.sbar_thumb_hovered = accentColor; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for Cream theme used in Options > Theme > Cream. + */ + libraryColorsCreamTheme() { + // * MAIN COLORS * // + const accentColor = RGB(120, 170, 130); + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + lib.ui.col.iconPlus_h = RGB(0, 0, 0); + lib.ui.col.iconPlus_sel = ['modern', 'facet'].includes(grSet.libraryDesign) || !lib.pop.highlight.nowPlaying ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.iconPlusBg = RGB(255, 255, 255); + lib.ui.col.iconMinus_e = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(0, 0, 0); + + // * TEXT COLORS * // + lib.ui.col.text = + libSet.albumArtShow && libImg.labels.overlayDark ? RGB(220, 220, 220) : + this.BLEND ? RGB(65, 65, 65) : + RGB(90, 90, 90); + + lib.ui.col.text_h = + libSet.albumArtShow && libImg.labels.overlayDark ? RGB(255, 255, 255) : + ['modern', 'facet'].includes(grSet.libraryDesign) && lib.panel.imgView ? RGB(255, 255, 255) : + RGB(0, 0, 0); + + lib.ui.col.text_nowp = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(0, 0, 0) : RGB(255, 255, 255); + + lib.ui.col.textSel = + !['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(grSet.libraryDesign) || ![2, 1].includes(libSet.albumArtLabelType) || + (['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(grSet.libraryDesign) || [2, 1].includes(libSet.albumArtLabelType)) && !lib.pop.highlight.nowPlaying ? + libSet.albumArtShow && libImg.labels.overlayDark ? RGB(0, 0, 0) : RGB(255, 255, 255) : + lib.ui.col.text_h; + + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = this.BLEND ? RGB(60, 60, 60) : RGB(90, 90, 90); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = this.BLEND ? RGB(60, 60, 60) : RGB(90, 90, 90); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + lib.ui.col.crossBtn = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + lib.ui.col.filterBtn = this.BLEND ? RGB(60, 60, 60) : RGB(120, 120, 120); + lib.ui.col.settingsBtn = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + lib.ui.col.line = RGB(200, 200, 200); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = accentColor; + lib.ui.col.sbarNormal = RGB(116, 127, 129); + lib.ui.col.sbarHovered = accentColor; + lib.ui.col.sbarDrag = accentColor; + } + + /** + * The Biography colors for Cream theme used in Options > Theme > Cream. + */ + biographyColorsCreamTheme() { + // * MAIN COLORS * // + const accentColor = RGB(120, 170, 130); + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = this.BLEND ? RGB(60, 60, 60) : RGB(100, 100, 100); + bio.ui.col.bottomLine = (bio.ui.blur.blend || bio.ui.blur.light) ? RGB(120, 120, 120) : pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = RGB(255, 255, 255); + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + bio.ui.col.noPhotoStubBg = RGB(255, 247, 240); + bio.ui.col.noPhotoStubText = accentColor; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = accentColor; + bio.ui.col.sbarNormal = RGB(116, 127, 129); + bio.ui.col.sbarHovered = accentColor; + bio.ui.col.sbarDrag = accentColor; + } + + /** + * The Main colors for Cream theme used in Options > Theme > Cream. + */ + mainColorsCreamTheme() { + // * MAIN COLORS * // + const accentColor = RGB(120, 170, 130); + grCol.bg = RGB(255, 247, 240); + grCol.loadingThemeBg = RGB(255, 247, 240); + grCol.uiHacksFrame = RGB(255, 247, 240); + grCol.shadow = RGBA(0, 0, 0, 25); + grCol.discArtShadow = RGBA(0, 0, 0, 10); + grCol.noAlbumArtStub = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + grCol.lowerBarArtist = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + grCol.lowerBarTitle = this.BLEND || this.BLEND2 ? RGB(90, 90, 90) : RGB(100, 100, 100); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = RGB(255, 247, 245); + grCol.detailsText = this.BLEND ? RGB(80, 80, 80) : RGB(120, 120, 120); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = accentColor; + grCol.timelinePlayed = RGB(139, 196, 151); + grCol.timelineUnplayed = RGB(158, 222, 171); + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = RGB(255, 255, 255); + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? RGB(255, 255, 255) : + this.TMB === 'inner' ? RGB(255, 255, 255) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(250, 250, 250) : RGB(255, 255, 255) : + RGB(247, 239, 233); + + grCol.menuStyleBg = this.TMB === 'emboss' ? RGB(240, 230, 220) : this.BEVEL ? RGB(212, 205, 200) : RGB(229, 222, 216); + grCol.menuRectStyleEmbossTop = this.BEVEL ? RGB(235, 235, 235) : RGB(255, 255, 255); + grCol.menuRectStyleEmbossBottom = this.BEVEL ? RGB(205, 205, 205) : RGB(215, 215, 215); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGB(100, 150, 110, 100) : + this.BLEND || this.BLEND2 ? RGB(150, 150, 150) : + RGB(100, 150, 110); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(190, 190, 190, 100) : + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(200, 200, 200) : RGB(220, 220, 220) : + this.BLEND || this.BLEND2 ? RGB(150, 150, 150) : + RGB(190, 190, 190); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : RGB(100, 150, 110); + grCol.menuTextHovered = this.BLEND ? RGB(60, 60, 60) : RGB(100, 100, 100); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = RGB(255, 255, 255); + grCol.transportEllipseNormal = RGB(220, 220, 220); + grCol.transportEllipseHovered = RGB(200, 200, 200); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(212, 205, 200) : RGB(229, 222, 216) : + this.TPB === 'emboss' ? RGB(240, 225, 210) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(255, 255, 255) : + this.TPB === 'emboss' ? RGB(255, 255, 255) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? this.BEVEL ? RGB(190, 190, 190) : RGB(220, 220, 220) : + this.TPB === 'emboss' ? this.BEVEL ? RGB(210, 210, 210) : RGB(225, 225, 225) : ''; + + grCol.transportIconNormal = this.BLEND || this.BLEND2 || this.TPB === 'minimal' ? RGB(65, 135, 80) : RGB(100, 150, 110); + grCol.transportIconHovered = RGB(100, 100, 100); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + this.PB === 'bevel' ? RGB(240, 240, 240) : + this.BEVEL ? RGB(225, 225, 225) : RGB(255, 255, 255); + + grCol.progressBarStreaming = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + grCol.progressBarFrame = RGB(230, 230, 230); + grCol.progressBarFill = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = TintColor(accentColor, 40); + grCol.peakmeterBarFillTop = TintColor(accentColor, 10); + grCol.peakmeterBarFillMiddle = TintColor(accentColor, 20); + grCol.peakmeterBarFillBack = ShadeColor(accentColor, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = accentColor; + grCol.peakmeterBarVertFillPeaks = ShadeColor(accentColor, 20); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = this.BEVEL || this.BLEND ? TintColor(accentColor, 10) : accentColor; + grCol.waveformBarFillBack = this.BEVEL || this.BLEND ? ShadeColor(accentColor, 30) : ShadeColor(accentColor, 20); + grCol.waveformBarFillPreFront = this.BEVEL || this.BLEND ? RGB(205, 200, 190) : RGB(180, 175, 165); + grCol.waveformBarFillPreBack = this.BEVEL || this.BLEND ? RGB(115, 110, 105) : RGB(140, 135, 130); + grCol.waveformBarIndicator = RGB(60, 60, 60); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(255, 255, 255); + grCol.volumeBarFrame = RGB(220, 220, 220); + grCol.volumeBarFill = this.BLEND || this.BLEND2 ? RGB(65, 135, 80) : accentColor; + + // * STYLE COLORS * // + grCol.styleBevel = RGB(0, 0, 0); + grCol.styleGradient = ''; + grCol.styleGradient2 = ''; + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 25) : + this.BEVEL ? RGBA(0, 0, 0, 20) : RGBA(0, 0, 0, 10) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 35) : RGBA(0, 0, 0, 30) : + this.BEVEL ? RGBA(0, 0, 0, 15) : RGBA(0, 0, 0, 10) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 140) : RGBA(0, 0, 0, 15) : + this.BEVEL ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 255) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 140) : RGBA(0, 0, 0, 15) : + this.BEVEL ? RGBA(255, 255, 255, 120) : RGBA(0, 0, 0, 10) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + } + // #endregion + + // * PUBLIC METHODS - NEON THEMES * // + // #region PUBLIC METHODS - NEON THEMES + /** + * The Playlist colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. + */ + playlistColorsNeonThemes() { + // * MAIN COLORS * // + const accentColor = ({ + nblue: RGB(0, 200, 255), + ngreen: RGB(0, 200, 0), + nred: RGB(240, 10, 60), + ngold: RGB(255, 205, 5) + }[this.THEME]); + + const accentColorLight = ({ + nblue: RGB(0, 238, 255), + ngreen: RGB(0, 255, 0), + nred: RGB(255, 8, 8), + ngold: RGB(255, 242, 3) + }[this.THEME]); + + pl.col.bg = RGB(10, 10, 10); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(200, 200, 200); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(220, 220, 220) : RGB(255, 255, 255); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(255, 255, 255) : RGB(200, 200, 200); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = this.BLEND || this.BLEND2 ? RGBA(25, 25, 25, 100) : RGB(25, 25, 25); + pl.col.header_sideMarker = accentColor; + pl.col.header_artist_normal = RGB(240, 240, 240); + pl.col.header_artist_playing = accentColor; + pl.col.header_album_normal = RGB(220, 220, 220); + pl.col.header_album_playing = RGB(240, 240, 240); + pl.col.header_info_normal = RGB(220, 220, 220); + pl.col.header_info_playing = RGB(240, 240, 240); + pl.col.header_date_normal = RGB(220, 220, 220); + pl.col.header_date_playing = accentColor; + pl.col.header_line_normal = RGB(45, 45, 45); + pl.col.header_line_playing = RGB(50, 50, 50); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(20, 20, 20, 130) : this.ALT2 ? RGB(35, 35, 35) : RGB(20, 20, 20); + pl.col.row_selection_bg = RGB(10, 10, 10); + pl.col.row_selection_frame = RGB(40, 40, 40); + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(200, 200, 200); + pl.col.row_title_playing = RGB(255, 255, 255); + pl.col.row_title_selected = RGB(255, 255, 255); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = RGB(45, 45, 45); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = accentColor; + pl.col.sbar_btn_hovered = accentColorLight; + pl.col.sbar_thumb_normal = accentColor; + pl.col.sbar_thumb_hovered = accentColorLight; + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + } + + /** + * The Library colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. + */ + libraryColorsNeonThemes() { + // * MAIN COLORS * // + const accentColor = ({ + nblue: RGB(0, 200, 255), + ngreen: RGB(0, 200, 0), + nred: RGB(240, 10, 60), + ngold: RGB(255, 205, 5) + }[this.THEME]); + + const accentColorLight = ({ + nblue: RGB(0, 238, 255), + ngreen: RGB(0, 255, 0), + nred: RGB(255, 8, 8), + ngold: RGB(255, 242, 3) + }[this.THEME]); + + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = libSet.albumArtShow ? TintColor(pl.col.row_nowplaying_bg, 7) : pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * NODE COLORS * // + lib.ui.col.iconPlus = pl.col.header_artist_playing; + lib.ui.col.iconPlus_h = RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = RGB(255, 255, 255); + lib.ui.col.iconPlusBg = RGB(45, 45, 45); + lib.ui.col.iconMinus_e = pl.col.header_artist_playing; + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(255, 255, 255); + + // * TEXT COLORS * // + lib.ui.col.text = RGB(200, 200, 200); + lib.ui.col.text_h = RGB(255, 255, 255); + lib.ui.col.text_nowp = pl.col.header_artist_playing; + lib.ui.col.textSel = RGB(255, 255, 255); + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(200, 200, 200); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(200, 200, 200); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = pl.col.header_artist_playing; + lib.ui.col.crossBtn = pl.col.header_artist_playing; + lib.ui.col.filterBtn = RGB(200, 200, 200); + lib.ui.col.settingsBtn = pl.col.header_artist_playing; + lib.ui.col.line = RGB(45, 45, 45); + lib.ui.col.s_line = lib.ui.col.line; + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = accentColor; + lib.ui.col.sbarNormal = accentColor; + lib.ui.col.sbarHovered = accentColorLight; + lib.ui.col.sbarDrag = accentColorLight; + } + + /** + * The Biography colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. + */ + biographyColorsNeonThemes() { + // * MAIN COLORS * // + const accentColor = ({ + nblue: RGB(0, 200, 255), + ngreen: RGB(0, 200, 0), + nred: RGB(240, 10, 60), + ngold: RGB(255, 205, 5) + }[this.THEME]); + + const accentColorLight = ({ + nblue: RGB(0, 238, 255), + ngreen: RGB(0, 255, 0), + nred: RGB(255, 8, 8), + ngold: RGB(255, 242, 3) + }[this.THEME]); + + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * HEADER COLORS * // + bio.ui.col.headingText = pl.col.header_artist_playing; + bio.ui.col.bottomLine = RGB(55, 55, 55); + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + + // * POPUP COLORS * // + bio.ui.col.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + bio.ui.col.popupText = accentColor; + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = bio.ui.col.text; + bio.ui.col.lyricsHighlight = accentColor; + bio.ui.col.noPhotoStubBg = RGB(25, 25, 25); + bio.ui.col.noPhotoStubText = pl.col.header_artist_playing; + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = accentColor; + bio.ui.col.sbarNormal = accentColor; + bio.ui.col.sbarHovered = accentColorLight; + bio.ui.col.sbarDrag = accentColorLight; + } + + /** + * The Main colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. + */ + mainColorsNeonThemes() { + // * MAIN COLORS * // + const accentColor = ({ + nblue: RGB(0, 200, 255), + ngreen: RGB(0, 200, 0), + nred: RGB(240, 10, 60), + ngold: RGB(255, 205, 5) + }[this.THEME]); + + const accentColorLight = ({ + nblue: RGB(0, 238, 255), + ngreen: RGB(0, 255, 0), + nred: RGB(255, 8, 8), + ngold: RGB(255, 242, 3) + }[this.THEME]); + + const accentColorDark = ({ + nblue: RGB(0, 160, 205), + ngreen: RGB(0, 150, 0), + nred: RGB(180, 5, 35), + ngold: RGB(200, 160, 0) + }[this.THEME]); + + const accentColorDarker = ({ + nblue: RGB(0, 120, 155), + ngreen: RGB(0, 100, 0), + nred: RGB(130, 5, 25), + ngold: RGB(150, 120, 0) + }[this.THEME]); + + grCol.bg = this.BEVEL ? RGB(30, 30, 30) : RGB(20, 20, 20); + grCol.loadingThemeBg = RGB(20, 20, 20); + grCol.uiHacksFrame = RGB(30, 30, 30); + grCol.shadow = RGBA(0, 0, 0, 255); + grCol.discArtShadow = RGBA(0, 0, 0, 40); + grCol.noAlbumArtStub = accentColor; + grCol.lowerBarArtist = accentColor; + grCol.lowerBarTitle = RGB(220, 220, 220); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + grCol.lyricsNormal = RGB(255, 255, 255); + grCol.lyricsHighlight = accentColor; + grCol.lyricsShadow = RGB(0, 0, 0); + + // * DETAILS COLORS * // + grCol.detailsBg = this.BLEND || this.BLEND2 ? RGBA(10, 10, 10, 100) : RGB(10, 10, 10); + grCol.detailsText = RGB(255, 255, 255); + grCol.detailsRating = RGB(255, 170, 32); + grCol.detailsHotness = grCol.detailsRating; + grCol.timelineAdded = accentColor; + grCol.timelinePlayed = accentColorDark; + grCol.timelineUnplayed = accentColorDarker; + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = RGBAtoRGB(pl.col.header_nowplaying_bg, 255); + grCol.popupText = accentColor; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? this.BEVEL ? RGB(40, 40, 40) : RGB(50, 50, 50) : + this.TMB === 'inner' ? this.BEVEL ? RGB(45, 45, 45) : RGB(50, 50, 50) : + RGB(50, 50, 50); + + grCol.menuStyleBg = + this.TMB === 'emboss' ? this.BEVEL ? RGB(35, 35, 35) : RGB(40, 40, 40) : + this.BEVEL ? RGB(25, 25, 25) : RGB(20, 20, 20); + + grCol.menuRectStyleEmbossTop = RGB(60, 60, 60); + grCol.menuRectStyleEmbossBottom = RGB(0, 0, 0); + grCol.menuRectNormal = this.TMB === 'filled' ? RGBtoRGBA(accentColor, 80) : accentColor; + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBtoRGBA(accentColorLight, 80) : + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(0, 0, 0) : + accentColorLight; + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = accentColor; + grCol.menuTextHovered = accentColorLight; + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = RGB(35, 35, 35); + grCol.transportEllipseNormal = RGB(50, 50, 50); + grCol.transportEllipseHovered = accentColorLight; + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(0, 0, 0) : + this.TPB === 'emboss' ? RGB(50, 50, 50) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(50, 50, 50) : + this.TPB === 'emboss' ? this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(0, 0, 0) : + this.TPB === 'emboss' ? RGB(10, 10, 10) : ''; + + grCol.transportIconNormal = accentColor; + grCol.transportIconHovered = accentColorLight; + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = RGB(35, 35, 35); + grCol.progressBarStreaming = accentColorLight; + grCol.progressBarFrame = grCol.bg; + grCol.progressBarFill = accentColor; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = TintColor(accentColor, 40); + grCol.peakmeterBarFillTop = TintColor(accentColor, 10); + grCol.peakmeterBarFillMiddle = TintColor(accentColor, 20); + grCol.peakmeterBarFillBack = ShadeColor(accentColor, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = accentColor; + grCol.peakmeterBarVertFillPeaks = TintColor(accentColor, 60); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = accentColor; + grCol.waveformBarFillBack = ShadeColor(accentColor, 20); + grCol.waveformBarFillPreFront = RGB(100, 100, 100); + grCol.waveformBarFillPreBack = RGB(80, 80, 80); + grCol.waveformBarIndicator = RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(30, 30, 30); + grCol.volumeBarFrame = RGB(50, 50, 50); + grCol.volumeBarFill = accentColor; + + // * STYLE COLORS * // + grCol.styleBevel = RGB(0, 0, 0); + grCol.styleGradient = ''; + grCol.styleGradient2 = ''; + grCol.styleAlternative = this.BEVEL ? RGB(30, 30, 30) : RGB(20, 20, 20); + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : ''; + + grCol.styleProgressBarLineTop = RGBA(0, 0, 0, 255); + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? RGBA(255, 255, 255, 20) : + this.BEVEL ? RGBA(255, 255, 255, 25) : RGBA(255, 255, 255, 20) : + this.PB === 'inner' ? this.PBD === 'rounded' ? RGBA(255, 255, 255, 20) : + this.BEVEL ? RGBA(255, 255, 255, 35) : RGBA(255, 255, 255, 25) : + RGBA(0, 0, 0, 100); + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 80) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 80) : ''; + } + // #endregion + + // * PUBLIC METHODS - CUSTOM THEME * // + // #region PUBLIC METHODS - CUSTOM THEME + /** + * The Playlist colors for Custom theme used in Options > Theme > Custom. + */ + playlistColorsCustomTheme() { + try { + // * MAIN COLORS * // + pl.col.bg = HEXtoRGB(grCfg.cTheme.pl_col_bg); + + // * PLAYLIST MANAGER COLORS * // + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? HEXtoRGB(grCfg.cTheme.pl_col_bg) : HEXtoRGB(grCfg.cTheme.pl_col_plman_text_normal); + pl.col.plman_text_hovered = HEXtoRGB(grCfg.cTheme.pl_col_plman_text_hovered); + pl.col.plman_text_pressed = HEXtoRGB(grCfg.cTheme.pl_col_plman_text_pressed); + + // * HEADER COLORS * // + pl.col.header_nowplaying_bg = this.BLEND ? HEXtoRGBA(grCfg.cTheme.pl_col_header_nowplaying_bg, 130) : HEXtoRGB(grCfg.cTheme.pl_col_header_nowplaying_bg); + pl.col.header_sideMarker = HEXtoRGB(grCfg.cTheme.pl_col_header_sideMarker); + pl.col.header_artist_normal = HEXtoRGB(grCfg.cTheme.pl_col_header_artist_normal); + pl.col.header_artist_playing = HEXtoRGB(grCfg.cTheme.pl_col_header_artist_playing); + pl.col.header_album_normal = HEXtoRGB(grCfg.cTheme.pl_col_header_album_normal); + pl.col.header_album_playing = HEXtoRGB(grCfg.cTheme.pl_col_header_album_playing); + pl.col.header_info_normal = HEXtoRGB(grCfg.cTheme.pl_col_header_info_normal); + pl.col.header_info_playing = HEXtoRGB(grCfg.cTheme.pl_col_header_info_playing); + pl.col.header_date_normal = HEXtoRGB(grCfg.cTheme.pl_col_header_date_normal); + pl.col.header_date_playing = HEXtoRGB(grCfg.cTheme.pl_col_header_date_playing); + pl.col.header_line_normal = HEXtoRGB(grCfg.cTheme.pl_col_header_line_normal); + pl.col.header_line_playing = HEXtoRGB(grCfg.cTheme.pl_col_header_line_playing); + + // * ROW COLORS * // + pl.col.row_nowplaying_bg = this.BLEND ? HEXtoRGBA(grCfg.cTheme.pl_col_row_nowplaying_bg, 130) : HEXtoRGB(grCfg.cTheme.pl_col_row_nowplaying_bg); + pl.col.row_stripes_bg = this.BLEND ? HEXtoRGBA(grCfg.cTheme.pl_col_row_stripes_bg, 130) : HEXtoRGB(grCfg.cTheme.pl_col_row_stripes_bg); + pl.col.row_selection_frame = HEXtoRGB(grCfg.cTheme.pl_col_row_selection_frame); + pl.col.row_sideMarker = HEXtoRGB(grCfg.cTheme.pl_col_row_sideMarker); + pl.col.row_title_normal = this.BLEND ? ShadeColor(HEXtoRGB(grCfg.cTheme.pl_col_row_title_normal), 10) : HEXtoRGB(grCfg.cTheme.pl_col_row_title_normal); + pl.col.row_title_playing = HEXtoRGB(grCfg.cTheme.pl_col_row_title_playing); + pl.col.row_title_selected = HEXtoRGB(grCfg.cTheme.pl_col_row_title_selected); + pl.col.row_title_hovered = HEXtoRGB(grCfg.cTheme.pl_col_row_title_hovered); + pl.col.row_rating_color = HEXtoRGB(grCfg.cTheme.pl_col_row_rating_color); + pl.col.row_disc_subheader_line = HEXtoRGB(grCfg.cTheme.pl_col_row_disc_subheader_line); + pl.col.row_drag_line = HEXtoRGB(grCfg.cTheme.pl_col_row_drag_line); + pl.col.row_drag_line_reached = HEXtoRGB(grCfg.cTheme.pl_col_row_drag_line_reached); + + // * SCROLLBAR COLORS * // + pl.col.sbar_btn_normal = HEXtoRGB(grCfg.cTheme.pl_col_sbar_btn_normal); + pl.col.sbar_btn_hovered = HEXtoRGB(grCfg.cTheme.pl_col_sbar_btn_hovered); + pl.col.sbar_thumb_normal = HEXtoRGB(grCfg.cTheme.pl_col_sbar_thumb_normal); + pl.col.sbar_thumb_hovered = HEXtoRGB(grCfg.cTheme.pl_col_sbar_thumb_hovered); + pl.col.sbar_thumb_drag = HEXtoRGB(grCfg.cTheme.pl_col_sbar_thumb_drag); + } + catch (e) { + fb.ShowPopupMessage(`Error when initializing playlist custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${grCfg.configPathCustom}\n`, 'Playlist custom theme color error'); + } + } + + /** + * The Library colors for Custom theme used in Options > Theme > Custom. + */ + libraryColorsCustomTheme() { + try { + // * MAIN COLORS * // + lib.ui.col.bg = this.ALT ? ShadeColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_bg), 5) : this.ALT2 ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_bg), 5) : HEXtoRGB(grCfg.cTheme.lib_ui_col_bg); + lib.ui.col.rowStripes = this.BLEND ? HEXtoRGBA(grCfg.cTheme.lib_ui_col_rowStripes, 130) : HEXtoRGB(grCfg.cTheme.lib_ui_col_rowStripes); + + // * ROW COLORS * // + lib.ui.col.nowPlayingBg = this.BLEND ? HEXtoRGBA(grCfg.cTheme.lib_ui_col_nowPlayingBg, 130) : HEXtoRGB(grCfg.cTheme.lib_ui_col_nowPlayingBg); + lib.ui.col.sideMarker = HEXtoRGB(grCfg.cTheme.lib_ui_col_sideMarker); + lib.ui.col.selectionFrame = HEXtoRGB(grCfg.cTheme.lib_ui_col_selectionFrame); + lib.ui.col.selectionFrame2 = HEXtoRGB(grCfg.cTheme.lib_ui_col_selectionFrame2); + lib.ui.col.hoverFrame = HEXtoRGB(grCfg.cTheme.lib_ui_col_hoverFrame); + + // * NODE COLORS * // + lib.ui.col.iconPlus = this.BLEND ? ShadeColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_iconPlus), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_iconPlus); + lib.ui.col.iconPlus_h = HEXtoRGB(grCfg.cTheme.lib_ui_col_iconPlus_h); + lib.ui.col.iconPlus_sel = HEXtoRGB(grCfg.cTheme.lib_ui_col_iconPlus_sel); + lib.ui.col.iconPlusBg = HEXtoRGB(grCfg.cTheme.lib_ui_col_iconPlusBg); + lib.ui.col.iconMinus_e = this.BLEND ? ShadeColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_iconMinus_e), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_iconMinus_e); + lib.ui.col.iconMinus_c = HEXtoRGB(grCfg.cTheme.lib_ui_col_iconMinus_c); + lib.ui.col.iconMinus_h = HEXtoRGB(grCfg.cTheme.lib_ui_col_iconMinus_h); + + // * TEXT COLORS * // + lib.ui.col.text = libSet.albumArtShow && libImg.labels.overlayDark ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_text), 40) : this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_text), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_text); + lib.ui.col.text_h = libSet.albumArtShow && libImg.labels.overlayDark ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_text), 40) : HEXtoRGB(grCfg.cTheme.lib_ui_col_text_h); + lib.ui.col.text_nowp = HEXtoRGB(grCfg.cTheme.lib_ui_col_text_nowp); + lib.ui.col.textSel = libSet.albumArtShow && libSet.albumArtLabelType === 1 ? lib.ui.col.text_nowp : HEXtoRGB(grCfg.cTheme.lib_ui_col_textSel); + lib.ui.col.txt = HEXtoRGB(grCfg.cTheme.lib_ui_col_txt); + lib.ui.col.txt_h = HEXtoRGB(grCfg.cTheme.lib_ui_col_txt_h); + lib.ui.col.txt_box = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_txt_box), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_txt_box); + lib.ui.col.search = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_search), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_search); + + // * BUTTON COLORS * // + lib.ui.col.searchBtn = HEXtoRGB(grCfg.cTheme.lib_ui_col_searchBtn); + lib.ui.col.crossBtn = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_crossBtn), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_crossBtn); + lib.ui.col.filterBtn = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_filterBtn), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_filterBtn); + lib.ui.col.settingsBtn = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.lib_ui_col_settingsBtn), 10) : HEXtoRGB(grCfg.cTheme.lib_ui_col_settingsBtn); + lib.ui.col.line = HEXtoRGB(grCfg.cTheme.lib_ui_col_line); + lib.ui.col.s_line = HEXtoRGB(grCfg.cTheme.lib_ui_col_s_line); + + // * SCROLLBAR COLORS * // + lib.ui.col.sbarBtns = HEXtoRGB(grCfg.cTheme.lib_ui_col_sbarBtns); + lib.ui.col.sbarNormal = HEXtoRGB(grCfg.cTheme.lib_ui_col_sbarNormal); + lib.ui.col.sbarHovered = HEXtoRGB(grCfg.cTheme.lib_ui_col_sbarHovered); + lib.ui.col.sbarDrag = HEXtoRGB(grCfg.cTheme.lib_ui_col_sbarDrag); + } + catch (e) { + fb.ShowPopupMessage(`Error when initializing library custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${grCfg.configPathCustom}\n`, 'Library custom theme color error'); + } + } + + /** + * The Biography colors for Custom theme used in Options > Theme > Custom. + */ + biographyColorsCustomTheme() { + try { + // * MAIN COLORS * // + bio.ui.col.bg = this.ALT ? ShadeColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_bg), 5) : this.ALT2 ? TintColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_bg), 5) : HEXtoRGB(grCfg.cTheme.bio_ui_col_bg); + bio.ui.col.rowStripes = this.BLEND ? HEXtoRGBA(grCfg.cTheme.bio_ui_col_rowStripes, 130) : HEXtoRGB(grCfg.cTheme.bio_ui_col_rowStripes); + + // * HEADER COLORS * // + bio.ui.col.headingText = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_headingText), 10) : HEXtoRGB(grCfg.cTheme.bio_ui_col_headingText); + bio.ui.col.bottomLine = (bio.ui.blur.blend || bio.ui.blur.light) ? ShadeColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_bottomLine), 25) : HEXtoRGB(grCfg.cTheme.bio_ui_col_bottomLine); + bio.ui.col.centerLine = HEXtoRGB(grCfg.cTheme.bio_ui_col_centerLine); + bio.ui.col.sectionLine = HEXtoRGB(grCfg.cTheme.bio_ui_col_sectionLine); + + // * TEXT COLORS * // + bio.ui.col.text = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_text), 10) : HEXtoRGB(grCfg.cTheme.bio_ui_col_text); + bio.ui.col.source = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_source), 10) : HEXtoRGB(grCfg.cTheme.bio_ui_col_source); + bio.ui.col.accent = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_accent), 10) : HEXtoRGB(grCfg.cTheme.bio_ui_col_accent); + bio.ui.col.summary = this.BLEND ? TintColor(HEXtoRGB(grCfg.cTheme.bio_ui_col_summary), 10) : HEXtoRGB(grCfg.cTheme.bio_ui_col_summary); + + // * POPUP COLORS * // + bio.ui.col.popupBg = HEXtoRGB(grCfg.cTheme.grCol_popupBg); + bio.ui.col.popupText = HEXtoRGB(grCfg.cTheme.grCol_popupText); + + // * MISC COLORS * // + bio.ui.col.lyricsNormal = HEXtoRGB(grCfg.cTheme.bio_ui_col_lyricsNormal); + bio.ui.col.lyricsHighlight = HEXtoRGB(grCfg.cTheme.bio_ui_col_lyricsHighlight); + bio.ui.col.noPhotoStubBg = HEXtoRGB(grCfg.cTheme.bio_ui_col_noPhotoStubBg); + bio.ui.col.noPhotoStubText = HEXtoRGB(grCfg.cTheme.bio_ui_col_noPhotoStubText); + + // * SCROLLBAR COLORS * // + bio.ui.col.sbarBtns = HEXtoRGB(grCfg.cTheme.bio_ui_col_sbarBtns); + bio.ui.col.sbarNormal = HEXtoRGB(grCfg.cTheme.bio_ui_col_sbarNormal); + bio.ui.col.sbarHovered = HEXtoRGB(grCfg.cTheme.bio_ui_col_sbarHovered); + bio.ui.col.sbarDrag = HEXtoRGB(grCfg.cTheme.bio_ui_col_sbarDrag); + } + catch (e) { + fb.ShowPopupMessage(`Error when initializing biography custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${grCfg.configPathCustom}\n`, 'Biography custom theme color error'); + } + } + + /** + * The Main colors for Custom theme used in Options > Theme > Custom. + */ + mainColorsCustomTheme() { + try { + const lightImg = grCol.imgBrightness > 180; + const lightBg = new Color(HEXtoRGB(grCfg.cTheme.grCol_bg)).brightness > 200; + const darkBg = new Color(HEXtoRGB(grCfg.cTheme.grCol_bg)).brightness < 50; + const nighttime = (grSet.styleNighttime || grSet.themeDayNightMode && grSet.themeDayNightTime === 'night') && !this.RW; + + // * MAIN COLORS * // + grCol.bg = this.BEVEL ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_bg), grCol.lightBgMain ? 80 : 0) : HEXtoRGB(grCfg.cTheme.grCol_bg); + grCol.loadingThemeBg = nighttime && grCfg.cTheme.grCol_preloaderBg === '' ? RGB(25, 25, 25) : grCfg.cTheme.grCol_preloaderBg !== '' ? HEXtoRGB(grCfg.cTheme.grCol_preloaderBg) : RGB(245, 245, 245); + grCol.uiHacksFrame = nighttime ? RGB(25, 25, 25) : grCol.bg; + grCol.shadow = HEXtoRGBA(grCfg.cTheme.grCol_shadow, grCol.lightBgMain ? 50 : 75); + grCol.discArtShadow = HEXtoRGBA(grCfg.cTheme.grCol_discArtShadow, grCol.lightBgMain ? 50 : 75); + grCol.noAlbumArtStub = HEXtoRGB(grCfg.cTheme.grCol_noAlbumArtStub); + grCol.lowerBarArtist = this.BLEND || this.BLEND2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_lowerBarArtist), 10) : HEXtoRGB(grCfg.cTheme.grCol_lowerBarArtist); + grCol.lowerBarTitle = this.BLEND || this.BLEND2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_lowerBarTitle), 10) : HEXtoRGB(grCfg.cTheme.grCol_lowerBarTitle); + grCol.lowerBarTime = HEXtoRGB(grCfg.cTheme.grCol_lowerBarTime); + grCol.lowerBarLength = HEXtoRGB(grCfg.cTheme.grCol_lowerBarLength); + grCol.lyricsNormal = HEXtoRGB(grCfg.cTheme.grCol_lyricsNormal); + grCol.lyricsHighlight = HEXtoRGB(grCfg.cTheme.grCol_lyricsHighlight); + grCol.lyricsShadow = HEXtoRGB(grCfg.cTheme.grCol_lyricsShadow); + + // * DETAILS COLORS * // + grCol.detailsBg = HEXtoRGB(grCfg.cTheme.grCol_detailsBg); + grCol.detailsText = HEXtoRGB(grCfg.cTheme.grCol_detailsText); + grCol.detailsRating = HEXtoRGB(grCfg.cTheme.grCol_detailsRating); + grCol.detailsHotness = grCol.detailsRating; + + grCol.timelineAdded = + grm.ui.isStreaming ? RGB(207, 0, 5) : + grCol.lightBgDetails ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_timelinePlayed), this.BLEND ? 35 : 0) : + HEXtoRGB(grCfg.cTheme.grCol_timelineAdded); + + grCol.timelinePlayed = + grm.ui.isStreaming ? RGB(207, 0, 5) : + grCol.lightBgDetails ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_timelinePlayed), this.BLEND ? 35 : 0) : + HEXtoRGB(grCfg.cTheme.grCol_timelinePlayed); + + grCol.timelineUnplayed = + grm.ui.isStreaming ? RGB(207, 0, 5) : + grCol.lightBgDetails ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_timelineUnplayed), this.BLEND ? 20 : 0) : + HEXtoRGB(grCfg.cTheme.grCol_timelineUnplayed); + + grCol.timelineFrame = HEXtoRGB(grCfg.cTheme.grCol_timelineFrame); + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = HEXtoRGB(grCfg.cTheme.grCol_popupBg); + grCol.popupText = HEXtoRGB(grCfg.cTheme.grCol_popupText); + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.BLEND || this.BLEND2 ? + grCol.lightBgMain ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_menuBgColor), 10) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuBgColor), 10) : + HEXtoRGB(grCfg.cTheme.grCol_menuBgColor); + + grCol.menuStyleBg = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? grCol.lightBgMain ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_menuStyleBg), 10) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuStyleBg), 10) : HEXtoRGB(grCfg.cTheme.grCol_menuStyleBg) : + this.TMB === 'emboss' ? + grCol.lightBgMain ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_menuStyleBg), 10) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuStyleBg), 10) : ''; + + grCol.menuRectStyleEmbossTop = HEXtoRGB(grCfg.cTheme.grCol_menuRectStyleEmbossTop); + grCol.menuRectStyleEmbossBottom = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectStyleEmbossBottom), 10) : HEXtoRGB(grCfg.cTheme.grCol_menuRectStyleEmbossBottom); + + grCol.menuRectNormal = + this.BLEND || this.BLEND2 ? this.BEVEL ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectNormal), 10) : HEXtoRGB(grCfg.cTheme.grCol_menuRectNormal) : + this.ALT2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectNormal), 10) : + HEXtoRGB(grCfg.cTheme.grCol_menuRectNormal); + + grCol.menuRectHovered = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectHovered), 15) : ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectHovered), 5) : + this.BLEND || this.BLEND2 ? + this.BEVEL ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectHovered), 10) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectHovered), 5) : + this.ALT2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuRectHovered), 10) : + HEXtoRGB(grCfg.cTheme.grCol_menuRectHovered); + + grCol.menuRectDown = HEXtoRGB(grCfg.cTheme.grCol_menuRectDown); + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_menuTextNormal), 10) : HEXtoRGB(grCfg.cTheme.grCol_menuTextNormal); + grCol.menuTextHovered = HEXtoRGB(grCfg.cTheme.grCol_menuTextHovered); + grCol.menuTextDown = HEXtoRGB(grCfg.cTheme.grCol_menuTextDown); + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = + this.BLEND || this.BLEND2 ? + lightImg || grCol.lightBgMain ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseBg), 5) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseBg), 10) : + darkBg && this.TPB === 'emboss' ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseBg), 15) : HEXtoRGB(grCfg.cTheme.grCol_transportEllipseBg); + + grCol.transportEllipseNormal = + this.BLEND || this.BLEND2 ? + this.BEVEL ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseNormal), 5) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseNormal), 10) : + HEXtoRGB(grCfg.cTheme.grCol_transportEllipseNormal); + + grCol.transportEllipseHovered = + this.BLEND || this.BLEND2 ? + this.BEVEL ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseHovered), 5) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportEllipseHovered), 10) : + HEXtoRGB(grCfg.cTheme.grCol_transportEllipseHovered); + + grCol.transportEllipseDown = HEXtoRGB(grCfg.cTheme.grCol_transportEllipseDown); + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? + ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleBg), 0) : + this.TPB === 'emboss' ? + this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleBottom), 5) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleBg), 0) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? + TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleTop), 0) : + this.TPB === 'emboss' ? + lightImg || grCol.lightBgMain ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleTop), 0) : + TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleTop), 15) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? + ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleBottom), 0) : + this.TPB === 'emboss' ? + darkBg ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleBottom), 20) : + TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportStyleBottom), 10) : ''; + + grCol.transportIconNormal = + this.BLEND || this.BLEND2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportIconNormal), 10) : HEXtoRGB(grCfg.cTheme.grCol_transportIconNormal); + + grCol.transportIconHovered = + this.BLEND || this.BLEND2 ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_transportIconHovered), 10) : HEXtoRGB(grCfg.cTheme.grCol_transportIconHovered); + + grCol.transportIconDown = HEXtoRGB(grCfg.cTheme.grCol_transportIconDown); + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + this.PB === 'bevel' ? + this.BLEND || this.BLEND2 ? lightImg || grCol.lightBgMain ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 5) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 10) : + this.BEVEL ? HEXtoRGB(grCfg.cTheme.grCol_progressBar) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 10) : + this.PB === 'inner' ? + this.BLEND || this.BLEND2 ? lightImg || grCol.lightBgMain ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 10) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 5) : + this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 5) : HEXtoRGB(grCfg.cTheme.grCol_progressBar) : + this.BLEND || this.BLEND2 ? grCol.lightBgMain ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 10) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 5) : + this.BEVEL ? grCol.lightBgMain ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 5) : TintColor(HEXtoRGB(grCfg.cTheme.grCol_progressBar), 5) : + HEXtoRGB(grCfg.cTheme.grCol_progressBar); + + grCol.progressBarStreaming = HEXtoRGB(grCfg.cTheme.grCol_progressBarStreaming); + grCol.progressBarFrame = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_progressBarFrame), 5) : HEXtoRGB(grCfg.cTheme.grCol_progressBarFrame); + grCol.progressBarFill = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_progressBarFill), 5) : HEXtoRGB(grCfg.cTheme.grCol_progressBarFill); + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarProg), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarProg); + grCol.peakmeterBarProgFill = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarProgFill), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarProgFill); + grCol.peakmeterBarFillTop = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarFillTop), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarFillTop); + grCol.peakmeterBarFillMiddle = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarFillMiddle), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarFillMiddle); + grCol.peakmeterBarFillBack = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarFillBack), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarFillBack); + grCol.peakmeterBarVertProgFill = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarVertProgFill), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarVertProgFill); + grCol.peakmeterBarVertFill = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarVertFill), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarVertFill); + grCol.peakmeterBarVertFillPeaks = this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarVertFillPeaks), 5) : HEXtoRGB(grCfg.cTheme.grCol_peakmeterBarVertFillPeaks); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = lightBg && (this.BEVEL || this.BLEND) ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillFront), 5) : HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillFront); + grCol.waveformBarFillBack = lightBg && (this.BEVEL || this.BLEND) ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillBack), 5) : HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillBack); + grCol.waveformBarFillPreFront = lightBg && (this.BEVEL || this.BLEND) ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillPreFront), 10) : HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillPreFront); + grCol.waveformBarFillPreBack = lightBg && (this.BEVEL || this.BLEND) ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillPreBack), 5) : HEXtoRGB(grCfg.cTheme.grCol_waveformBarFillPreBack); + grCol.waveformBarIndicator = HEXtoRGB(grCfg.cTheme.grCol_waveformBarIndicator); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = HEXtoRGB(grCfg.cTheme.grCol_volumeBar); + + grCol.volumeBarFrame = + this.VB === 'bevel' || this.VB === 'inner' ? TintColor(HEXtoRGB(grCfg.cTheme.grCol_volumeBarFrame), 5) : + HEXtoRGB(grCfg.cTheme.grCol_volumeBarFrame); + + grCol.volumeBarFill = HEXtoRGB(grCfg.cTheme.grCol_volumeBarFill); + + // * STYLE COLORS * // + grCol.styleBevel = HEXtoRGB(grCfg.cTheme.grCol_styleBevel); + grCol.styleGradient = HEXtoRGB(grCfg.cTheme.grCol_styleGradient); + grCol.styleGradient2 = HEXtoRGB(grCfg.cTheme.grCol_styleGradient2); + + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBar), lightBg ? 5 : 10) : HEXtoRGB(grCfg.cTheme.grCol_styleProgressBar) : + this.PB === 'inner' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBar), lightBg ? 5 : 10) : HEXtoRGB(grCfg.cTheme.grCol_styleProgressBar) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineTop), darkBg ? 40 : lightBg ? 0 : 10) : HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineTop) : + this.PB === 'inner' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineTop), darkBg ? 20 : lightBg ? 5 : 10) : HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineTop) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineBottom), darkBg ? 30 : lightBg ? 30 : 20) : HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineBottom) : + this.PB === 'inner' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineBottom), darkBg ? 25 : lightBg ? 15 : 20) : HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarLineBottom) : ''; + + grCol.styleProgressBarFill = + this.PBF === 'bevel' || this.PBF === 'inner' ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarFill), 15) : + HEXtoRGB(grCfg.cTheme.grCol_styleProgressBarFill); + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleVolumeBar), 10) : HEXtoRGB(grCfg.cTheme.grCol_styleVolumeBar) : + this.VB === 'inner' ? this.BEVEL ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleVolumeBar), 10) : HEXtoRGB(grCfg.cTheme.grCol_styleVolumeBar) : ''; + + grCol.styleVolumeBarFill = + this.VBF === 'bevel' || this.VBF === 'inner' ? ShadeColor(HEXtoRGB(grCfg.cTheme.grCol_styleVolumeBarFill), 15) : + HEXtoRGB(grCfg.cTheme.grCol_styleVolumeBarFill); + } + catch (e) { + fb.ShowPopupMessage(`Error when initializing main custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${grCfg.configPathCustom}\n`, 'Main custom theme color error'); + } + } + // #endregion + + // * PUBLIC METHODS - WHITE PANEL AND MAIN COLORS * // + // #region PUBLIC METHODS - WHITE PANEL AND MAIN COLORS + /** + * Mainly used for style Black and white 2 or theme color adjustments for style Reborn fusion 1 and 2 when panel bg is too light. + * @param {boolean} lighterBg - If true, lightens the panel background color. + * @param {object} [accentColor] - If provided, the RGB color object to be used as the accent color. + */ + panelWhiteColors(lighterBg, accentColor) { + // * PLAYLIST COLORS * // + pl.col.bg = lighterBg ? RGB(255, 255, 255) : RGB(245, 245, 245); + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(180, 180, 180); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(100, 100, 100) : RGB(240, 240, 240); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(100, 100, 100) : RGB(180, 180, 180); + pl.col.header_nowplaying_bg = this.BLEND ? lighterBg ? RGBA(245, 245, 245, 130) : RGBA(255, 255, 255, 130) : lighterBg ? RGB(245, 245, 245) : RGB(255, 255, 255); + pl.col.header_sideMarker = grm.ui.isStreaming ? RGB(207, 0, 5) : accentColor || RGB(40, 40, 40); + pl.col.header_artist_normal = RGB(80, 80, 80); + pl.col.header_artist_playing = RGB(60, 60, 60); + pl.col.header_album_normal = RGB(80, 80, 80); + pl.col.header_album_playing = RGB(60, 60, 60); + pl.col.header_info_normal = RGB(60, 60, 60); + pl.col.header_info_playing = RGB(60, 60, 60); + pl.col.header_date_normal = RGB(60, 60, 60); + pl.col.header_date_playing = RGB(60, 60, 60); + pl.col.header_line_normal = this.BLEND ? RGB(190, 190, 190) : RGB(215, 215, 215); + pl.col.header_line_playing = this.BLEND ? RGB(200, 200, 200) : RGB(215, 215, 215); + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(25, 25, 25, 130) : RGB(25, 25, 25); + pl.col.row_selection_bg = this.BLEND ? RGB(190, 190, 190) : RGB(215, 215, 215); + pl.col.row_selection_frame = this.BLEND ? RGB(190, 190, 190) : pl.col.row_selection_bg; + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(80, 80, 80); + pl.col.row_title_playing = RGB(60, 60, 60); + pl.col.row_title_selected = RGB(0, 0, 0); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = this.BLEND ? RGB(190, 190, 190) : RGB(215, 215, 215); + pl.col.row_drag_line = ShadeColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + pl.col.sbar_btn_normal = RGB(100, 100, 100); + pl.col.sbar_btn_hovered = RGB(0, 0, 0); + pl.col.sbar_thumb_normal = RGB(100, 100, 100); + pl.col.sbar_thumb_hovered = RGB(40, 40, 40); + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = grm.ui.isStreaming ? RGB(207, 0, 5) : accentColor || RGB(40, 40, 40); + lib.ui.col.selectionFrame = this.BLEND ? RGB(190, 190, 190) : RGB(215, 215, 215); + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + lib.ui.col.iconPlus = RGB(80, 80, 80); + lib.ui.col.iconPlus_h = RGB(0, 0, 0); + lib.ui.col.iconPlus_sel = RGB(0, 0, 0); + lib.ui.col.iconPlusBg = grSet.libraryDesign === 'traditional' ? RGB(255, 255, 255) : RGB(45, 45, 45); + lib.ui.col.iconMinus_e = RGB(80, 80, 80); + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(0, 0, 0); + lib.ui.col.text = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(220, 220, 220) : RGB(80, 80, 80); + lib.ui.col.text_h = libSet.albumArtShow && libImg.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.text_nowp = RGB(0, 0, 0); + lib.ui.col.textSel = RGB(0, 0, 0); + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(80, 80, 80); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(80, 80, 80); + lib.ui.col.searchBtn = RGB(0, 0, 0); + lib.ui.col.crossBtn = RGB(0, 0, 0); + lib.ui.col.filterBtn = RGB(80, 80, 80); + lib.ui.col.settingsBtn = RGB(80, 80, 80); + lib.ui.col.line = this.BLEND ? RGB(190, 190, 190) : RGB(215, 215, 215); + lib.ui.col.s_line = lib.ui.col.line; + lib.ui.col.sbarBtns = RGB(60, 60, 60); + lib.ui.col.sbarNormal = RGB(0, 0, 0); + lib.ui.col.sbarHovered = RGB(40, 40, 40); + lib.ui.col.sbarDrag = RGB(40, 40, 40); + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.headingText = RGB(60, 60, 60); + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + bio.ui.col.sbarBtns = RGB(60, 60, 60); + bio.ui.col.sbarNormal = RGB(0, 0, 0); + bio.ui.col.sbarHovered = RGB(40, 40, 40); + bio.ui.col.sbarDrag = RGB(40, 40, 40); + bio.ui.col.noPhotoStubBg = RGB(25, 25, 25); + bio.ui.col.noPhotoStubText = this.THEME === 'cream' ? RGB(120, 170, 130) : pl.col.header_artist_playing; + + // * DETAILS COLORS * // + grCol.detailsBg = pl.col.bg; + grCol.detailsText = RGB(60, 60, 60); + grCol.timelineAdded = grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(40, 40, 40); + grCol.timelinePlayed = grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(80, 80, 80); + grCol.timelineUnplayed = grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(120, 120, 120); + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = pl.col.row_nowplaying_bg; + grCol.popupText = pl.col.row_title_playing; + } + + /** + * Mainly used for style Black and white 1 or theme color adjustments for style Reborn fusion 1 and 2 when main bg is too light. + * @param {boolean} lighterBg - If true, lightens the main background color. + */ + mainWhiteColors(lighterBg) { + // * MAIN COLORS * // + grCol.bg = lighterBg || this.BEVEL ? RGB(255, 255, 255) : RGB(230, 230, 230); + grCol.loadingThemeBg = grCol.bg; + grCol.uiHacksFrame = grCol.bg; + grCol.noAlbumArtStub = RGB(255, 255, 255); + grCol.lowerBarArtist = RGB(80, 80, 80); + grCol.lowerBarTitle = RGB(80, 80, 80); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = RGB(255, 255, 255); + + grCol.menuStyleBg = + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(210, 210, 210) : + this.TMB === 'emboss' ? RGB(235, 235, 235) : + this.BEVEL ? RGB(205, 205, 205) : RGB(220, 220, 220); + + grCol.menuRectStyleEmbossTop = RGB(255, 255, 255); + grCol.menuRectStyleEmbossBottom = this.BEVEL ? RGB(200, 200, 200) : RGB(195, 195, 195); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGB(200, 200, 200) : + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(140, 140, 140) : RGB(150, 150, 150) : + this.BEVEL ? RGB(170, 170, 170) : RGB(180, 180, 180); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGB(200, 200, 200) : + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(200, 200, 200) : RGB(205, 205, 205) : + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(140, 140, 140) : RGB(150, 150, 150) : + this.BEVEL ? RGB(170, 170, 170) : RGB(180, 180, 180); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = RGB(80, 80, 80); + grCol.menuTextHovered = RGB(40, 40, 40); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = (this.BLEND || this.BLEND2) && fb.IsPlaying ? RGB(230, 230, 230) : RGB(255, 255, 255); + grCol.transportEllipseNormal = this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(200, 200, 200) : RGB(210, 210, 210) : RGB(220, 220, 220); + grCol.transportEllipseHovered = this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(160, 160, 160) : RGB(170, 170, 170) : RGB(180, 180, 180); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' ? this.BEVEL ? RGB(200, 200, 200) : RGB(205, 205, 205) : + this.TPB === 'inner' ? this.BEVEL ? RGB(215, 215, 215) : RGB(205, 205, 205) : + this.TPB === 'emboss' ? this.BEVEL ? RGB(230, 230, 230) : RGB(215, 215, 215) : + RGB(225, 225, 225); + + grCol.transportStyleTop = RGB(255, 255, 255); + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? + this.BLEND || this.BLEND2 ? RGB(180, 180, 180) : this.BEVEL ? RGB(200, 200, 200) : RGB(220, 220, 220) : + this.TPB === 'emboss' ? + this.BLEND || this.BLEND2 ? RGB(180, 180, 180) : this.BEVEL ? RGB(215, 215, 215) : RGB(210, 210, 210) : + RGB(230, 230, 230); + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(205, 205, 205) : RGB(215, 215, 215) : + this.BEVEL ? RGB(195, 195, 195) : RGB(210, 210, 210); + + grCol.progressBarFill = RGB(255, 255, 255); + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = RGB(20, 20, 20); + grCol.peakmeterBarFillTop = RGB(140, 140, 140); + grCol.peakmeterBarFillMiddle = RGB(20, 20, 20); + grCol.peakmeterBarFillBack = RGB(80, 80, 80); + grCol.peakmeterBarVertProgFill = RGB(20, 20, 20); + grCol.peakmeterBarVertFill = RGB(20, 20, 20); + grCol.peakmeterBarVertFillPeaks = RGB(120, 120, 120); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = RGB(120, 120, 120); + grCol.waveformBarFillBack = RGB(20, 20, 20); + grCol.waveformBarFillPreFront = RGB(160, 160, 160); + grCol.waveformBarFillPreBack = RGB(120, 120, 120); + grCol.waveformBarIndicator = RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(255, 255, 255); + grCol.volumeBarFill = RGB(120, 120, 120); + grCol.volumeBarFrame = RGB(210, 210, 210); + + // * STYLE COLORS * // + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : + this.PB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 35) : RGBA(0, 0, 0, 40) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.PBD === 'rounded' ? RGBA(0, 0, 0, 40) : + this.BEVEL ? RGBA(255, 255, 255, 20) : RGBA(0, 0, 0, 0) : + this.PB === 'inner' ? this.PBD === 'rounded' ? RGBA(0, 0, 0, 50) : + RGBA(0, 0, 0, 20) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 180) : RGBA(255, 255, 255, 255) : + this.BEVEL ? RGBA(255, 255, 255, 160) : RGBA(255, 255, 255, 220) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 150) : RGBA(255, 255, 255, 255) : + this.BEVEL ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 170) : ''; + + grCol.styleProgressBarFill = + this.PBF === 'bevel' ? RGBA(0, 0, 0, this.BEVEL ? 40 : 30) : + this.PBF === 'inner' ? RGBA(0, 0, 0, this.BEVEL ? 50 : 40) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 30) : + this.VB === 'inner' ? RGBA(0, 0, 0, 30) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(255, 255, 255, 90) : ''; + } + // #endregion + + // * PUBLIC METHODS - BLACK PANEL AND MAIN COLORS * // + // #region PUBLIC METHODS - BLACK PANEL AND MAIN COLORS + /** + * Mainly used for style Black and white 1 or theme color adjustments for style Reborn fusion 1 and 2 when panel bg is too dark. + * @param {boolean} darkerBg - If true, darkens the panel background color. + * @param {object} [accentColor] - If provided, the RGB color object to be used as the accent color. + */ + panelBlackColors(darkerBg, accentColor) { + // * PLAYLIST COLORS * // + pl.col.bg = darkerBg ? RGB(0, 0, 0) : RGB(20, 20, 20); + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = grSet.autoHidePlman ? pl.col.bg : RGB(180, 180, 180); + pl.col.plman_text_hovered = grSet.autoHidePlman ? RGB(200, 200, 200) : RGB(240, 240, 240); + pl.col.plman_text_pressed = grSet.autoHidePlman ? RGB(240, 240, 240) : RGB(180, 180, 180); + pl.col.header_nowplaying_bg = this.BLEND ? RGBA(230, 230, 230, 200) : RGB(230, 230, 230); + pl.col.header_sideMarker = grm.ui.isStreaming ? RGB(207, 0, 5) : accentColor || RGB(255, 255, 255); + pl.col.header_artist_normal = RGB(220, 220, 220); + pl.col.header_artist_playing = RGB(25, 25, 25); + pl.col.header_album_normal = RGB(200, 200, 200); + pl.col.header_album_playing = RGB(25, 25, 25); + pl.col.header_info_normal = RGB(200, 200, 200); + pl.col.header_info_playing = RGB(25, 25, 25); + pl.col.header_date_normal = RGB(220, 220, 220); + pl.col.header_date_playing = RGB(25, 25, 25); + pl.col.header_line_normal = this.BLEND ? RGB(80, 80, 80) : RGB(45, 45, 45); + pl.col.header_line_playing = RGB(25, 25, 25); + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_stripes_bg = this.BLEND ? RGBA(25, 25, 25, 130) : RGB(25, 25, 25); + pl.col.row_selection_bg = this.BLEND ? RGB(80, 80, 80) : RGB(45, 45, 45); + pl.col.row_selection_frame = pl.col.row_selection_bg; + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_title_normal = RGB(200, 200, 200); + pl.col.row_title_playing = RGB(25, 25, 25); + pl.col.row_title_selected = RGB(255, 255, 255); + pl.col.row_title_hovered = pl.col.row_title_selected; + pl.col.row_rating_color = RGB(255, 190, 0); + pl.col.row_disc_subheader_line = this.BLEND ? RGB(80, 80, 80) : RGB(45, 45, 45); + pl.col.row_drag_line = TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + pl.col.sbar_btn_normal = RGB(200, 200, 200); + pl.col.sbar_btn_hovered = RGB(255, 255, 255); + pl.col.sbar_thumb_normal = RGB(180, 180, 180); + pl.col.sbar_thumb_hovered = RGB(255, 255, 255); + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = grm.ui.isStreaming ? RGB(207, 0, 5) : accentColor || RGB(255, 255, 255); + lib.ui.col.selectionFrame = this.BLEND ? RGB(80, 80, 80) : RGB(45, 45, 45); + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + lib.ui.col.iconPlus = RGB(220, 220, 220); + lib.ui.col.iconPlus_h = RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = RGB(255, 255, 255); + lib.ui.col.iconPlusBg = RGB(45, 45, 45); + lib.ui.col.iconMinus_e = RGB(220, 220, 220); + lib.ui.col.iconMinus_c = lib.ui.col.iconMinus_e; + lib.ui.col.iconMinus_h = RGB(255, 255, 255); + lib.ui.col.text = RGB(200, 200, 200); + lib.ui.col.text_h = !libSet.albumArtShow || libSet.albumArtShow && libSet.highLightRow !== 2 ? RGB(255, 255, 255) : RGB(0, 0, 0); + lib.ui.col.text_nowp = RGB(0, 0, 0); + lib.ui.col.textSel = + libSet.albumArtShow && !['coversLabelsRight', 'coversLabelsBottom', 'coversLabelsBlend'].includes(grSet.libraryDesign) || + grSet.libraryDesign === 'traditional' ? RGB(0, 0, 0) : RGB(255, 255, 255); + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = RGB(200, 200, 200); + lib.ui.col.count = lib.ui.col.text; + lib.ui.col.search = RGB(200, 200, 200); + lib.ui.col.searchBtn = RGB(255, 255, 255); + lib.ui.col.crossBtn = RGB(255, 255, 255); + lib.ui.col.filterBtn = RGB(220, 220, 220); + lib.ui.col.settingsBtn = RGB(220, 220, 220); + lib.ui.col.line = this.BLEND ? RGB(80, 80, 80) : RGB(45, 45, 45); + lib.ui.col.s_line = lib.ui.col.line; + lib.ui.col.sbarBtns = RGB(200, 200, 200); + lib.ui.col.sbarNormal = RGB(255, 255, 255); + lib.ui.col.sbarHovered = RGB(255, 255, 255); + lib.ui.col.sbarDrag = RGB(255, 255, 255); + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.headingText = RGB(230, 230, 230); + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.text = pl.col.row_title_normal; + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + bio.ui.col.sbarBtns = RGB(200, 200, 200); + bio.ui.col.sbarNormal = RGB(255, 255, 255); + bio.ui.col.sbarHovered = RGB(255, 255, 255); + bio.ui.col.sbarDrag = RGB(255, 255, 255); + bio.ui.col.noPhotoStubBg = RGB(25, 25, 25); + bio.ui.col.noPhotoStubText = this.THEME === 'cream' ? RGB(120, 170, 130) : pl.col.header_artist_playing; + + // * DETAILS COLORS * // + grCol.detailsBg = pl.col.bg; + grCol.detailsText = RGB(220, 220, 220); + grCol.timelineAdded = grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(230, 230, 230); + grCol.timelinePlayed = grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(180, 180, 180); + grCol.timelineUnplayed = grm.ui.isStreaming ? RGB(207, 0, 5) : RGB(160, 160, 160); + grCol.timelineFrame = grCol.detailsBg; + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * POPUP COLORS * // + grCol.popupBg = pl.col.row_nowplaying_bg; + grCol.popupText = pl.col.row_title_playing; + } + + /** + * Mainly used for style Black and white 2 or theme color adjustments for style Reborn fusion 1 and 2 when main bg is too dark. + * @param {boolean} darkerBg - If true, darkens the main background color. + */ + mainBlackColors(darkerBg) { + const nighttime = (grSet.styleNighttime || grSet.themeDayNightMode && grSet.themeDayNightTime === 'night') && !this.RW; + + // * MAIN COLORS * // + grCol.bg = this.BEVEL ? darkerBg ? RGB(25, 25, 25) : RGB(50, 50, 50) : darkerBg ? RGB(0, 0, 0) : RGB(25, 25, 25); + grCol.loadingThemeBg = nighttime ? RGB(25, 25, 25) : grCol.bg; + grCol.uiHacksFrame = grCol.bg; + grCol.shadow = grm.ui.isPlayingCD ? RGBA(0, 0, 0, 30) : grCol.shadow; + grCol.noAlbumArtStub = RGB(40, 40, 40); + grCol.lowerBarArtist = RGB(240, 240, 240); + grCol.lowerBarTitle = RGB(220, 220, 220); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTONS COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? this.BEVEL ? RGB(40, 40, 40) : RGB(50, 50, 50) : + this.TMB === 'inner' ? this.BEVEL ? RGB(55, 55, 55) : RGB(50, 50, 50) : + this.TMB === 'emboss' ? RGB(45, 45, 45) : + RGB(35, 35, 35); + + grCol.menuStyleBg = + this.TMB === 'inner' ? RGB(20, 20, 20) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(45, 45, 45) : RGB(50, 50, 50) : + this.BEVEL ? RGB(30, 30, 30) : RGB(20, 20, 20); + + grCol.menuRectStyleEmbossTop = this.BEVEL ? RGB(60, 60, 60) : RGB(70, 70, 70); + grCol.menuRectStyleEmbossBottom = RGB(0, 0, 0); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGBA(60, 60, 60, 100) : + this.TMB === 'bevel' ? RGB(0, 0, 0) : + RGB(60, 60, 60); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(120, 120, 120, 100) : + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(0, 0, 0) : + RGB(100, 100, 100); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? RGB(200, 200, 200) : RGB(180, 180, 180); + grCol.menuTextHovered = RGB(255, 255, 255); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = this.BLEND || this.BLEND2 ? RGB(60, 60, 60) : RGB(40, 40, 40); + grCol.transportEllipseNormal = RGB(50, 50, 50); + grCol.transportEllipseHovered = RGB(100, 100, 100); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(20, 20, 20) : + this.TPB === 'emboss' ? RGB(50, 50, 50) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(50, 50, 50) : + this.TPB === 'emboss' ? this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(10, 10, 10) : + this.TPB === 'emboss' ? RGB(20, 20, 20) : ''; + + grCol.transportIconNormal = RGB(200, 200, 200); + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = RGB(50, 50, 50); + grCol.progressBarFill = RGB(210, 210, 210); + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = RGB(200, 200, 200); + grCol.peakmeterBarFillTop = RGB(120, 120, 120); + grCol.peakmeterBarFillMiddle = RGB(160, 160, 160); + grCol.peakmeterBarFillBack = RGB(80, 80, 80); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = RGB(245, 245, 245); + grCol.peakmeterBarVertFillPeaks = RGB(200, 200, 200); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = RGB(245, 245, 245); + grCol.waveformBarFillBack = RGB(200, 200, 200); + grCol.waveformBarFillPreFront = RGB(160, 160, 160); + grCol.waveformBarFillPreBack = RGB(120, 120, 120); + grCol.waveformBarIndicator = RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = grCol.progressBar; + grCol.volumeBarFill = grCol.progressBarFill; + grCol.volumeBarFrame = RGB(50, 50, 50); + + // * STYLE COLORS * // + grCol.styleProgressBar = + this.PB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : + this.PB === 'inner' ? RGBA(0, 0, 0, 100) : ''; + + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 255) : + RGBA(0, 0, 0, 255) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 150) : + this.BEVEL ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 100) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 40) : RGBA(255, 255, 255, 30) : + this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 25) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 35) : RGBA(255, 255, 255, 30) : + this.BEVEL ? RGBA(255, 255, 255, 45) : RGBA(255, 255, 255, 40) : ''; + + grCol.styleProgressBarFill = this.PBF === 'bevel' || this.PBF === 'inner' ? RGBA(0, 0, 0, 70) : ''; + + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 80) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 80) : ''; + } + // #endregion + + // * PUBLIC METHODS - LIBRARY & BIOGRAPHY THEME COLORS * // + // #region PUBLIC METHODS - LIBRARY & BIOGRAPHY THEME COLORS + /** + * The Library theme colors used in Options > Library > Theme. + */ + libraryThemeColors() { + // * SETUP COLORS * // + const colBrightness = new Color(lib.ui.col.bg).brightness; + grCol.lightBgLib = + libSet.theme === 1 && grCol.imgBrightness > 200 + || + libSet.theme === 2 && (colBrightness > 150 || colBrightness > 75 && grCol.imgBrightness > 200) + || + libSet.theme === 3 + || + libSet.theme === 5 && (colBrightness > 150); + + // * GET BLENDED BG IMAGE * // + lib.ui.get = true; + + // * ROW COLORS * // + lib.ui.col.selectionFrame = grCol.lightBgLib ? RGBA(0, 0, 0, 100) : RGBA(255, 255, 255, 100); + + // * NODE COLORS * // + lib.ui.col.iconPlus = grCol.lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); + lib.ui.col.iconPlus_h = grCol.lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); + lib.ui.col.iconPlus_sel = grCol.lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * TEXT COLORS * // + lib.ui.col.text = grCol.lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); + lib.ui.col.text_h = grCol.lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); + lib.ui.col.textSel = libSet.albumArtShow ? lib.ui.col.text_nowp : lib.ui.col.text; + lib.ui.col.txt = lib.ui.col.text; + lib.ui.col.txt_h = lib.ui.col.text_h; + lib.ui.col.txt_box = grCol.lightBgLib ? RGB(40, 40, 40) : RGB(220, 220, 220); + lib.ui.col.count = lib.ui.col.text; + + // * BUTTON COLORS * // + lib.ui.col.search = grCol.lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); + lib.ui.col.searchBtn = grCol.lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); + lib.ui.col.crossBtn = grCol.lightBgLib ? RGB(40, 40, 40) : RGB(255, 255, 255); + lib.ui.col.filterBtn = grCol.lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); + lib.ui.col.settingsBtn = grCol.lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); + lib.ui.col.line = grCol.lightBgLib ? RGBA(0, 0, 0, 100) : RGBA(255, 255, 255, 100); + lib.ui.col.s_line = lib.ui.col.line; + } + + /** + * The Biography theme colors used in Options > Biography > Theme. + */ + biographyThemeColors() { + // * SETUP COLORS * // + const colBrightness = new Color(bio.ui.col.bg).brightness; + grCol.lightBgBio = + bioSet.theme === 1 && grCol.imgBrightness > 200 + || + bioSet.theme === 2 && (colBrightness > 150 || grCol.imgBrightness > 200) + || + bioSet.theme === 3 + || + bioSet.theme === 4 && grCol.imgBrightness > 150; + + // * MAIN COLORS * // + bio.ui.col.rowStripes = RGBtoRGBA(pl.col.row_stripes_bg, 100); + + // * HEADER COLORS * // + bio.ui.col.headingText = grCol.lightBgBio ? RGB(65, 65, 65) : RGB(230, 230, 230); + bio.ui.col.bottomLine = grCol.lightBgBio ? RGB(120, 120, 120) : pl.col.header_line_normal; + bio.ui.col.centerLine = bio.ui.col.bottomLine; + bio.ui.col.sectionLine = bio.ui.col.bottomLine; + + // * TEXT COLORS * // + bio.ui.col.text = grCol.lightBgBio ? RGB(60, 60, 60) : RGB(220, 220, 220); + bio.ui.col.source = bio.ui.col.headingText; + bio.ui.col.accent = bio.ui.col.headingText; + bio.ui.col.summary = bio.ui.col.text; + } + // #endregion + + // * PUBLIC METHODS - THEME COLOR INITIALIZATION * // + // #region PUBLIC METHODS - THEME COLOR INITIALIZATION + /** + * Initializes current values for all theme pref.properties. + */ + initThemePrefVals() { + /** @private Options > Theme. */ + this.THEME = grSet.theme; + /** @private Options > Theme > Custom. */ + this.CTHEME = grSet.theme.startsWith('custom'); + /** @private Options > Style > Bevel. */ + this.BEVEL = grSet.styleBevel; + /** @private Options > Style > Blend. */ + this.BLEND = grSet.styleBlend; + /** @private Options > Style > Blend 2. */ + this.BLEND2 = grSet.styleBlend2; + /** @private Options > Style > Gradient. */ + this.GRAD = grSet.styleGradient; + /** @private Options > Style > Gradient 2. */ + this.GRAD2 = grSet.styleGradient2; + /** @private Options > Style > Alternative. */ + this.ALT = grSet.styleAlternative; + /** @private Options > Style > Alternative 2. */ + this.ALT2 = grSet.styleAlternative2; + /** @private Options > Style > Black and white ( White theme ). */ + this.BW = grSet.styleBlackAndWhite; + /** @private Options > Style > Black and white 2 ( White theme ). */ + this.BW2 = grSet.styleBlackAndWhite2; + /** @private Options > Style > Black and white reborn ( White theme ). */ + this.BWR = grSet.styleBlackAndWhiteReborn; + /** @private Options > Style > Black reborn ( Black theme ). */ + this.BR = grSet.styleBlackReborn; + /** @private Options > Style > Reborn white ( Reborn theme ). */ + this.RW = grSet.styleRebornWhite; + /** @private Options > Style > Reborn black ( Reborn theme ). */ + this.RB = grSet.styleRebornBlack; + /** @private Options > Style > Reborn fusion ( Reborn theme ). */ + this.RF = grSet.styleRebornFusion; + /** @private Options > Style > Reborn fusion 2 ( Reborn theme ). */ + this.RF2 = grSet.styleRebornFusion2; + /** @private Options > Style > Reborn fusion accent ( Reborn theme ). */ + this.RFA = grSet.styleRebornFusionAccent; + /** @private Options > Style > Random pastel ( Random theme ). */ + this.RP = grSet.styleRandomPastel; + /** @private Options > Style > Random dark ( Random theme ). */ + this.RD = grSet.styleRandomDark; + /** @private Options > Style > Auto color ( Random theme ). */ + this.RAC = grSet.styleRandomAutoColor; + /** @private Options > Style > Buttons > Top menu. */ + this.TMB = grSet.styleTopMenuButtons; + /** @private Options > Style > Buttons > Transport. */ + this.TPB = grSet.styleTransportButtons; + /** @private Options > Style > Progress bar > Design. */ + this.PBD = grSet.styleProgressBarDesign; + /** @private Options > Style > Progress bar > Background. */ + this.PB = grSet.styleProgressBar; + /** @private Options > Style > Progress bar > Progress fill. */ + this.PBF = grSet.styleProgressBarFill; + /** @private Options > Style > Volume bar > Design. */ + this.VBD = grSet.styleVolumeBarDesign; + /** @private Options > Style > Volume bar > Background. */ + this.VB = grSet.styleVolumeBar; + /** @private Options > Style > Volume bar > Volume fill. */ + this.VBF = grSet.styleVolumeBarFill; + /** @private Options > Layout. */ + this.LAYOUT = grSet.layout; + /** @private Options > Brightness. */ + this.BRT = grSet.themeBrightness; + } + + /** + * Init all colors that are used in the Playlist, mostly called from grMain.ui.initTheme(). + */ + initPlaylistColors() { + const playlistColors = { + white: this.playlistColorsWhiteTheme.bind(this), + black: this.playlistColorsBlackTheme.bind(this), + reborn: this.playlistColorsRebornRandomTheme.bind(this), + random: this.playlistColorsRebornRandomTheme.bind(this), + blue: this.playlistColorsBlueTheme.bind(this), + darkblue: this.playlistColorsDarkblueTheme.bind(this), + red: this.playlistColorsRedTheme.bind(this), + cream: this.playlistColorsCreamTheme.bind(this), + nblue: this.playlistColorsNeonThemes.bind(this), + ngreen: this.playlistColorsNeonThemes.bind(this), + nred: this.playlistColorsNeonThemes.bind(this), + ngold: this.playlistColorsNeonThemes.bind(this) + }; + + if (playlistColors[this.THEME]) { + playlistColors[this.THEME](); + } else if (this.CTHEME) { + this.playlistColorsCustomTheme(); + } + } + + /** + * Init all colors that are used in the Library, mostly called from grMain.ui.initTheme(). + */ + initLibraryColors() { + const libraryColors = { + white: this.libraryColorsWhiteTheme.bind(this), + black: this.libraryColorsBlackTheme.bind(this), + reborn: this.libraryColorsRebornRandomTheme.bind(this), + random: this.libraryColorsRebornRandomTheme.bind(this), + blue: this.libraryColorsBlueTheme.bind(this), + darkblue: this.libraryColorsDarkblueTheme.bind(this), + red: this.libraryColorsRedTheme.bind(this), + cream: this.libraryColorsCreamTheme.bind(this), + nblue: this.libraryColorsNeonThemes.bind(this), + ngreen: this.libraryColorsNeonThemes.bind(this), + nred: this.libraryColorsNeonThemes.bind(this), + ngold: this.libraryColorsNeonThemes.bind(this) + }; + + if (libraryColors[this.THEME]) { + libraryColors[this.THEME](); + } else if (this.CTHEME) { + this.libraryColorsCustomTheme(); + } + + if (libSet.theme !== 0) this.libraryThemeColors(); + } + + /** + * Init all colors that are used in the Biography, mostly called from grMain.ui.initTheme(). + */ + initBiographyColors() { + const biographyColors = { + white: this.biographyColorsWhiteTheme.bind(this), + black: this.biographyColorsBlackTheme.bind(this), + reborn: this.biographyColorsRebornRandomTheme.bind(this), + random: this.biographyColorsRebornRandomTheme.bind(this), + blue: this.biographyColorsBlueTheme.bind(this), + darkblue: this.biographyColorsDarkblueTheme.bind(this), + red: this.biographyColorsRedTheme.bind(this), + cream: this.biographyColorsCreamTheme.bind(this), + nblue: this.biographyColorsNeonThemes.bind(this), + ngreen: this.biographyColorsNeonThemes.bind(this), + nred: this.biographyColorsNeonThemes.bind(this), + ngold: this.biographyColorsNeonThemes.bind(this) + }; + + if (biographyColors[this.THEME]) { + biographyColors[this.THEME](); + } else if (this.CTHEME) { + this.biographyColorsCustomTheme(); + } + + if (bioSet.theme !== 0) this.biographyThemeColors(); + } + + /** + * Init all colors that are used in Georgia-ReBORN main, mostly called from grMain.ui.initTheme(). + */ + initMainColors() { + const mainColors = { + white: this.mainColorsWhiteTheme.bind(this), + black: this.mainColorsBlackTheme.bind(this), + reborn: this.mainColorsRebornRandomTheme.bind(this), + random: this.mainColorsRebornRandomTheme.bind(this), + blue: this.mainColorsBlueTheme.bind(this), + darkblue: this.mainColorsDarkblueTheme.bind(this), + red: this.mainColorsRedTheme.bind(this), + cream: this.mainColorsCreamTheme.bind(this), + nblue: this.mainColorsNeonThemes.bind(this), + ngreen: this.mainColorsNeonThemes.bind(this), + nred: this.mainColorsNeonThemes.bind(this), + ngold: this.mainColorsNeonThemes.bind(this) + }; + + if (mainColors[this.THEME]) { + mainColors[this.THEME](); + } else if (this.CTHEME) { + this.mainColorsCustomTheme(); + } + } + + /** + * Init all colors that are used in the chronflow user-component, mostly called from grMain.ui.initTheme(). + */ + initChronflowColors() { + if (!Component.ChronFlow) return; + try { + const chron = new ActiveXObject('chron.IChronControl'); + if (chron) { + let r_bg = 255; + let g_bg = 255; + let b_bg = 255; + + if (pl.col.bg !== RGB(255, 255, 255)) { + const bg_rgb = Math.abs(pl.col.bg); + + r_bg = GetRed(bg_rgb); + g_bg = GetGreen(bg_rgb); + b_bg = GetBlue(bg_rgb); + + r_bg = 255 - r_bg; + g_bg = 255 - g_bg; + b_bg = 255 - b_bg; + } + else { + r_bg = 255; + g_bg = 255; + b_bg = 255; + } + + // * SetPanelColor + const bg = (b_bg << 16) | (g_bg << 8) | r_bg; + chron.SetPanelColor(bg, /*skip refresh*/ [0]); + + // * SetTextColor + r_bg = GetRed(grCol.lowerBarArtist); + g_bg = GetGreen(grCol.lowerBarArtist); + b_bg = GetBlue(grCol.lowerBarArtist); + + let strhex = '0x'; + const rgbtohex = RGBtoHEX(b_bg, g_bg, r_bg); + strhex = strhex.concat(rgbtohex); + chron.SetTextColor(strhex, /*refresh*/ [1]); + } + } + catch (e) { + // DebugLog('Unable to create ActiveX chron.IChronControl object'); + } + } + // #endregion + + // * PUBLIC METHODS - THEME COLOR ADJUSTMENTS * // + // #region PUBLIC METHODS - THEME COLOR ADJUSTMENTS + /** + * Post init color adjustments, used for White, Black, Reborn and Random theme. + */ + themeColorAdjustments() { + const cBRT = grCol.colBrightness; + const cBRT2 = grCol.colBrightness2; + const iBRT = grCol.imgBrightness; + const bevel = this.BEVEL; + const blend = this.BLEND; + const blend2 = this.BLEND2; + const alt = this.ALT; + const transpBtns = this.TPB; + const progBar = this.PB; + const progBarFillBevelInner = this.PBF === 'bevel' || this.PBF === 'inner'; + + // * WHITE THEME/REBORN WHITE WITH STYLE BLEND - dynamically adjust transport buttons styles + if (((this.THEME === 'white' && !this.BW2 || this.RW) && (blend || blend2)) && fb.IsPlaying && !grm.ui.isStreaming && !grm.ui.isPlayingCD) { + switch (true) { + case ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) > 500: grCol.transportStyleBottom = RGB(175, 175, 175); break; + case ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) > 300: grCol.transportStyleBottom = RGB(180, 180, 180); break; + case ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) > 150: grCol.transportStyleBottom = RGB(185, 185, 185); break; + case ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) > 75: grCol.transportStyleBottom = RGB(190, 190, 190); break; + case ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) > 50: grCol.transportStyleBottom = RGB(195, 195, 195); break; + case ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) >= 0: grCol.transportStyleBottom = RGB(200, 200, 200); break; + } + } + + // * WHITE THEME/REBORN WHITE WITH STYLE BLEND - dynamically adjust progress bar background color + if ((this.THEME === 'white' || this.RW) && (blend || blend2) && fb.IsPlaying) { + if (ColorDistance(RGB(iBRT, iBRT, iBRT), grCol.bg, true) < 180) { + if (grCfg.settings.showDebugThemeLog) console.log('>>> Blended album art image is too close to col.bg. Adjusting progress bar'); + grCol.progressBar = bevel ? TintColor(grCol.progressBar, 10) : ShadeColor(grCol.progressBar, 10); + } + if (!this.BWR && ColorDistance(grCol.progressBarFill, grCol.progressBar, true) < 150) { + if (grCfg.settings.showDebugThemeLog) console.log('>>> Progress bar fill color is too close to progress bar background. Adjusting progress bar fill'); + grCol.progressBarFill = bevel ? ShadeColor(grCol.progressBarFill, 20) : ShadeColor(grCol.progressBarFill, 10); + } + } + + //////////////////////////// + // * STYLE BLACK REBORN * // + //////////////////////////// + if (this.BR && fb.IsPlaying && !grm.ui.isStreaming && !grm.ui.isPlayingCD && !grm.ui.noAlbumArtStub) { + // * PLAYLIST COLORS * // + pl.col.header_nowplaying_bg = + cBRT > 200 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 175 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 150 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 125 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 100 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 75 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 50 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT > 25 ? blend ? RGBtoRGBA(grCol.primary, bevel ? 180 : 200) : grCol.primary : + cBRT >= 0 ? cBRT < 10 ? TintColor(grCol.primary, blend || blend2 ? 15 : 10) : + TintColor(grCol.primary, blend || blend2 ? 15 : 5) : ''; + + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + + // * MAIN COLORS * // + grCol.transportEllipseBg = + cBRT > 150 ? transpBtns === 'emboss' ? TintColor(grCol.transportEllipseBg, 10) : grCol.transportEllipseBg : + grCol.transportEllipseBg; + + grCol.transportStyleTop = + cBRT > 200 ? transpBtns === 'emboss' ? TintColor(grCol.transportStyleTop, 30) : TintColor(grCol.transportStyleTop, 10) : + cBRT > 175 ? transpBtns === 'emboss' ? TintColor(grCol.transportStyleTop, 30) : TintColor(grCol.transportStyleTop, 10) : + cBRT > 150 ? transpBtns === 'emboss' ? TintColor(grCol.transportStyleTop, 30) : TintColor(grCol.transportStyleTop, 10) : + cBRT > 125 ? transpBtns === 'emboss' ? TintColor(grCol.transportStyleTop, 30) : TintColor(grCol.transportStyleTop, 15) : + cBRT > 100 ? transpBtns === 'emboss' ? TintColor(grCol.transportStyleTop, 40) : ShadeColor(grCol.transportStyleTop, 10) : + cBRT > 75 ? transpBtns === 'emboss' ? TintColor(grCol.transportStyleTop, 40) : ShadeColor(grCol.transportStyleTop, 8) : + cBRT > 50 ? transpBtns === 'emboss' ? ShadeColor(grCol.transportStyleTop, 10) : TintColor(grCol.transportStyleTop, 6) : + cBRT > 25 ? transpBtns === 'emboss' ? ShadeColor(grCol.transportStyleTop, 20) : TintColor(grCol.transportStyleTop, 4) : + cBRT >= 0 ? transpBtns === 'emboss' ? ShadeColor(grCol.transportStyleTop, 20) : TintColor(grCol.transportStyleTop, 4) : ''; + + grCol.transportStyleBottom = + cBRT > 200 ? TintColor(grCol.transportStyleBottom, 6) : + cBRT > 175 ? TintColor(grCol.transportStyleBottom, 6) : + cBRT > 150 ? TintColor(grCol.transportStyleBottom, 6) : + cBRT > 125 ? TintColor(grCol.transportStyleBottom, 6) : + cBRT > 100 ? ShadeColor(grCol.transportStyleBottom, 6) : + cBRT > 75 ? ShadeColor(grCol.transportStyleBottom, 12) : + cBRT > 50 ? TintColor(grCol.transportStyleBottom, 6) : + cBRT > 25 ? TintColor(grCol.transportStyleBottom, 4) : + cBRT >= 0 ? TintColor(grCol.transportStyleBottom, 4) : ''; + + grCol.progressBar = + cBRT < 25 ? bevel ? TintColor(grCol.primary, cBRT < 10 ? blend2 ? 15 : 10 : 5) : ShadeColor(grCol.primary, 30) : + cBRT < 50 ? RGB(0, 0, 0) : pl.col.bg; + + grCol.styleProgressBar = + cBRT > 200 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 45 : 65) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 40 : 60) : '' : + cBRT > 175 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 45 : 65) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 40 : 60) : '' : + cBRT > 150 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 45 : 65) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 40 : 60) : '' : + cBRT > 125 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 35 : 55) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 30 : 50) : '' : + cBRT > 100 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 25 : 40) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 20 : 40) : '' : + cBRT > 75 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 15 : 25) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 15 : 20) : '' : + cBRT > 50 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 10 : 15) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 10 : 15) : '' : + cBRT < 50 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 10 : 10) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 10 : 10) : '' : + grCol.styleProgressBar; + + grCol.progressBarFill = + cBRT > 200 ? bevel ? TintColor(grCol.progressBarFill, 10) : ShadeColor(grCol.progressBarFill, 15) : + cBRT > 175 ? bevel ? TintColor(grCol.progressBarFill, 5) : ShadeColor(grCol.progressBarFill, blend ? 5 : 15) : + cBRT > 150 ? bevel ? TintColor(grCol.progressBarFill, 10) : ShadeColor(grCol.progressBarFill, 20) : + cBRT > 125 ? bevel ? TintColor(grCol.progressBarFill, 10) : ShadeColor(grCol.progressBarFill, 20) : + cBRT > 100 ? bevel ? TintColor(grCol.progressBarFill, 15) : ShadeColor(grCol.progressBarFill, 30) : + cBRT > 75 ? TintColor(grCol.progressBarFill, 30) : + cBRT > 50 ? TintColor(grCol.progressBarFill, 30) : + cBRT > 25 ? TintColor(grCol.primary, 25) : + cBRT >= 0 ? TintColor(grCol.primary, 25) : ''; + + grCol.styleProgressBarFill = + cBRT > 200 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 80 : 60) : '' : + cBRT > 175 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 80 : 60) : '' : + cBRT > 150 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 80 : 60) : '' : + cBRT > 125 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 90 : 70) : '' : + cBRT > 100 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 100 : 85) : '' : + cBRT > 75 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 100 : 90) : '' : + cBRT > 50 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 110 : 100) : '' : + cBRT > 25 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 130 : 120) : '' : + cBRT >= 0 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 130 : 120) : '' : ''; + + grCol.shadow = + cBRT > 200 ? RGBA(0, 0, 0, alt ? 45 : 35) : + cBRT > 175 ? RGBA(0, 0, 0, alt ? 45 : 35) : + cBRT > 150 ? RGBA(0, 0, 0, alt ? 45 : 35) : + cBRT > 125 ? RGBA(0, 0, 0, alt ? 50 : 40) : + cBRT > 100 ? RGBA(0, 0, 0, alt ? 60 : 45) : + cBRT > 75 ? RGBA(0, 0, 0, alt ? 75 : 50) : + cBRT > 50 ? RGBA(0, 0, 0, alt ? 100 : 55) : + cBRT > 25 ? RGBA(0, 0, 0, alt ? 120 : 70) : + cBRT >= 0 ? RGBA(0, 0, 0, alt ? 140 : 90) : ''; + } + + /////////////////////////////////////////////////////// + // * REBORN/RANDOM/STYLE REBORN WHITE/BLACK/FUSION * // + /////////////////////////////////////////////////////// + // * Dynamically adjust background colors, lines, transport buttons, progress/volume bar, gradient and shadow + if ((this.THEME === 'reborn' || this.THEME === 'random') && fb.IsPlaying && !grm.ui.isStreaming && !grm.ui.isPlayingCD && !grm.ui.noAlbumArtStub) { + const primary = this.RF2 ? grCol.primary_alt : grCol.primary; + const primary_alt = this.RF ? grCol.primary_alt : grCol.primary; + + // * PLAYLIST COLORS * // + pl.col.header_nowplaying_bg = + cBRT > 200 ? blend ? RGBtoRGBA(TintColor(primary, 20), 130) : TintColor(primary, 20) : + cBRT > 175 ? blend ? RGBtoRGBA(TintColor(primary, 12), 130) : TintColor(primary, 12) : + cBRT > 150 ? blend ? RGBtoRGBA(TintColor(primary, 12), 130) : TintColor(primary, 12) : + cBRT > 125 ? blend ? RGBtoRGBA(TintColor(primary, 10), 130) : TintColor(primary, 10) : + cBRT > 100 ? blend ? RGBtoRGBA(TintColor(primary, 10), 130) : TintColor(primary, 10) : + cBRT > 75 ? blend ? RGBtoRGBA(TintColor(primary, 8), 130) : TintColor(primary, 8) : + cBRT > 50 ? blend ? RGBtoRGBA(TintColor(primary, 6), 130) : TintColor(primary, 6) : + cBRT > 25 ? blend ? RGBtoRGBA(TintColor(primary, 6), 130) : TintColor(primary, 6) : + cBRT >= 0 ? cBRT < 10 ? TintColor(primary, blend || blend2 ? 15 : 10) : + TintColor(primary, blend || blend2 ? 15 : 5) : ''; + + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + + pl.col.header_line_normal = + cBRT > 200 ? ShadeColor(primary, 16) : + cBRT > 175 ? ShadeColor(primary, 18) : + cBRT > 150 ? ShadeColor(primary, 20) : + cBRT > 125 ? ShadeColor(primary, 22) : + cBRT > 100 ? ShadeColor(primary, 24) : + cBRT > 75 ? ShadeColor(primary, 26) : + cBRT > 50 ? ShadeColor(primary, 28) : + cBRT > 25 ? ShadeColor(primary, 30) : + cBRT >= 0 ? TintColor(primary, 15) : ''; + + pl.col.header_line_playing = + cBRT > 200 ? ShadeColor(primary, 26) : + cBRT > 175 ? ShadeColor(primary, 28) : + cBRT > 150 ? ShadeColor(primary, 30) : + cBRT > 125 ? ShadeColor(primary, 32) : + cBRT > 100 ? ShadeColor(primary, 34) : + cBRT > 75 ? ShadeColor(primary, 36) : + cBRT > 50 ? ShadeColor(primary, 38) : + cBRT > 25 ? ShadeColor(primary, 40) : + cBRT >= 0 ? TintColor(primary, 20) : ''; + + pl.col.row_selection_frame = pl.col.header_line_normal; + pl.col.row_disc_subheader_line = pl.col.header_line_normal; + pl.col.row_drag_line = pl.col.row_sideMarker; + pl.col.row_drag_line_reached = cBRT > 210 ? ShadeColor(pl.col.row_sideMarker, 25) : TintColor(pl.col.row_sideMarker, 50); + + // * LIBRARY COLORS * // + lib.ui.col.selectionFrame = pl.col.header_line_normal; + lib.ui.col.line = pl.col.header_line_playing; + + // * MAIN COLORS * // + grCol.styleGradient = + cBRT > 200 ? ShadeColor(primary, this.RB ? 65 : 25) : + cBRT > 175 ? ShadeColor(primary, this.RB ? 60 : 30) : + cBRT > 150 ? ShadeColor(primary, this.RB ? 55 : 35) : + cBRT > 125 ? ShadeColor(primary, this.RB ? 50 : 40) : + cBRT > 100 ? ShadeColor(primary, this.RB ? 45 : 45) : + cBRT > 75 ? ShadeColor(primary, this.RB ? 40 : 50) : + cBRT > 50 ? ShadeColor(primary, this.RB ? 35 : 55) : + cBRT > 25 ? ShadeColor(primary, this.RB ? 30 : 60) : + cBRT >= 0 ? this.RB ? TintColor(primary, 10) : ShadeColor(primary, 25) : ''; + + grCol.styleGradient2 = grCol.styleGradient; + + if (!this.RW && !this.RB && !this.RF) { + grCol.bg = + cBRT < 10 ? TintColor(primary_alt, blend || blend2 ? 15 : 10) : + cBRT < 25 ? TintColor(primary_alt, blend || blend2 ? 15 : 5) : + grCol.bg; + + grCol.progressBar = + cBRT > 200 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 20 : 10) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 175 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 25 : 10) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 150 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 25 : 10) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 125 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 30 : 15) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 100 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 30 : 15) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 75 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 35 : 20) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 50 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 45 : 25) : ShadeColor(primary_alt, bevel ? 30 : 15) : + cBRT > 25 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 0 : 0) : ShadeColor(primary_alt, bevel ? 40 : 15) : + cBRT >= 0 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 0 : 0) : ShadeColor(primary_alt, bevel ? 40 : 10) : ''; + } + + grCol.progressBarFill = + cBRT < 175 > 125 ? progBarFillBevelInner ? TintColor(grCol.progressBarFill, 10) : grCol.progressBarFill : + grCol.progressBarFill; + + grCol.styleProgressBarFill = + cBRT > 200 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 25 : 20) : '' : + cBRT > 175 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 30 : 25) : '' : + cBRT > 150 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 35 : 30) : '' : + cBRT > 125 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 45 : 40) : '' : + cBRT > 100 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 50 : 45) : '' : + cBRT > 75 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 55 : 50) : '' : + cBRT > 50 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 65 : 60) : '' : + cBRT > 25 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 75 : 70) : '' : + cBRT >= 0 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 85 : 80) : '' : ''; + + if (!this.RW && !this.RB) { + grCol.styleProgressBarLineTop = + cBRT > 200 ? RGBA(0, 0, 0, bevel ? 15 : 10) : + cBRT > 175 ? RGBA(0, 0, 0, bevel ? 20 : 15) : + cBRT > 150 ? RGBA(0, 0, 0, bevel ? 30 : 25) : + cBRT > 125 ? RGBA(0, 0, 0, bevel ? 35 : 30) : + cBRT > 100 ? RGBA(0, 0, 0, bevel ? 40 : 35) : + cBRT > 75 ? RGBA(0, 0, 0, bevel ? 45 : 40) : + cBRT > 50 ? RGBA(0, 0, 0, bevel ? 50 : 45) : + cBRT > 25 ? RGBA(0, 0, 0, bevel ? 55 : 50) : + cBRT >= 0 ? RGBA(0, 0, 0, bevel ? 65 : 60) : ''; + + grCol.styleProgressBarLineBottom = + cBRT > 200 ? RGBA(255, 255, 255, bevel ? 45 : 50) : + cBRT > 175 ? RGBA(255, 255, 255, bevel ? 40 : 45) : + cBRT > 150 ? RGBA(255, 255, 255, bevel ? 35 : 40) : + cBRT > 125 ? RGBA(255, 255, 255, bevel ? 30 : 35) : + cBRT > 100 ? RGBA(255, 255, 255, bevel ? 25 : 30) : + cBRT > 75 ? RGBA(255, 255, 255, bevel ? 20 : 25) : + cBRT > 50 ? RGBA(255, 255, 255, bevel ? 15 : 20) : + cBRT > 25 ? RGBA(255, 255, 255, bevel ? 10 : 15) : + cBRT >= 0 ? RGBA(255, 255, 255, bevel ? 5 : 10) : ''; + } + + grCol.shadow = + cBRT > 200 ? RGBA(0, 0, 0, alt ? 45 : 35) : + cBRT > 175 ? RGBA(0, 0, 0, alt ? 45 : 35) : + cBRT > 150 ? RGBA(0, 0, 0, alt ? 45 : 35) : + cBRT > 125 ? RGBA(0, 0, 0, alt ? 50 : 40) : + cBRT > 100 ? RGBA(0, 0, 0, alt ? 60 : 45) : + cBRT > 75 ? RGBA(0, 0, 0, alt ? 75 : 50) : + cBRT > 50 ? RGBA(0, 0, 0, alt ? 100 : 55) : + cBRT > 25 ? RGBA(0, 0, 0, alt ? 120 : 70) : + cBRT >= 0 ? RGBA(0, 0, 0, alt ? 140 : 90) : ''; + + + // * REBORN/RANDOM THEME/STYLE REBORN WHITE/REBORN BLACK - Adjust colors primary color is almost pure white + const defaultRebornRandom = cBRT > 210 && (!blend && !blend2) && !this.RW && !this.RB && !this.RF && !this.RF2 && !this.RD; + const rebornWhiteBlack = cBRT > 210 && (this.RW || this.RB); + + if (defaultRebornRandom || rebornWhiteBlack) { + // * PLAYLIST COLORS * // + pl.col.bg = RGB(255, 255, 255); + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = pl.col.bg; + + pl.col.header_nowplaying_bg = + defaultRebornRandom ? blend && iBRT > 240 ? grCol.lightAccent_7 : blend2 && iBRT > 240 ? grCol.lightAccent_35 : RGB(245, 245, 245) : + rebornWhiteBlack ? blend ? RGBtoRGBA(grCol.lightAccent, 130) : RGB(245, 245, 245) : + pl.col.header_nowplaying_bg; + + pl.col.header_sideMarker = RGB(90, 90, 90); + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_sideMarker = pl.col.header_sideMarker; + pl.col.row_selection_frame = RGB(220, 220, 220); + pl.col.sbar_btn_normal = RGB(90, 90, 90); + pl.col.sbar_btn_hovered = RGB(0, 0, 0); + pl.col.sbar_thumb_normal = RGB(235, 235, 235); + pl.col.sbar_thumb_hovered = RGB(90, 90, 90); + pl.col.sbar_thumb_drag = pl.col.sbar_thumb_hovered; + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.bgSel = pl.col.header_nowplaying_bg; + lib.ui.col.nowPlayingBg = pl.col.header_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.header_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.sbarBtns = RGB(90, 90, 90); + lib.ui.col.sbarNormal = RGB(210, 210, 210); + lib.ui.col.sbarHovered = RGB(90, 90, 90); + lib.ui.col.sbarDrag = RGB(90, 90, 90); + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.sbarBtns = RGB(90, 90, 90); + bio.ui.col.sbarNormal = RGB(210, 210, 210); + bio.ui.col.sbarHovered = RGB(90, 90, 90); + bio.ui.col.sbarDrag = RGB(90, 90, 90); + + // * MAIN COLORS * // + grCol.bg = this.RB ? RGB(0, 0, 0) : bevel ? RGB(255, 255, 255) : RGB(245, 245, 245); + grCol.detailsBg = pl.col.bg; + + if (this.BR || this.RW || this.RB) { + grCol.transportEllipseBg = grCol.lightAccent_100; + grCol.transportEllipseNormal = ShadeColor(grCol.lightAccent_7, 10); + } + + if (!this.RB) { + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = + (this.BLEND || this.BLEND2) && fb.IsPlaying ? RGB(230, 230, 230) : + this.BEVEL ? RGB(240, 240, 240) : RGB(255, 255, 255); + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + this.PB === 'bevel' ? this.BEVEL ? RGB(245, 245, 245) : RGB(220, 220, 220) : + this.BEVEL ? this.BLEND || this.BLEND2 ? RGB(235, 235, 235) : RGB(225, 225, 225) : + (this.BLEND || this.BLEND2) && fb.IsPlaying && !grm.ui.noAlbumArtStub ? RGB(240, 240, 240) : + RGB(220, 220, 220); + + grCol.progressBarStreaming = RGB(207, 0, 5); + grCol.progressBarFrame = this.BEVEL ? RGB(180, 180, 180) : grCol.bg; + grCol.progressBarFill = RGB(90, 90, 90); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = pl.col.bg; + grCol.volumeBarFill = RGB(90, 90, 90); + } + + if (this.RB) { + // * STYLE COLORS * // + grCol.styleGradient = grCol.darkAccent_75; + grCol.styleGradient2 = grCol.darkAccent_75; + } + } + + // * REBORN/RANDOM THEME - Adjust colors when using style blend and album art is almost pure white + if (iBRT > 210 && (blend || blend2) && !this.RW && !this.RB && !this.RD) { + grCol.primary = TintColor(grCol.primary, 15); + + // * PLAYLIST COLORS * // + pl.col.header_nowplaying_bg = + blend && iBRT > 240 ? grCol.lightAccent_7 : + blend2 && iBRT > 240 ? grCol.lightAccent_35 : + RGBtoRGBA(grCol.lightAccent_50, 130); + + pl.col.header_sideMarker = cBRT < 150 ? grCol.lightAccent_80 : grCol.lightAccent_100; + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_sideMarker = pl.col.header_sideMarker; + + // * LIBRARY COLORS * // + lib.ui.col.sideMarker = cBRT < 150 ? grCol.lightAccent_80 : grCol.lightAccent_100; + + // * MAIN COLORS * // + grCol.transportEllipseNormal = cBRT < 150 ? grCol.transportEllipseNormal : ShadeColor(grCol.lightAccent_7, 10); + grCol.transportEllipseBg = cBRT < 150 ? grCol.lightAccent_80 : grCol.lightAccent_100; + grCol.progressBar = cBRT < 150 ? grCol.lightAccent_7 : blend2 && iBRT > 240 ? grCol.lightAccent_35 : ShadeColor(grCol.lightAccent_7, 5); + grCol.progressBarFill = cBRT < 150 ? grCol.lightAccent_80 : grCol.lightAccent_100; + } + + ///////////////////////////////////// + // * STYLE REBORN FUSION 1 AND 2 * // + ///////////////////////////////////// + // * ADJUST COLORS WHEN PANEL BG IS TOO LIGHT * // + if (cBRT > 210 && this.RF || cBRT2 > 210 && this.RF2) { + this.panelWhiteColors(true); + } + + // * ADJUST COLORS WHEN MAIN BG IS TOO LIGHT * // + if (cBRT2 > 210 && this.RF || cBRT > 210 && this.RF2) { + this.mainWhiteColors(); + } + + // * ADJUST COLORS WHEN PANEL BG IS TOO DARK * // + if (cBRT < 25 && this.RF || cBRT2 < 25 && this.RF2) { + this.panelBlackColors(true); + } + + // * ADJUST COLORS WHEN MAIN BG IS TOO DARK * // + if (cBRT2 < 25 && this.RF || cBRT < 25 && this.RF2) { + this.mainBlackColors(); + } + } + } + // #endregion +} + + +////////////////////// +// * STYLE COLORS * // +////////////////////// +/** + * A class that provides the full collection of all style colors and its methods. + */ +class StyleColors { + /** + * Creates the `StyleColors` instance and initializes theme preference values. + */ + constructor() { + this.initThemePrefVals(); + } + + // * PUBLIC METHODS - STYLE NIGHTTIME * // + // #region PUBLIC METHODS - STYLE NIGHTTIME + /** + * Active Reborn theme used in Options > Style > Nighttime. + */ + styleNighttimeColors() { + if (this.CTHEME) return; + + const rebornNightAccentColor = grSet.theme === 'reborn' ? RGB(210, 235, 240) : false; + + if (!fb.IsPlaying) { + grm.theme.panelBlackColors(false, rebornNightAccentColor); + grm.theme.mainBlackColors(); + } else { + grm.theme.playlistColorsRebornRandomTheme(); + grm.theme.libraryColorsRebornRandomTheme(); + grm.theme.biographyColorsRebornRandomTheme(); + grm.theme.mainColorsRebornRandomTheme(); + } + } + // #endregion + + // * PUBLIC METHODS - STYLE ALTERNATIVE * // + // #region PUBLIC METHODS - STYLE ALTERNATIVE + /** + * Any active theme used in Options > Style > Alternative. + */ + styleAlternativeColors() { + // * PLAYLIST * // + pl.col.bg = + grSet.theme === 'white' ? RGB(245, 245, 245) : + grSet.theme === 'black' ? TintColor(pl.col.bg, 6) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? ShadeColor(pl.col.bg, 5) : + grSet.theme === 'blue' ? RGB(8, 110, 190) : + grSet.theme === 'darkblue' ? RGB(17, 35, 57) : + grSet.theme === 'red' ? RGB(106, 18, 18) : + grSet.theme === 'cream' ? RGB(255, 247, 240) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? TintColor(pl.col.bg, 8) : + this.CTHEME ? ShadeColor(pl.col.bg, 5) : ''; + + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = pl.col.bg; + + pl.col.header_nowplaying_bg = + grSet.theme === 'blue' ? RGB(20, 120, 205) : + grSet.theme === 'darkblue' ? RGB(18, 42, 70) : + grSet.theme === 'red' ? RGB(130, 25, 25) : + pl.col.header_nowplaying_bg; + + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_selection_bg = pl.col.row_nowplaying_bg; + + // * LIBRARY * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + + // * BIOGRAPHY * // + bio.ui.col.bg = pl.col.bg; + + // * MAIN * // + grCol.bg = + grSet.theme === 'white' ? RGB(255, 255, 255) : + grSet.theme === 'black' ? TintColor(grCol.bg, 4) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? TintColor(grCol.bg, 8) : + grSet.theme === 'blue' ? RGB(20, 120, 205) : + grSet.theme === 'darkblue' ? RGB(18, 42, 70) : + grSet.theme === 'red' ? RGB(130, 25, 25) : + grSet.theme === 'cream' ? RGB(255, 255, 255) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? RGB(30, 30, 30) : + this.CTHEME ? TintColor(grCol.bg, 8) : ''; + + grCol.uiHacksFrame = grCol.bg; + + grCol.shadow = + grSet.theme === 'reborn' || grSet.theme === 'random' ? RGBA(0, 0, 0, 25) : + grSet.theme === 'blue' ? grCol.shadow + RGBA(0, 0, 0, 25) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? RGBA(0, 0, 0, 255) : + grCol.shadow + RGBA(0, 0, 0, 10); + + grCol.detailsBg = pl.col.bg; + grCol.detailsText = pl.col.row_title_normal; + + // * LOWER BAR TRANSPORT BUTTONS * // + grCol.transportEllipseBg = + grSet.theme === 'white' ? TintColor(grCol.transportEllipseBg, 100) : + grSet.theme === 'black' ? TintColor(grCol.transportEllipseBg, 4) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? TintColor(grCol.transportEllipseBg, 0) : + grSet.theme === 'blue' ? TintColor(grCol.transportEllipseBg, 6) : + grSet.theme === 'darkblue' ? TintColor(grCol.transportEllipseBg, 0) : + grSet.theme === 'red' ? RGB(158, 30, 30) : + grSet.theme === 'cream' ? pl.col.bg : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? ShadeColor(grCol.transportEllipseBg, 20) : + ShadeColor(grCol.transportEllipseBg, 10); + + grCol.transportEllipseNormal = + grSet.theme === 'black' ? ShadeColor(grCol.transportEllipseNormal, 6) : + grSet.theme === 'red' ? RGB(106, 18, 18) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? ShadeColor(grCol.transportEllipseNormal, 90) : + TintColor(grCol.transportEllipseNormal, 6); + + grCol.transportEllipseHovered = TintColor(grCol.transportEllipseHovered, 6); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + grSet.theme === 'black' ? TintColor(grCol.transportStyleBg, 4) : + grSet.theme === 'blue' ? TintColor(grCol.transportStyleBg, 6) : + grSet.theme === 'darkblue' ? TintColor(grCol.transportStyleBg, 0) : + grSet.theme === 'red' ? TintColor(grCol.transportStyleBg, 2) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? ShadeColor(grCol.transportStyleBg, 6) : + TintColor(grCol.transportStyleBg, 6); + + grCol.transportStyleTop = + grSet.theme === 'black' ? ShadeColor(grCol.transportStyleTop, 6) : + grSet.theme === 'blue' ? ShadeColor(grCol.transportStyleTop, 6) : + grSet.theme === 'darkblue' ? ShadeColor(grCol.transportStyleTop, 0) : + grSet.theme === 'red' ? TintColor(grCol.transportStyleTop, this.TPB === 'emboss' ? 6 : 2) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? ShadeColor(grCol.transportStyleTop, 6) : + TintColor(grCol.transportStyleTop, 6); + + grCol.transportStyleBottom = + grSet.theme === 'black' ? ShadeColor(grCol.transportStyleBottom, 0) : + grSet.theme === 'blue' ? ShadeColor(grCol.transportStyleBottom, 6) : + grSet.theme === 'darkblue' ? ShadeColor(grCol.transportStyleBottom, 0) : + grSet.theme === 'red' ? TintColor(grCol.transportStyleBottom, this.TPB === 'emboss' ? 6 : 2) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? ShadeColor(grCol.transportStyleBottom, 6) : + TintColor(grCol.transportStyleBottom, 6); + + // * PROGRESS BAR * // + grCol.progressBar = + grSet.theme === 'white' ? this.BEVEL ? TintColor(grCol.progressBar, 60) : TintColor(grCol.progressBar, 40) : + grSet.theme === 'black' ? TintColor(grCol.progressBar, 2) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? grCol.colBrightness < 25 ? TintColor(grCol.primary, 12) : fb.IsPlaying && !grm.ui.noAlbumArtStub ? pl.col.bg : grCol.progressBar : + grSet.theme === 'blue' ? TintColor(grCol.progressBar, 2) : + grSet.theme === 'darkblue' ? TintColor(grCol.progressBar, 0) : + grSet.theme === 'red' ? RGB(158, 30, 30) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? this.BEVEL ? ShadeColor(grCol.progressBar, 60) : ShadeColor(grCol.progressBar, 35) : + this.CTHEME ? ShadeColor(grCol.progressBar, 5) : + pl.col.bg; + + // * VOLUME BAR * // + grCol.volumeBar = grCol.progressBar; + grCol.volumeBarFill = grCol.progressBarFill; + } + // #endregion + + // * PUBLIC METHODS - STYLE ALTERNATIVE 2 * // + // #region PUBLIC METHODS - STYLE ALTERNATIVE 2 + /** + * Any active theme used in Options > Style > Alternative 2. + */ + styleAlternative2Colors() { + // * PLAYLIST * // + pl.col.bg = + grSet.theme === 'white' ? TintColor(pl.col.bg, 4) : + grSet.theme === 'black' ? TintColor(pl.col.bg, 3) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? TintColor(pl.col.bg, 5) : + grSet.theme === 'blue' ? RGB(20, 120, 205) : + grSet.theme === 'darkblue' ? RGB(18, 42, 70) : + grSet.theme === 'red' ? RGB(120, 22, 22) : + grSet.theme === 'cream' ? RGB(255, 255, 255) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? TintColor(pl.col.bg, 6) : + this.CTHEME ? TintColor(pl.col.bg, 5) : ''; + + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = pl.col.bg; + + pl.col.header_nowplaying_bg = + grSet.theme === 'black' && grCol.colBrightness < 25 ? TintColor(grCol.primary, 15) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? this.BLEND ? RGBtoRGBA(grCol.darkAccent, 60) : RGBtoRGBA(grCol.darkAccent, 40) : + grSet.theme === 'red' ? RGB(140, 25, 25) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? TintColor(pl.col.header_nowplaying_bg, 6) : + pl.col.header_nowplaying_bg; + + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_selection_bg = pl.col.row_nowplaying_bg; + + // * LIBRARY * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + + // * BIOGRAPHY * // + bio.ui.col.bg = pl.col.bg; + + // * MAIN * // + grCol.bg = + grSet.theme === 'white' ? ShadeColor(grCol.bg, 6) : + grSet.theme === 'black' ? TintColor(grCol.bg, 10) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? ShadeColor(grCol.bg, 8) : + grSet.theme === 'blue' ? RGB(8, 102, 180) : + grSet.theme === 'darkblue' ? RGB(17, 35, 57) : + grSet.theme === 'red' ? RGB(95, 15, 15) : + grSet.theme === 'cream' ? RGB(255, 247, 240) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? RGB(25, 25, 25) : + this.CTHEME ? ShadeColor(grCol.bg, 8) : ''; + + grCol.uiHacksFrame = grCol.bg; + + grCol.shadow = + grSet.theme === 'black' ? grCol.shadow - RGBA(0, 0, 0, 80) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? grCol.shadow : + grCol.shadow + RGBA(0, 0, 0, 5); + + grCol.detailsBg = pl.col.bg; + grCol.detailsText = pl.col.row_title_normal; + + // * LOWER BAR TRANSPORT BUTTONS * // + grCol.transportEllipseBg = + grSet.theme === 'white' ? TintColor(grCol.transportEllipseBg, 100) : + grSet.theme === 'black' ? ShadeColor(grCol.transportEllipseBg, 6) : + grSet.theme === 'darkblue' ? TintColor(grCol.transportEllipseBg, 0) : + grSet.theme === 'red' ? RGB(140, 25, 25) : + TintColor(grCol.transportEllipseBg, 6); + + grCol.transportEllipseNormal = + grSet.theme === 'black' ? ShadeColor(grCol.transportEllipseNormal, 60) : + TintColor(grCol.transportEllipseNormal, 6); + + grCol.transportEllipseHovered = TintColor(grCol.transportEllipseHovered, 6); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + grSet.theme === 'white' ? this.BEVEL ? ShadeColor(grCol.transportStyleBg, 10) : ShadeColor(grCol.transportStyleBg, 7) : + grSet.theme === 'darkblue' ? TintColor(grCol.transportStyleBg, 0) : + grSet.theme === 'red' ? TintColor(grCol.transportStyleBg, 2) : + grSet.theme === 'cream' ? RGB(230, 230, 230) : + TintColor(grCol.transportStyleBg, 6); + + grCol.transportStyleTop = + grSet.theme === 'white' ? ShadeColor(grCol.transportStyleTop, 6) : + grSet.theme === 'blue' ? TintColor(grCol.transportStyleTop, 12) : + grSet.theme === 'darkblue' ? TintColor(grCol.transportStyleTop, 0) : + grSet.theme === 'red' ? TintColor(grCol.transportStyleTop, 2) : + TintColor(grCol.transportStyleTop, 6); + + grCol.transportStyleBottom = + grSet.theme === 'white' ? ShadeColor(grCol.transportStyleBottom, 6) : + grSet.theme === 'blue' ? ShadeColor(grCol.transportStyleBottom, 10) : + grSet.theme === 'darkblue' ? TintColor(grCol.transportStyleBottom, 0) : + grSet.theme === 'red' ? TintColor(grCol.transportStyleBottom, 2) : + TintColor(grCol.transportStyleBottom, 6); + + // * PROGRESS BAR * // + grCol.progressBar = + grSet.theme === 'white' ? this.BEVEL ? TintColor(grCol.progressBar, 60) : TintColor(grCol.progressBar, 100) : + grSet.theme === 'black' ? ShadeColor(grCol.progressBar, 16) : + grSet.theme === 'reborn' || grSet.theme === 'random' ? grCol.colBrightness < 25 ? TintColor(grCol.primary, 12) : pl.col.bg : + grSet.theme === 'blue' ? pl.col.bg : + grSet.theme === 'darkblue' ? pl.col.row_nowplaying_bg : + grSet.theme === 'red' ? TintColor(grCol.progressBar, 0) : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? TintColor(grCol.progressBar, 3) : + this.CTHEME ? ShadeColor(grCol.progressBar, 2) : + pl.col.bg; + + // * VOLUME BAR * // + grCol.volumeBar = grCol.progressBar; + grCol.volumeBarFill = grCol.progressBarFill; + } + // #endregion + + // * PUBLIC METHODS - STYLE BLACK AND WHITE * // + // #region PUBLIC METHODS - STYLE BLACK AND WHITE + /** + * Active White theme used in Options > Style > Black and white. + */ + styleBlackAndWhiteColors() { + grCol.primary = RGB(255, 255, 255); + grm.theme.panelBlackColors(); + grm.theme.mainWhiteColors(); + } + // #endregion + + // * PUBLIC METHODS - STYLE BLACK AND WHITE 2 * // + // #region PUBLIC METHODS - STYLE BLACK AND WHITE 2 + /** + * Active White theme used in Options > Style > Black and white 2. + */ + styleBlackAndWhite2Colors() { + grCol.primary = RGB(255, 255, 255); + grm.theme.panelWhiteColors(); + grm.theme.mainBlackColors(); + } + // #endregion + + // * PUBLIC METHODS - STYLE BLACK REBORN * // + // #region PUBLIC METHODS - STYLE BLACK REBORN + /** + * Active Black theme used in Options > Style > Black reborn. + */ + styleBlackRebornColors() { + if (!fb.IsPlaying || !grm.ui.albumArt && !grm.ui.noAlbumArtStub) grCol.primary = RGB(25, 25, 25); + if (grm.ui.isStreaming || grm.ui.isPlayingCD) grm.color.setNoAlbumArtColors(); + + // * PLAYLIST COLORS * // + pl.col.bg = RGB(20, 20, 20); + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = pl.col.bg; + pl.col.header_nowplaying_bg = grCol.colBrightness < 25 ? grCol.lightAccent : this.BEVEL ? ShadeColor(grCol.primary, 10) : grCol.primary; + pl.col.header_line_normal = this.BLEND ? RGB(65, 65, 65) : RGB(45, 45, 45); + pl.col.header_line_playing = RGB(25, 25, 25); + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_disc_subheader_line = this.BLEND ? RGB(65, 65, 65) : RGB(45, 45, 45); + pl.col.row_drag_line = grCol.lightBg ? ShadeColor(pl.col.row_selection_frame, 20) : TintColor(pl.col.row_selection_frame, 20); + pl.col.row_drag_line_reached = pl.col.row_sideMarker; + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.nowPlayingBg = pl.col.row_nowplaying_bg; + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + + // * MAIN COLORS * // + grCol.bg = grCol.colBrightness < 25 || grm.ui.isStreaming || grm.ui.isPlayingCD ? this.BEVEL ? RGB(40, 40, 40) : RGB(25, 25, 25) : grCol.primary; + grCol.detailsBg = pl.col.bg; + grCol.detailsText = RGB(220, 220, 220); + grCol.styleBevel = grm.ui.isStreaming || grm.ui.isPlayingCD || !fb.IsPlaying ? RGB(0, 0, 0) : grCol.primary === RGB(175, 205, 225) ? RGB(70, 90, 105) : grCol.darkAccent_50; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = pl.col.bg; + grCol.transportEllipseNormal = grCol.transportEllipseBg; + grCol.transportEllipseHovered = RGB(120, 120, 120); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * WHEN PLAYING * // + if (fb.IsPlaying) { + if (grCol.lightBg) { + // * PLAYLIST COLORS * // + pl.col.row_title_playing = grCol.darkAccent_100; + + // * MAIN COLORS * // + grCol.noAlbumArtStub = RGB(175, 205, 225); + grCol.lowerBarArtist = grCol.darkAccent_75; + grCol.lowerBarTitle = grCol.darkAccent_75; + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 40) : RGBtoRGBA(grCol.lightAccent_80, 50) : + this.TMB !== 'default' ? grCol.lightAccent_10 : grCol.lightAccent_50; + + grCol.menuRectNormal = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 40) : RGBtoRGBA(grCol.lightAccent_80, 30) : + grCol.darkAccent; + + grCol.menuRectHovered = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 40) : RGBtoRGBA(grCol.darkAccent_75, 30) : + grCol.darkAccent; + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = grCol.darkAccent_75; + grCol.menuTextHovered = grCol.darkAccent_100; + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportIconNormal = this.TPB === 'minimal' ? RGB(20, 20, 20) : RGB(180, 180, 180); + grCol.menuStyleBg = grCol.primary === RGB(175, 205, 225) ? RGB(130, 153, 168) : grCol.accent; + grCol.menuRectStyleEmbossTop = grCol.lightAccent; + grCol.menuRectStyleEmbossBottom = grCol.darkAccent; + } + else { + // * PLAYLIST COLORS * // + pl.col.row_title_playing = grCol.lightAccent_100; + + // * MAIN COLORS * // + grCol.noAlbumArtStub = RGB(175, 205, 225); + grCol.lowerBarArtist = grCol.lightAccent_100; + grCol.lowerBarTitle = grCol.lightAccent_100; + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.lightAccent_80, 40) : RGBtoRGBA(grCol.lightAccent_80, 50) : + this.TMB !== 'default' ? grCol.lightAccent_10 : grCol.darkAccent_50; + + grCol.menuRectNormal = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 60) : RGBtoRGBA(grCol.darkAccent_75, 50) : + grCol.lightAccent_50; + + grCol.menuRectHovered = + this.TMB === 'bevel' || this.TMB === 'inner' ? + this.BEVEL ? RGBtoRGBA(grCol.darkAccent_75, 60) : RGBtoRGBA(grCol.darkAccent_75, 50) : + grCol.lightAccent_50; + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = grCol.lightAccent_80; + grCol.menuTextHovered = grCol.lightAccent_100; + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportIconNormal = this.TPB === 'minimal' ? RGB(220, 220, 220) : RGB(180, 180, 180); + grCol.menuStyleBg = grCol.primary === RGB(175, 205, 225) ? RGB(130, 153, 168) : grCol.accent; + grCol.menuRectStyleEmbossTop = grCol.lightAccent; + grCol.menuRectStyleEmbossBottom = grCol.darkAccent; + } + } + + // * PROGRESS BAR COLORS * // + grCol.progressBar = + grCol.colBrightness < 50 ? + this.PB === 'bevel' ? RGB(30, 30, 30) : + this.PB === 'inner' ? RGB(30, 30, 30) : + this.BEVEL ? grCol.colBrightness < 25 ? RGB(30, 30, 30) : RGB(0, 0, 0) : RGB(0, 0, 0) : + pl.col.bg; + + if (grCol.primary === RGB(175, 205, 225)) { + grCol.progressBarFill = this.BLEND || this.BLEND2 ? RGB(155, 185, 205) : RGB(145, 170, 190); + } + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness > 200 || grCol.colBrightness < 75 ? TintColor(grCol.primary, 100) : ShadeColor(grCol.primary, 100); + grCol.peakmeterBarFillTop = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 10) : TintColor(grCol.primary, 40); + grCol.peakmeterBarFillMiddle = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 20) : TintColor(grCol.primary, 60); + grCol.peakmeterBarFillBack = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 40) : TintColor(grCol.primary, 80); + grCol.peakmeterBarVertProgFill = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 25) : grCol.progressBarFill; + grCol.peakmeterBarVertFill = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 15) : TintColor(grCol.primary, 40); + grCol.peakmeterBarVertFillPeaks = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 25) : TintColor(grCol.primary, 60); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 80) : TintColor(grCol.primary, 90); + grCol.waveformBarFillBack = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 40) : TintColor(grCol.primary, 45); + grCol.waveformBarFillPreFront = grCol.colBrightness > 150 ? ShadeColor(grCol.primary, 40) : TintColor(grCol.primary, 50); + grCol.waveformBarFillPreBack = grCol.colBrightness > 150 ? ShadeColor(grCol.primary, 20) : TintColor(grCol.primary, 25); + grCol.waveformBarIndicator = grCol.colBrightness > 200 ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = pl.col.bg; + grCol.volumeBarFill = grCol.progressBarFill; + + // * STYLE COLORS * // + grCol.styleVolumeBar = + this.VB === 'bevel' ? this.BEVEL ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : + this.VB === 'inner' ? this.BEVEL ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : ''; + + grCol.styleVolumeBarFill = this.VBF === 'bevel' || this.VBF === 'inner' ? RGBA(0, 0, 0, 100) : ''; + } + // #endregion + + // * PUBLIC METHODS - STYLE REBORN WHITE * // + // #region PUBLIC METHODS - STYLE REBORN WHITE + /** + * Active Reborn theme used in Options > Style > Reborn white. + */ + styleRebornWhiteColors() { + // * PLAYLIST COLORS * // + pl.col.bg = !fb.IsPlaying ? RGB(255, 255, 255) : pl.col.bg; + + // * MAIN COLORS * // + grCol.bg = this.BLEND || this.BLEND2 ? RGB(255, 255, 255) : RGB(245, 245, 245); + grCol.noAlbumArtStub = RGB(90, 90, 90); + grCol.lowerBarArtist = this.BLEND || this.BLEND2 ? RGB(40, 40, 40) : RGB(80, 80, 80); + grCol.lowerBarTitle = this.BLEND || this.BLEND2 ? RGB(50, 50, 50) : RGB(100, 100, 100); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(250, 250, 250) : RGB(255, 255, 255) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(250, 250, 250) : RGB(235, 235, 235) : + RGB(255, 255, 255); + + grCol.menuRectNormal = + this.TMB === 'filled' ? RGB(200, 200, 200) : + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(140, 140, 140) : RGB(150, 150, 150) : + this.BEVEL ? RGB(170, 170, 170) : RGB(180, 180, 180); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGB(200, 200, 200) : + this.TMB === 'bevel' || this.TMB === 'inner' ? this.BEVEL ? RGB(200, 200, 200) : RGB(220, 220, 220) : + this.BLEND || this.BLEND2 ? this.BEVEL ? RGB(140, 140, 140) : RGB(150, 150, 150) : + this.BEVEL ? RGB(170, 170, 170) : RGB(180, 180, 180); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? RGB(50, 50, 50) : RGB(100, 100, 100); + grCol.menuTextHovered = this.BLEND || this.BLEND2 ? RGB(0, 0, 0) : RGB(80, 80, 80); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = this.BLEND || this.BLEND2 ? RGB(225, 225, 225) : RGB(255, 255, 255); + grCol.transportEllipseNormal = RGB(220, 220, 220); + grCol.transportEllipseHovered = RGB(200, 200, 200); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + grCol.transportIconNormal = this.BLEND || this.BLEND2 ? RGB(80, 80, 80) : RGB(100, 100, 100); + grCol.transportIconHovered = RGB(80, 80, 80); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = this.BEVEL ? RGB(225, 225, 225) : RGB(220, 220, 220); + grCol.progressBarFill = ShadeColor(grCol.primary, 5); + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness < 75 ? TintColor(grCol.primary, 40) : ShadeColor(grCol.primary, 40); + grCol.peakmeterBarFillTop = TintColor(grCol.primary, 10); + grCol.peakmeterBarFillMiddle = TintColor(grCol.primary, 30); + grCol.peakmeterBarFillBack = TintColor(grCol.primary, 50); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = ShadeColor(grCol.primary, 10); + grCol.peakmeterBarVertFillPeaks = TintColor(grCol.primary, 20); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.primary; + grCol.waveformBarFillBack = ShadeColor(grCol.primary, 20); + grCol.waveformBarFillPreFront = RGB(180, 180, 180); + grCol.waveformBarFillPreBack = RGB(160, 160, 160); + grCol.waveformBarIndicator = grCol.colBrightness > 200 ? RGB(0, 0, 0) : TintColor(grCol.primary, 30); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(255, 255, 255); + grCol.volumeBarFill = grCol.primary; + grCol.volumeBarFrame = RGB(220, 220, 220); + + // * STYLE COLORS * // + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 50) : + this.BEVEL ? RGBA(0, 0, 0, 10) : RGBA(0, 0, 0, 0) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 50) : + this.BEVEL ? RGBA(0, 0, 0, 10) : RGBA(0, 0, 0, 20) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 255) : + this.BEVEL ? RGBA(255, 255, 255, 100) : + this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 255) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 20) : RGBA(0, 0, 0, 25) : + this.BEVEL ? RGBA(0, 0, 0, 5) : + this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 255) : ''; + } + // #endregion + + // * PUBLIC METHODS - STYLE REBORN BLACK * // + // #region PUBLIC METHODS - STYLE REBORN BLACK + /** + * Active Reborn theme used in Options > Style > Reborn black. + */ + styleRebornBlackColors() { + // * MAIN COLORS * // + grCol.bg = this.BEVEL ? RGB(40, 40, 40) : RGB(20, 20, 20); + grCol.noAlbumArtStub = RGB(90, 90, 90); + grCol.lowerBarArtist = RGB(240, 240, 240); + grCol.lowerBarTitle = this.BLEND || this.BLEND2 ? RGB(220, 220, 220) : RGB(200, 200, 200); + grCol.lowerBarTime = grCol.lowerBarTitle; + grCol.lowerBarLength = grCol.lowerBarTitle; + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = + this.TMB === 'bevel' ? this.BEVEL ? RGB(40, 40, 40) : RGB(50, 50, 50) : + this.TMB === 'inner' ? this.BEVEL ? RGB(55, 55, 55) : RGB(50, 50, 50) : + this.TMB === 'emboss' ? RGB(45, 45, 45) : + grCol.darkAccent_50; + + grCol.menuStyleBg = + this.TMB === 'inner' ? RGB(20, 20, 20) : + this.TMB === 'emboss' ? this.BEVEL ? RGB(45, 45, 45) : RGB(50, 50, 50) : + this.BEVEL ? RGB(30, 30, 30) : RGB(20, 20, 20); + + grCol.menuRectStyleEmbossTop = this.BEVEL ? RGB(60, 60, 60) : RGB(70, 70, 70); + grCol.menuRectStyleEmbossBottom = RGB(0, 0, 0); + + grCol.menuRectNormal = this.TMB === 'filled' ? RGBA(60, 60, 60, 100) : RGB(60, 60, 60); + + grCol.menuRectHovered = + this.TMB === 'filled' ? RGBA(120, 120, 120, 100) : + this.TMB === 'bevel' || this.TMB === 'inner' ? RGB(0, 0, 0) : + RGB(120, 120, 120); + + grCol.menuRectDown = grCol.menuRectHovered; + grCol.menuTextNormal = this.BLEND || this.BLEND2 ? RGB(220, 220, 220) : RGB(180, 180, 180); + grCol.menuTextHovered = RGB(255, 255, 255); + grCol.menuTextDown = grCol.menuTextHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = this.BLEND || this.BLEND2 ? RGB(50, 50, 50) : RGB(35, 35, 35); + grCol.transportEllipseNormal = RGB(60, 60, 60); + grCol.transportEllipseHovered = RGB(120, 120, 120); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.transportStyleBg = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(20, 20, 20) : + this.TPB === 'emboss' ? RGB(50, 50, 50) : ''; + + grCol.transportStyleTop = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(50, 50, 50) : + this.TPB === 'emboss' ? this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; + + grCol.transportStyleBottom = + this.TPB === 'bevel' || this.TPB === 'inner' ? RGB(10, 10, 10) : + this.TPB === 'emboss' ? RGB(20, 20, 20) : ''; + + grCol.transportIconNormal = this.BLEND || this.BLEND2 ? RGB(180, 180, 180) : RGB(160, 160, 160); + grCol.transportIconHovered = RGB(255, 255, 255); + grCol.transportIconDown = grCol.transportIconHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = RGB(50, 50, 50); + grCol.progressBarFill = grCol.colBrightness < 50 ? grCol.lightAccent : grCol.primary; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness > 200 ? ShadeColor(grCol.primary, 40) : grCol.colBrightness < 50 ? TintColor(grCol.primary, 50) : TintColor(grCol.primary, 40); + grCol.peakmeterBarFillTop = grCol.colBrightness < 50 ? TintColor(grCol.primary, 20) : TintColor(grCol.primary, 10); + grCol.peakmeterBarFillMiddle = grCol.colBrightness < 50 ? TintColor(grCol.primary, 40) : TintColor(grCol.primary, 30); + grCol.peakmeterBarFillBack = grCol.colBrightness < 50 ? TintColor(grCol.primary, 30) : ShadeColor(grCol.primary, 15); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = grCol.colBrightness < 50 ? TintColor(grCol.primary, 20) : ShadeColor(grCol.primary, 10); + grCol.peakmeterBarVertFillPeaks = grCol.colBrightness < 50 ? TintColor(grCol.primary, 30) : TintColor(grCol.primary, 20); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.colBrightness < 50 ? TintColor(grCol.primary, 40) : grCol.colBrightness < 100 ? TintColor(grCol.primary, 20) : grCol.primary; + grCol.waveformBarFillBack = grCol.colBrightness < 50 ? TintColor(grCol.primary, 20) : grCol.colBrightness < 100 ? grCol.primary : ShadeColor(grCol.primary, 20); + grCol.waveformBarFillPreFront = RGB(100, 100, 100); + grCol.waveformBarFillPreBack = RGB(80, 80, 80); + grCol.waveformBarIndicator = grCol.colBrightness > 200 ? RGB(255, 255, 255) : RGB(220, 220, 220); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = RGB(35, 35, 35); + grCol.volumeBarFill = grCol.colBrightness < 50 ? grCol.lightAccent : grCol.primary; + grCol.volumeBarFrame = RGB(60, 60, 60); + + // * STYLE COLORS * // + grCol.styleProgressBarLineTop = + this.PB === 'bevel' ? RGBA(0, 0, 0, 255) : + this.PB === 'inner' ? this.PBD === 'rounded' ? this.BEVEL ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 150) : + this.BEVEL ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 100) : ''; + + grCol.styleProgressBarLineBottom = + this.PB === 'bevel' ? this.PBD === 'rounded' ? RGBA(255, 255, 255, 30) : + this.BEVEL ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 25) : + this.PB === 'inner' ? this.PBD === 'rounded' ? RGBA(255, 255, 255, 30) : + this.BEVEL ? RGBA(255, 255, 255, 45) : + this.BLEND || this.BLEND2 ? RGBA(255, 255, 255, 25) : + grCol.colBrightness < 50 ? RGBA(255, 255, 255, 15) : RGBA(255, 255, 255, 40) : ''; + } + // #endregion + + // * PUBLIC METHODS - STYLE REBORN FUSION * // + // #region PUBLIC METHODS - STYLE REBORN FUSION + /** + * Active Reborn theme used in Options > Style > Reborn fusion. + */ + styleRebornFusionColors() { + if (!(fb.IsPlaying && grCol.isColored)) return; + const smallColDiff = ColorDistance(grCol.primary, grCol.primary_alt) < 100; + + // * PLAYLIST COLORS * // + pl.col.header_nowplaying_bg = this.BLEND ? RGBtoRGBA(grCol.lightAccent_7_alt, 130) : grCol.lightAccent_7_alt; + pl.col.header_sideMarker = smallColDiff ? grCol.lightAccent_35_alt : grCol.primary_alt; + pl.col.row_sideMarker = pl.col.header_sideMarker; + + // * MAIN COLORS * // + grCol.bg = grCol.primary_alt; + grCol.uiHacksFrame = grCol.bg; + grCol.transportEllipseBg = grCol.lightAccent_50_alt; + grCol.transportEllipseNormal = grCol.lightAccent_alt; + grCol.transportEllipseHovered = grCol.lightAccent_50_alt; + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + // * PROGRESS BAR COLORS * // + grCol.progressBar = grCol.accent_alt; + grCol.progressBarFill = smallColDiff ? grCol.lightAccent_50 : grCol.primary; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness2 > 200 ? ShadeColor(grCol.primary_alt, 100) : grCol.colBrightness2 < 75 ? TintColor(grCol.primary_alt, 100) : TintColor(grCol.primary_alt, 40); + grCol.peakmeterBarFillTop = grCol.colBrightness2 > 200 ? ShadeColor(grCol.primary_alt, 10) : TintColor(grCol.primary_alt, 40); + grCol.peakmeterBarFillMiddle = grCol.colBrightness2 > 200 ? ShadeColor(grCol.primary_alt, 20) : TintColor(grCol.primary_alt, 60); + grCol.peakmeterBarFillBack = grCol.colBrightness2 > 200 ? ShadeColor(grCol.primary_alt, 40) : TintColor(grCol.primary_alt, 80); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = smallColDiff ? grCol.lightAccent_50 : grCol.primary; + grCol.peakmeterBarVertFillPeaks = TintColor(grCol.primary, 60); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.colBrightness2 > 200 || grm.ui.noAlbumArtStub ? ShadeColor(grCol.primary_alt, 80) : TintColor(grCol.primary_alt, 90); + grCol.waveformBarFillBack = grCol.colBrightness2 > 200 || grm.ui.noAlbumArtStub ? ShadeColor(grCol.primary_alt, 40) : TintColor(grCol.primary_alt, 45); + grCol.waveformBarFillPreFront = grCol.colBrightness2 > 150 ? ShadeColor(grCol.primary_alt, 40) : TintColor(grCol.primary_alt, 50); + grCol.waveformBarFillPreBack = grCol.colBrightness2 > 150 ? ShadeColor(grCol.primary_alt, 20) : TintColor(grCol.primary_alt, 25); + grCol.waveformBarIndicator = grCol.colBrightness2 > 200 || grm.ui.noAlbumArtStub ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = grCol.transportEllipseBg; + grCol.volumeBarFrame = grCol.volumeBar; + grCol.volumeBarFill = grCol.progressBarFill; + } + // #endregion + + // * PUBLIC METHODS - STYLE REBORN FUSION 2 * // + // #region PUBLIC METHODS - STYLE REBORN FUSION 2 + /** + * Active Reborn theme used in Options > Style > Reborn fusion 2. + */ + styleRebornFusion2Colors() { + if (!(fb.IsPlaying && grCol.isColored)) return; + const smallColDiff = ColorDistance(grCol.primary, grCol.primary_alt) < 100; + + // * PLAYLIST COLORS * // + pl.col.bg = grCol.primary_alt; + pl.col.plman_bg = pl.col.bg; + pl.col.plman_text_normal = pl.col.bg; + pl.col.header_nowplaying_bg = this.BLEND ? RGBtoRGBA(grCol.lightAccent_7, 130) : TintColor(grCol.primary_alt, 10); + pl.col.header_sideMarker = smallColDiff ? grCol.lightAccent_35 : grCol.primary; + pl.col.row_nowplaying_bg = pl.col.header_nowplaying_bg; + pl.col.row_sideMarker = smallColDiff ? grCol.lightAccent_35 : grCol.primary; + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.rowStripes = pl.col.row_stripes_bg; + lib.ui.col.nowPlayingBg = libSet.albumArtShow ? TintColor(pl.col.row_nowplaying_bg, 7) : pl.col.row_nowplaying_bg; + lib.ui.col.sideMarker = pl.col.row_sideMarker; + lib.ui.col.selectionFrame = pl.col.row_selection_frame; + lib.ui.col.selectionFrame2 = lib.ui.col.sideMarker; + lib.ui.col.hoverFrame = lib.ui.col.sideMarker; + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.rowStripes = pl.col.row_stripes_bg; + + // * MAIN COLORS * // + grCol.bg = grCol.primary; + grCol.detailsBg = grCol.primary_alt !== RGB(25, 160, 240) && grm.ui.albumArt && !grm.ui.isStreaming ? grCol.primary_alt : RGB(255, 255, 255); + grCol.detailsText = + grm.ui.isStreaming || grm.ui.isPlayingCD || !grm.ui.albumArt ? RGB(120, 120, 120) : + grCol.lightBgDetails ? RGB(55, 55, 55) : + RGB(255, 255, 255); + + grCol.transportEllipseBg = grCol.lightAccent_65; + grCol.transportEllipseNormal = grCol.lightAccent; + grCol.transportEllipseHovered = grCol.lightAccent_50; + grCol.transportEllipseDown = grCol.transportEllipseHovered; + + grCol.progressBar = this.BEVEL ? ShadeColor(grCol.primary_alt, 5) : grCol.lightAccent; + grCol.progressBarFill = smallColDiff ? grCol.lightAccent_35 : grCol.primary_alt; + grCol.volumeBar = grCol.transportEllipseBg; + grCol.volumeBarFrame = grCol.volumeBar; + grCol.volumeBarFill = smallColDiff ? grCol.lightAccent_35 : grCol.primary; + } + // #endregion + + // * PUBLIC METHODS - STYLE REBORN FUSION ACCENT * // + // #region PUBLIC METHODS - STYLE REBORN FUSION ACCENT + /** + * Active Reborn theme used in Options > Style > Reborn fusion accent. + */ + styleRebornFusionAccentColors() { + if (!(fb.IsPlaying && grCol.isColored)) return; + const smallColDiff = ColorDistance(grCol.primary, grCol.primary_alt) < 100; + + pl.col.header_nowplaying_bg = smallColDiff ? grCol.colBrightness > 150 ? grCol.darkAccent_50_alt : grCol.lightAccent_50_alt : grCol.primary_alt; + pl.col.header_sideMarker = pl.col.header_nowplaying_bg; + pl.col.row_nowplaying_bg = grCol.primary_alt; + pl.col.row_sideMarker = pl.col.header_sideMarker; + lib.ui.col.sideMarker = pl.col.header_sideMarker; + + grCol.progressBarFill = smallColDiff ? grCol.colBrightness > 150 ? grCol.darkAccent_50_alt : grCol.lightAccent_50_alt : grCol.primary_alt; + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = grCol.progressBar; + grCol.peakmeterBarProgFill = grCol.colBrightness > 150 ? ShadeColor(grCol.primary_alt, smallColDiff ? 80 : 50) : TintColor(grCol.primary_alt, smallColDiff ? 80 : 50); + grCol.peakmeterBarFillTop = grCol.colBrightness > 150 ? ShadeColor(grCol.primary_alt, smallColDiff ? 40 : 10) : TintColor(grCol.primary_alt, smallColDiff ? 40 : 10); + grCol.peakmeterBarFillMiddle = grCol.colBrightness > 150 ? ShadeColor(grCol.primary_alt, smallColDiff ? 30 : 0) : TintColor(grCol.primary_alt, smallColDiff ? 30 : 0); + grCol.peakmeterBarFillBack = grCol.colBrightness > 150 ? ShadeColor(grCol.primary_alt, smallColDiff ? 60 : 30) : TintColor(grCol.primary_alt, smallColDiff ? 60 : 30); + grCol.peakmeterBarVertProgFill = grCol.progressBarFill; + grCol.peakmeterBarVertFill = grCol.progressBarFill; + grCol.peakmeterBarVertFillPeaks = TintColor(grCol.primary_alt, 60); + if (grm.peakBar) grm.peakBar.setColors(fb.GetNowPlaying()); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = grCol.colBrightness > 150 ? ShadeColor(grCol.primary_alt, smallColDiff || grCol.colBrightness > 200 || grm.ui.noAlbumArtStub ? 60 : 10) : TintColor(grCol.primary_alt, smallColDiff ? 60 : 10); + grCol.waveformBarFillBack = grCol.colBrightness > 150 ? ShadeColor(grCol.primary_alt, smallColDiff || grCol.colBrightness > 200 || grm.ui.noAlbumArtStub ? 80 : 20) : TintColor(grCol.primary_alt, smallColDiff ? 80 : 20); + grCol.waveformBarFillPreFront = grCol.colBrightness > 150 ? ShadeColor(grCol.primary, 20) : TintColor(grCol.primary, 30); + grCol.waveformBarFillPreBack = grCol.colBrightness > 150 ? ShadeColor(grCol.primary, 30) : TintColor(grCol.primary, 40); + grCol.waveformBarIndicator = grCol.colBrightness > 200 || grm.ui.noAlbumArtStub ? RGB(0, 0, 0) : RGB(255, 255, 255); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = grCol.transportEllipseBg; + grCol.volumeBarFrame = grCol.volumeBar; + grCol.volumeBarFill = grCol.progressBarFill; + } + // #endregion + + // * PUBLIC METHODS - STYLE INITIALIZATION * // + // #region PUBLIC METHODS - STYLE INITIALIZATION + /** + * Initializes current values for all theme pref.properties. + */ + initThemePrefVals() { + /** @private Options > Theme. */ + this.THEME = grSet.theme; + /** @private Options > Theme > Custom. */ + this.CTHEME = grSet.theme.startsWith('custom'); + /** @private Options > Style > Bevel. */ + this.BEVEL = grSet.styleBevel; + /** @private Options > Style > Blend. */ + this.BLEND = grSet.styleBlend; + /** @private Options > Style > Blend 2. */ + this.BLEND2 = grSet.styleBlend2; + /** @private Options > Style > Gradient. */ + this.GRAD = grSet.styleGradient; + /** @private Options > Style > Gradient 2. */ + this.GRAD2 = grSet.styleGradient2; + /** @private Options > Style > Alternative. */ + this.ALT = grSet.styleAlternative; + /** @private Options > Style > Alternative 2. */ + this.ALT2 = grSet.styleAlternative2; + /** @private Options > Style > Black and white ( White theme ). */ + this.BW = grSet.styleBlackAndWhite; + /** @private Options > Style > Black and white 2 ( White theme ). */ + this.BW2 = grSet.styleBlackAndWhite2; + /** @private Options > Style > Black and white reborn ( White theme ). */ + this.BWR = grSet.styleBlackAndWhiteReborn; + /** @private Options > Style > Black reborn ( Black theme ). */ + this.BR = grSet.styleBlackReborn; + /** @private Options > Style > Reborn white ( Reborn theme ). */ + this.RW = grSet.styleRebornWhite; + /** @private Options > Style > Reborn black ( Reborn theme ). */ + this.RB = grSet.styleRebornBlack; + /** @private Options > Style > Reborn fusion ( Reborn theme ). */ + this.RF = grSet.styleRebornFusion; + /** @private Options > Style > Reborn fusion 2 ( Reborn theme ). */ + this.RF2 = grSet.styleRebornFusion2; + /** @private Options > Style > Reborn fusion accent ( Reborn theme ). */ + this.RFA = grSet.styleRebornFusionAccent; + /** @private Options > Style > Random pastel ( Random theme ). */ + this.RP = grSet.styleRandomPastel; + /** @private Options > Style > Random dark ( Random theme ). */ + this.RD = grSet.styleRandomDark; + /** @private Options > Style > Auto color ( Random theme ). */ + this.RAC = grSet.styleRandomAutoColor; + /** @private Options > Style > Buttons > Top menu. */ + this.TMB = grSet.styleTopMenuButtons; + /** @private Options > Style > Buttons > Transport. */ + this.TPB = grSet.styleTransportButtons; + /** @private Options > Style > Progress bar > Design. */ + this.PBD = grSet.styleProgressBarDesign; + /** @private Options > Style > Progress bar > Background. */ + this.PB = grSet.styleProgressBar; + /** @private Options > Style > Progress bar > Progress fill. */ + this.PBF = grSet.styleProgressBarFill; + /** @private Options > Style > Volume bar > Design. */ + this.VBD = grSet.styleVolumeBarDesign; + /** @private Options > Style > Volume bar > Background. */ + this.VB = grSet.styleVolumeBar; + /** @private Options > Style > Volume bar > Volume fill. */ + this.VBF = grSet.styleVolumeBarFill; + /** @private Options > Layout. */ + this.LAYOUT = grSet.layout; + /** @private Options > Brightness. */ + this.BRT = grSet.themeBrightness; + } + + /** + * Init all colors that are used in styles, mostly called from grMain.ui.initTheme(). + */ + initStyleColors() { + if ((['reborn', 'random'].includes(grSet.theme) || this.CTHEME) && + (grSet.styleNighttime || grSet.themeDayNightMode && grSet.themeDayNightTime === 'night') && !this.RW) { + this.styleNighttimeColors(); + } + + const styleColors = { + styleAlternative: this.styleAlternativeColors.bind(this), + styleAlternative2: this.styleAlternative2Colors.bind(this), + styleBlackAndWhite: this.styleBlackAndWhiteColors.bind(this), + styleBlackAndWhite2: this.styleBlackAndWhite2Colors.bind(this), + styleBlackReborn: this.styleBlackRebornColors.bind(this), + styleRebornWhite: this.styleRebornWhiteColors.bind(this), + styleRebornBlack: this.styleRebornBlackColors.bind(this), + styleRebornFusion: this.styleRebornFusionColors.bind(this), + styleRebornFusion2: this.styleRebornFusion2Colors.bind(this), + styleRebornFusionAccent: this.styleRebornFusionAccentColors.bind(this) + }; + + for (const [styleName, styleFunction] of Object.entries(styleColors)) { + if (grSet[styleName]) { + styleFunction(); + break; + } + } + } + + /** + * Init style Black And White Reborn, dynamically change between style Black and white 1 and 2. + */ + initBlackAndWhiteReborn() { + grm.color.setImageBrightness(); + + if (grCol.imgBrightness > 150) { + this.BW2 = true; // White background + this.BW = false; + } + else { + this.BW = true; // Black background + this.BW2 = false; + } + } + // #endregion +} + + +/////////////////////// +// * COLOR METHODS * // +/////////////////////// +/** + * A class that provides color specific methods and is responsible for handling color operations. + * Allows for creation, manipulation, adjustment, and application of colors to the theme. + */ +class ColorMethods { + // * PUBLIC METHODS - GENERAL * // + // #region PUBLIC METHODS - GENERAL + /** + * Adjusts the text and button colors based on the brightness level provided. + * @param {number} percent - The percentage to adjust the brightness by. + * @param {boolean} darken - Whether to darken the color. + * @param {boolean} darkenMax - Whether to apply maximum darkening. + * @param {boolean} lighten - Whether to lighten the color. + * @param {boolean} lightenMax - Whether to apply maximum lightening. + */ + adjustTextButtonColors(percent, darken, darkenMax, lighten, lightenMax) { + /** + * Sets the color brightness based on the provided parameters. + * It selectively shades or tints the color based on the brightness adjustment directives. + * @param {number} color - The base color to be adjusted. + * @param {boolean} [boost] - If true, increases the intensity of the shade or tint. + * @param {boolean} [soften] - If true, decreases the intensity of the shade or tint. + * @returns {number} The adjusted color as a numerical value. + */ + const SetColor = (color, boost = false, soften = false) => { + switch (true) { + case darken: + return ShadeColor(color, percent * (boost ? 1.75 : soften ? 1.25 : 1.5)); + case darkenMax: + return ShadeColor(color, boost ? 100 : soften ? 60 : 85); + case lighten: + return TintColor(color, percent * (boost ? 1.75 : soften ? 1.25 : 1.5)); + case lightenMax: + return TintColor(color, boost ? 100 : soften ? 60 : 85); + } + }; + + const playlistColors = { + plman_text_normal: grSet.autoHidePlman ? pl.col.bg : SetColor(pl.col.plman_text_normal), + plman_text_hovered: SetColor(pl.col.plman_text_hovered, true), + plman_text_pressed: SetColor(pl.col.plman_text_pressed, true), + header_artist_normal: SetColor(pl.col.header_artist_normal), + header_artist_playing: SetColor(pl.col.header_artist_playing, true), + header_album_normal: SetColor(pl.col.header_album_normal), + header_album_playing: SetColor(pl.col.header_album_playing, true), + header_info_normal: SetColor(pl.col.header_info_normal, true), + header_info_playing: SetColor(pl.col.header_info_playing, true), + header_date_normal: SetColor(pl.col.header_date_normal), + header_date_playing: SetColor(pl.col.header_date_playing, true), + row_title_normal: SetColor(pl.col.row_title_normal), + row_title_playing: SetColor(pl.col.row_title_playing, true), + row_title_selected: SetColor(pl.col.row_title_selected, true), + row_title_hovered: SetColor(pl.col.row_title_hovered, true), + sbar_btn_normal: SetColor(pl.col.sbar_btn_normal), + sbar_btn_hovered: SetColor(pl.col.sbar_btn_hovered, true), + sbar_thumb_normal: SetColor(pl.col.sbar_thumb_normal, false, true), + sbar_thumb_hovered: SetColor(pl.col.sbar_thumb_hovered, true), + sbar_thumb_drag: SetColor(pl.col.sbar_thumb_drag, true) + }; + Object.assign(pl.col, playlistColors); + + const libraryColors = { + iconPlus: SetColor(lib.ui.col.iconPlus), + iconPlus_h: SetColor(lib.ui.col.iconPlus_h, true), + iconPlus_sel: SetColor(lib.ui.col.iconPlus_sel, true), + iconPlusBg: SetColor(lib.ui.col.iconPlusBg), + iconMinus_e: SetColor(lib.ui.col.iconMinus_e), + iconMinus_h: SetColor(lib.ui.col.iconMinus_h, true), + text: SetColor(lib.ui.col.text), + text_h: SetColor(lib.ui.col.text_h, true), + text_nowp: SetColor(lib.ui.col.text_nowp, true), + textSel: SetColor(lib.ui.col.textSel, true), + txt_box: SetColor(lib.ui.col.txt_box), + search: SetColor(lib.ui.col.search), + searchBtn: SetColor(lib.ui.col.searchBtn), + crossBtn: SetColor(lib.ui.col.crossBtn), + filterBtn: SetColor(lib.ui.col.filterBtn), + settingsBtn: SetColor(lib.ui.col.settingsBtn), + line: SetColor(lib.ui.col.line), + sbarBtns: SetColor(lib.ui.col.sbarBtns), + sbarNormal: SetColor(lib.ui.col.sbarNormal), + sbarHovered: SetColor(lib.ui.col.sbarHovered, true), + sbarDrag: SetColor(lib.ui.col.sbarDrag, true) + }; + Object.assign(lib.ui.col, libraryColors); + + const biographyColors = { + headingText: SetColor(bio.ui.col.headingText), + iconMinus_e: SetColor(bio.ui.col.iconMinus_e), + iconMinus_h: SetColor(bio.ui.col.iconMinus_h), + text: SetColor(bio.ui.col.text), + source: SetColor(bio.ui.col.source), + accent: SetColor(bio.ui.col.accent), + summary: SetColor(bio.ui.col.summary), + sbarBtns: SetColor(bio.ui.col.sbarBtns), + sbarNormal: SetColor(bio.ui.sbarNormal), + sbarHovered: SetColor(bio.ui.col.sbarHovered, true), + sbarDrag: SetColor(bio.ui.col.sbarDrag, true) + }; + Object.assign(bio.ui.col, biographyColors); + + const mainColors = { + detailsText: SetColor(grCol.detailsText), + popupText: SetColor(grCol.popupText), + noAlbumArtStub: SetColor(grCol.noAlbumArtStub), + lowerBarArtist: SetColor(grCol.lowerBarArtist), + lowerBarTitle: SetColor(grCol.lowerBarTitle), + lowerBarTime: SetColor(grCol.lowerBarTime), + lowerBarLength: SetColor(grCol.lowerBarLength), + menuTextNormal: SetColor(grCol.menuTextNormal), + menuTextHovered: SetColor(grCol.menuTextHovered, true), + menuTextDown: SetColor(grCol.menuTextDown, true), + transportIconNormal: !['reborn', 'random'].includes(grSet.theme) ? SetColor(grCol.transportIconNormal) : grCol.transportIconNormal, + transportIconHovered: !['reborn', 'random'].includes(grSet.theme) ? SetColor(grCol.transportIconHovered, true) : grCol.transportIconHovered, + transportIconDown: !['reborn', 'random'].includes(grSet.theme) ? SetColor(grCol.transportIconDown, true) : grCol.transportIconDown + }; + Object.assign(grCol, mainColors); + + window.Repaint(); + } + + /** + * Lightens or darkens the theme based on this.BRT value, used in Options > Brightness. + * @param {number} percent - The percentage number for lightening or darkening all colors in the theme. + */ + adjustThemeBrightness(percent) { + if (percent < 0) percent = Math.abs(percent); // Negative passed values need to be converted to positives + + if (this.BRT < 0) { // * Darken + // * PLAYLIST COLORS * // + pl.col.bg = ShadeColor(pl.col.bg, percent); + pl.col.plman_bg = ShadeColor(pl.col.plman_bg, percent); + pl.col.plman_text_normal = ShadeColor(pl.col.plman_text_normal, percent); + pl.col.header_nowplaying_bg = ShadeColor(pl.col.header_nowplaying_bg, percent); + pl.col.header_sideMarker = ShadeColor(pl.col.header_sideMarker, percent); + pl.col.header_line_normal = ShadeColor(pl.col.header_line_normal, percent); + pl.col.header_line_playing = ShadeColor(pl.col.header_line_playing, percent); + pl.col.row_nowplaying_bg = ShadeColor(pl.col.row_nowplaying_bg, percent); + pl.col.row_stripes_bg = ShadeColor(pl.col.row_stripes_bg, percent); + pl.col.row_selection_bg = ShadeColor(pl.col.row_selection_bg, percent); + pl.col.row_selection_frame = ShadeColor(pl.col.row_selection_frame, percent); + pl.col.row_sideMarker = ShadeColor(pl.col.row_sideMarker, percent); + pl.col.row_disc_subheader_line = ShadeColor(pl.col.row_disc_subheader_line, percent); + pl.col.sbar_btn_normal = ShadeColor(pl.col.sbar_btn_normal, percent); + pl.col.sbar_btn_hovered = ShadeColor(pl.col.sbar_btn_hovered, percent); + pl.col.sbar_thumb_normal = ShadeColor(pl.col.sbar_thumb_normal, percent); + pl.col.sbar_thumb_hovered = ShadeColor(pl.col.sbar_thumb_hovered, percent); + pl.col.sbar_thumb_drag = ShadeColor(pl.col.sbar_thumb_drag, percent); + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.line = ShadeColor(lib.ui.col.line, percent); + lib.ui.col.s_line = ShadeColor(lib.ui.col.s_line, percent); + lib.ui.col.nowPlayingBg = ShadeColor(lib.ui.col.nowPlayingBg, percent); + lib.ui.col.sideMarker = ShadeColor(lib.ui.col.sideMarker, percent); + lib.ui.col.sideMarker_nobw = ShadeColor(lib.ui.col.sideMarker_nobw, percent); + lib.ui.col.selectionFrame = ShadeColor(lib.ui.col.selectionFrame, percent); + lib.ui.col.sbarBtns = ShadeColor(lib.ui.col.sbarBtns, percent); + lib.ui.col.sbarNormal = ShadeColor(lib.ui.col.sbarNormal, percent); + lib.ui.col.sbarHovered = ShadeColor(lib.ui.col.sbarHovered, percent); + lib.ui.col.sbarDrag = ShadeColor(lib.ui.col.sbarDrag, percent); + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = pl.col.header_line_normal; + bio.ui.col.sbarBtns = ShadeColor(bio.ui.col.sbarBtns, percent); + bio.ui.col.sbarNormal = ShadeColor(bio.ui.col.sbarNormal, percent); + bio.ui.col.sbarHovered = ShadeColor(bio.ui.col.sbarHovered, percent); + bio.ui.col.sbarDrag = ShadeColor(bio.ui.col.sbarDrag, percent); + + // * MAIN COLORS * // + grCol.bg = ShadeColor(grCol.bg, percent); + grCol.uiHacksFrame = ShadeColor(grCol.uiHacksFrame, percent); + grCol.shadow = ShadeColor(grCol.shadow, percent); + grCol.detailsBg = ShadeColor(grCol.detailsBg, percent); + grCol.timelineAdded = ShadeColor(grCol.timelineAdded, percent); + grCol.timelinePlayed = ShadeColor(grCol.timelinePlayed, percent); + grCol.timelineUnplayed = ShadeColor(grCol.timelineUnplayed, percent); + grCol.timelineFrame = ShadeColor(grCol.timelineFrame, percent); + grCol.popupBg = ShadeColor(grCol.popupBg, percent); + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = ShadeColor(grCol.menuBgColor, percent); + grCol.menuStyleBg = ShadeColor(grCol.menuStyleBg, percent); + grCol.menuRectStyleEmbossTop = ShadeColor(grCol.menuRectStyleEmbossTop, percent); + grCol.menuRectStyleEmbossBottom = ShadeColor(grCol.menuRectStyleEmbossBottom, percent); + grCol.menuRectNormal = ShadeColor(grCol.menuRectNormal, percent); + grCol.menuRectHovered = ShadeColor(grCol.menuRectHovered, percent); + grCol.menuRectDown = grCol.menuRectHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = ShadeColor(grCol.transportEllipseBg, percent); + grCol.transportEllipseNormal = ShadeColor(grCol.transportEllipseNormal, percent); + grCol.transportEllipseHovered = ShadeColor(grCol.transportEllipseHovered, percent); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + grCol.transportStyleBg = ShadeColor(grCol.transportStyleBg, percent); + grCol.transportStyleTop = ShadeColor(grCol.transportStyleTop, percent); + grCol.transportStyleBottom = ShadeColor(grCol.transportStyleBottom, percent); + + // * PROGRESS BAR COLORS * // + grCol.progressBar = ShadeColor(grCol.progressBar, percent); + grCol.progressBarStreaming = ShadeColor(grCol.progressBarStreaming, percent); + grCol.progressBarFrame = ShadeColor(grCol.progressBarFrame, percent); + grCol.progressBarFill = ShadeColor(grCol.progressBarFill, percent); + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = ShadeColor(grCol.peakmeterBarProg, percent); + grCol.peakmeterBarProgFill = ShadeColor(grCol.peakmeterBarProgFill, percent); + grCol.peakmeterBarFillTop = ShadeColor(grCol.peakmeterBarFillTop, percent); + grCol.peakmeterBarFillMiddle = ShadeColor(grCol.peakmeterBarFillMiddle, percent); + grCol.peakmeterBarFillBack = ShadeColor(grCol.peakmeterBarFillBack, percent); + grCol.peakmeterBarVertProgFill = ShadeColor(grCol.peakmeterBarVertProgFill, percent); + grCol.peakmeterBarVertFill = ShadeColor(grCol.peakmeterBarVertFill, percent); + grCol.peakmeterBarVertFillPeaks = ShadeColor(grCol.peakmeterBarVertFillPeaks, percent); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = ShadeColor(grCol.waveformBarFillFront, percent); + grCol.waveformBarFillBack = ShadeColor(grCol.waveformBarFillBack, percent); + grCol.waveformBarFillPreFront = ShadeColor(grCol.waveformBarFillPreFront, percent); + grCol.waveformBarFillPreBack = ShadeColor(grCol.waveformBarFillPreBack, percent); + grCol.waveformBarIndicator = ShadeColor(grCol.waveformBarIndicator, percent); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = ShadeColor(grCol.volumeBar, percent); + grCol.volumeBarFrame = ShadeColor(grCol.volumeBarFrame, percent); + grCol.volumeBarFill = ShadeColor(grCol.volumeBarFill, percent); + + // * STYLE COLORS * // + grCol.styleProgressBar = ShadeColor(grCol.styleProgressBar, percent); + grCol.styleProgressBarLineTop = ShadeColor(grCol.styleProgressBarLineTop, percent); + grCol.styleProgressBarLineBottom = ShadeColor(grCol.styleProgressBarLineBottom, percent); + grCol.styleVolumeBar = ShadeColor(grCol.styleVolumeBar, percent); + + // * ONLY DARKEN BLACK TEXT AND BUTTON COLORS BUT NOT WHITE TEXT COLORS * // + const bgColBrightness = new Color(grCol.bg).brightness; + const txtColBrightness = new Color(pl.col.row_title_normal).brightness; + if (bgColBrightness < 200 && txtColBrightness < 150) { + this.adjustTextButtonColors(percent, true, false, false, false); + } + } + else if (this.BRT > 0) { // * Lighten + // * PLAYLIST COLORS * // + pl.col.bg = TintColor(pl.col.bg, percent); + pl.col.plman_bg = TintColor(pl.col.plman_bg, percent); + pl.col.plman_text_normal = TintColor(pl.col.plman_text_normal, percent); + pl.col.header_nowplaying_bg = TintColor(pl.col.header_nowplaying_bg, percent); + pl.col.header_sideMarker = TintColor(pl.col.header_sideMarker, percent); + pl.col.header_line_normal = TintColor(pl.col.header_line_normal, percent); + pl.col.header_line_playing = TintColor(pl.col.header_line_playing, percent); + pl.col.row_nowplaying_bg = TintColor(pl.col.row_nowplaying_bg, percent); + pl.col.row_stripes_bg = TintColor(pl.col.row_stripes_bg, percent); + pl.col.row_selection_bg = TintColor(pl.col.row_selection_bg, percent); + pl.col.row_selection_frame = TintColor(pl.col.row_selection_frame, percent); + pl.col.row_sideMarker = TintColor(pl.col.row_sideMarker, percent); + pl.col.row_disc_subheader_line = TintColor(pl.col.row_disc_subheader_line, percent); + pl.col.sbar_btn_normal = TintColor(pl.col.sbar_btn_normal, percent); + pl.col.sbar_btn_hovered = TintColor(pl.col.sbar_btn_hovered, percent); + pl.col.sbar_thumb_normal = TintColor(pl.col.sbar_thumb_normal, percent); + pl.col.sbar_thumb_hovered = TintColor(pl.col.sbar_thumb_hovered, percent); + pl.col.sbar_thumb_drag = TintColor(pl.col.sbar_thumb_drag, percent); + + // * LIBRARY COLORS * // + lib.ui.col.bg = pl.col.bg; + lib.ui.col.line = TintColor(lib.ui.col.line, percent); + lib.ui.col.s_line = TintColor(lib.ui.col.s_line, percent); + lib.ui.col.nowPlayingBg = TintColor(lib.ui.col.nowPlayingBg, percent); + lib.ui.col.sideMarker = TintColor(lib.ui.col.sideMarker, percent); + lib.ui.col.sideMarker_nobw = TintColor(lib.ui.col.sideMarker_nobw, percent); + lib.ui.col.selectionFrame = TintColor(lib.ui.col.selectionFrame, percent); + lib.ui.col.sbarBtns = TintColor(lib.ui.col.sbarBtns, percent); + lib.ui.col.sbarNormal = TintColor(lib.ui.col.sbarNormal, percent); + lib.ui.col.sbarHovered = TintColor(lib.ui.col.sbarHovered, percent); + lib.ui.col.sbarDrag = TintColor(lib.ui.col.sbarDrag, percent); + + // * BIOGRAPHY COLORS * // + bio.ui.col.bg = pl.col.bg; + bio.ui.col.bottomLine = pl.col.header_line_normal; + bio.ui.col.centerLine = pl.col.header_line_normal; + bio.ui.col.sbarBtns = TintColor(bio.ui.col.sbarBtns, percent); + bio.ui.col.sbarNormal = TintColor(bio.ui.col.sbarNormal, percent); + bio.ui.col.sbarHovered = TintColor(bio.ui.col.sbarHovered, percent); + bio.ui.col.sbarDrag = TintColor(bio.ui.col.sbarDrag, percent); + + // * MAIN COLORS * // + grCol.bg = TintColor(grCol.bg, percent); + grCol.uiHacksFrame = TintColor(grCol.uiHacksFrame, percent); + grCol.shadow = TintColor(grCol.shadow, percent); + grCol.detailsBg = TintColor(grCol.detailsBg, percent); + grCol.timelineAdded = TintColor(grCol.timelineAdded, percent); + grCol.timelinePlayed = TintColor(grCol.timelinePlayed, percent); + grCol.timelineUnplayed = TintColor(grCol.timelineUnplayed, percent); + grCol.timelineFrame = TintColor(grCol.timelineFrame, percent); + grCol.popupBg = TintColor(grCol.popupBg, percent); + if (grm.timeline) grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + + // * TOP MENU BUTTON COLORS * // + grCol.menuBgColor = TintColor(grCol.menuBgColor, percent); + grCol.menuStyleBg = TintColor(grCol.menuStyleBg, percent); + grCol.menuRectStyleEmbossTop = TintColor(grCol.menuRectStyleEmbossTop, percent); + grCol.menuRectStyleEmbossBottom = TintColor(grCol.menuRectStyleEmbossBottom, percent); + grCol.menuRectNormal = TintColor(grCol.menuRectNormal, percent); + grCol.menuRectHovered = TintColor(grCol.menuRectHovered, percent); + grCol.menuRectDown = grCol.menuRectHovered; + + // * LOWER BAR TRANSPORT BUTTON COLORS * // + grCol.transportEllipseBg = TintColor(grCol.transportEllipseBg, percent); + grCol.transportEllipseNormal = TintColor(grCol.transportEllipseNormal, percent); + grCol.transportEllipseHovered = TintColor(grCol.transportEllipseHovered, percent); + grCol.transportEllipseDown = grCol.transportEllipseHovered; + grCol.transportStyleBg = TintColor(grCol.transportStyleBg, percent); + grCol.transportStyleTop = TintColor(grCol.transportStyleTop, percent); + grCol.transportStyleBottom = TintColor(grCol.transportStyleBottom, percent); + + // * PROGRESS BAR COLORS * // + grCol.progressBar = TintColor(grCol.progressBar, percent); + grCol.progressBarStreaming = TintColor(grCol.progressBarStreaming, percent); + grCol.progressBarFrame = TintColor(grCol.progressBarFrame, percent); + grCol.progressBarFill = TintColor(grCol.progressBarFill, percent); + + // * PEAKMETER BAR COLORS * // + grCol.peakmeterBarProg = TintColor(grCol.peakmeterBarProg, percent); + grCol.peakmeterBarProgFill = TintColor(grCol.peakmeterBarProgFill, percent); + grCol.peakmeterBarFillTop = TintColor(grCol.peakmeterBarFillTop, percent); + grCol.peakmeterBarFillMiddle = TintColor(grCol.peakmeterBarFillMiddle, percent); + grCol.peakmeterBarFillBack = TintColor(grCol.peakmeterBarFillBack, percent); + grCol.peakmeterBarVertProgFill = TintColor(grCol.peakmeterBarVertProgFill, percent); + grCol.peakmeterBarVertFill = TintColor(grCol.peakmeterBarVertFill, percent); + grCol.peakmeterBarVertFillPeaks = TintColor(grCol.peakmeterBarVertFillPeaks, percent); + + // * WAVEFORM BAR COLORS * // + grCol.waveformBarFillFront = TintColor(grCol.waveformBarFillFront, percent); + grCol.waveformBarFillBack = TintColor(grCol.waveformBarFillBack, percent); + grCol.waveformBarFillPreFront = TintColor(grCol.waveformBarFillPreFront, percent); + grCol.waveformBarFillPreBack = TintColor(grCol.waveformBarFillPreBack, percent); + grCol.waveformBarIndicator = TintColor(grCol.waveformBarIndicator, percent); + + // * VOLUME BAR COLORS * // + grCol.volumeBar = TintColor(grCol.volumeBar, percent); + grCol.volumeBarFrame = TintColor(grCol.volumeBarFrame, percent); + grCol.volumeBarFill = TintColor(grCol.volumeBarFill, percent); + + // * STYLE COLORS * // + grCol.styleProgressBar = TintColor(grCol.styleProgressBar, percent); + grCol.styleProgressBarLineTop = TintColor(grCol.styleProgressBarLineTop, percent); + grCol.styleProgressBarLineBottom = TintColor(grCol.styleProgressBarLineBottom, percent); + grCol.styleVolumeBar = TintColor(grCol.styleVolumeBar, percent); + + // * LIGHTEN TEXT AND BUTTON COLORS * // + const bgColBrightness = new Color(grCol.bg).brightness; + if (bgColBrightness < 150 && bgColBrightness > 50) { + this.adjustTextButtonColors(percent, false, false, true, false); + } + } + + const bgColBrightness = new Color(grCol.bg).brightness; + if (this.BRT > 20 && bgColBrightness < 200 && bgColBrightness > 125) { + grCol.lightBg = false; + this.adjustTextButtonColors(percent, false, true, false, false); + } + else if (this.BRT < -20 && bgColBrightness < 150 && bgColBrightness > 50) { + grCol.lightBg = false; + this.adjustTextButtonColors(percent, false, false, false, true); + } + } + + /** + * Creates the color objects. + * @param {number} color - The primary color. + * @param {number} [color2] - The secondary color. + * @returns {Color} The color as an object. + */ + createThemeColorObject(color, color2) { + if (color2 === undefined) color2 = color; + const themeObj = { + primary: color.val, + primary_alt: color2.val, + darkAccent: ShadeColor(color.val, 30), + darkAccent_alt: ShadeColor(color2.val, 30), + accent: ShadeColor(color.val, 15), + accent_alt: ShadeColor(color2.val, 15), + lightAccent: TintColor(color.val, 20), + lightAccent_alt: TintColor(color2.val, 20) + }; + if (color.brightness < 18) { + // Hard code these values otherwise darkAccent and accent can be very hard to see on background + themeObj.darkAccent = RGB(32, 32, 32); + themeObj.darkAccent_alt = RGB(32, 32, 32); + themeObj.accent = RGB(56, 56, 56); + themeObj.accent_alt = RGB(56, 56, 56); + themeObj.lightAccent = RGB(78, 78, 78); + themeObj.lightAccent_alt = RGB(78, 78, 78); + } + else if (color.brightness < 40) { + themeObj.darkAccent = ShadeColor(color.val, 35); + themeObj.darkAccent_alt = ShadeColor(color2.val, 35); + themeObj.accent = TintColor(color.val, 10); + themeObj.accent_alt = TintColor(color2.val, 10); + themeObj.lightAccent = TintColor(color.val, 20); + themeObj.lightAccent_alt = TintColor(color2.val, 20); + } + else if (color.brightness > 210) { + themeObj.darkAccent = ShadeColor(color.val, 30); + themeObj.darkAccent_alt = ShadeColor(color2.val, 30); + themeObj.accent = ShadeColor(color.val, 20); + themeObj.accent_alt = ShadeColor(color2.val, 20); + themeObj.lightAccent = ShadeColor(color.val, 10); + themeObj.lightAccent_alt = ShadeColor(color2.val, 10); + } + return themeObj; + } + // #endregion + + // * PUBLIC METHODS - SET THEME COLORS * // + // #region PUBLIC METHODS - SET THEME COLORS + /** + * Sets Main, Playlist, Details, Library and Biography background color brightness rules. + * Based on background color and image brightness, text colors in theme will change accordingly to black or white. + * Used in White, Black, Reborn, Random and Custom themes. + */ + setBackgroundColorDefinition() { + const primaryBrightness = new Color(grCol.primary).brightness; + const primaryBrightnessAlt = new Color(grCol.primary_alt).brightness; + const colBrightness = grSet.styleRebornFusion ? primaryBrightnessAlt : primaryBrightness; + grCol.colBrightness = primaryBrightness; + grCol.colBrightness2 = primaryBrightnessAlt; + + const standardThemes = ['white', 'black', 'reborn', 'random', 'cream'].includes(grSet.theme) && !grSet.styleRebornFusion && !grSet.styleRebornFusion2; + const customThemes = grSet.theme.startsWith('custom'); + + // * STANDARD THEMES * // + if (standardThemes) { + const lightBrightness = (grCol.colBrightness > 150) && (!grSet.styleBlend && !grSet.styleBlend2 && !grSet.styleRandomDark); + + const lightBlend = + (grCol.colBrightness + grCol.imgBrightness > 285) && (grSet.styleBlend || grSet.styleBlend2) + && + (grCol.colBrightness > 150 && (grSet.theme === 'white' || grSet.theme === 'black') || grSet.theme === 'reborn' || grSet.theme === 'random' && !grSet.styleRandomDark); + + const noAlbumArt = grm.ui.noAlbumArtStub && (grSet.theme === 'white' && !grSet.styleBlackAndWhite || grSet.theme === 'reborn' || grSet.theme === 'random'); + + grCol.lightBg = lightBrightness || lightBlend || noAlbumArt || grSet.theme === 'cream'; + } + + // * GRADIENT STYLES, REBORN FUSION STYLES, CUSTOM THEMES * // + if (!(grSet.styleGradient || grSet.styleGradient2 || grSet.styleRebornFusion || grSet.styleRebornFusion2 || customThemes)) { + return; + } + + const gradientBrightness = (new Color(RGBAtoRGB(grCol.styleGradient)).brightness * 0.5); + const gradient2Brightness = (new Color(RGBAtoRGB(grCol.styleGradient2)).brightness * 0.5); + const colBrightnessGrad = colBrightness - gradientBrightness; + const colBrightnessGrad2 = colBrightness - gradient2Brightness; + + const mainBgColor = grSet.styleGradient ? colBrightnessGrad : grSet.styleGradient2 ? colBrightnessGrad2 : + new Color(grSet.styleRebornFusion2 ? primaryBrightness : (customThemes ? HEXtoRGB(grCfg.cTheme.grCol_bg) : primaryBrightnessAlt)).brightness; + const playlistBgColor = new Color(grSet.styleRebornFusion2 ? primaryBrightnessAlt : (customThemes ? HEXtoRGB(grCfg.cTheme.pl_col_bg) : primaryBrightness)).brightness; + const detailsBgColor = new Color(grSet.styleRebornFusion2 ? primaryBrightnessAlt : (customThemes ? HEXtoRGB(grCfg.cTheme.grCol_detailsBg) : primaryBrightness)).brightness; + const libraryBgColor = new Color(grSet.styleRebornFusion2 ? primaryBrightnessAlt : (customThemes ? HEXtoRGB(grCfg.cTheme.lib_ui_col_bg) : primaryBrightness)).brightness; + const biographyBgColor = new Color(grSet.styleRebornFusion2 ? primaryBrightnessAlt : (customThemes ? HEXtoRGB(grCfg.cTheme.bio_ui_col_bg) : primaryBrightness)).brightness; + + const isLightBg = (color) => + color + grCol.imgBrightness > 285 && (grSet.styleBlend || grSet.styleBlend2) + || + color > 150 && (!grSet.styleBlend && !grSet.styleBlend2 || grSet.styleBlend2 && grSet.styleRebornFusion2); + + grCol.lightBgMain = isLightBg(mainBgColor); + grCol.lightBgPlaylist = isLightBg(playlistBgColor); + grCol.lightBgDetails = isLightBg(detailsBgColor); + grCol.lightBgLibrary = isLightBg(libraryBgColor); + grCol.lightBgBiography = isLightBg(biographyBgColor); + } + + /** + * Applies a blur to the image based on the current theme and image brightness. + * @param {GdiBitmap} image - The image object to apply the blur to. + * @param {number} imageBrightness - The calculated total image brightness. + * @returns {GdiBitmap} The blurred image. + * @private + */ + _blurStyleBlendImage(image, imageBrightness) { + const blurLevels = { + white: 100, + black: 150, + reborn: imageBrightness > 125 ? 250 : + imageBrightness > 100 ? 220 : + imageBrightness > 75 ? 200 : + imageBrightness > 50 ? 220 : 200, + random: imageBrightness > 125 ? 250 : + imageBrightness > 100 ? 220 : + imageBrightness > 75 ? 200 : + imageBrightness > 50 ? 220 : 200, + default: 250 + }; + + const blurLevel = blurLevels[grSet.theme] || blurLevels.default; + image.StackBlur(blurLevel); + + if (grCfg.settings.showDebugThemeLog) console.log(`Blended image blur: ${blurLevel}`); + if (grCfg.settings.showDebugThemeOverlay) grm.ui.blendedImgBlur = blurLevel; + + return image; + } + + /** + * Formats the image with the correct alpha based on the current theme, and applies blur. + * @param {GdiBitmap} image - The image object to be formatted. + * @param {number} imageW - The width of the image. + * @param {number} imageH - The height of the image. + * @param {number} imageBrightness - The calculated total image brightness. + * @returns {GdiBitmap} The formatted image with applied alpha and blur. + * @private + */ + _formatStyleBlendImage(image, imageW, imageH, imageBrightness) { + if (!image || !imageW || !imageH) return image; + + const alphaValues = { + white: 70, + black: 50, + reborn: + grSet.styleRebornWhite ? + imageBrightness < 100 ? 70 : + imageBrightness < 75 ? 50 : + imageBrightness < 50 ? 30 : + imageBrightness < 25 ? 15 : 80 : // Default 80 + grSet.styleRebornBlack ? + imageBrightness > 240 ? 30 : + imageBrightness > 175 ? 100 : 80 : // Default 80 + // Standard Reborn + imageBrightness > 200 ? 150 : + imageBrightness > 150 ? 130 : + imageBrightness > 125 ? 120 : + imageBrightness > 100 ? 110 : + imageBrightness > 75 ? 100 : + imageBrightness > 50 ? 90 : 80, // Default 80 + blue: 80, + darkblue: 70, + red: 50, + cream: 70, + nblue: 50, + ngreen: 50, + nred: 50, + ngold: 50, + default: 70 + }; + + const alpha = alphaValues[grSet.theme] || alphaValues.default; + + let tempImg = gdi.CreateImage(imageW, imageH); + const g = tempImg.GetGraphics(); + + try { // * Prevent crash if album art is corrupt, file format is not supported or has a unusual ICC profile embedded + g.DrawImage(image, 0, 0, grm.ui.ww, grm.ui.wh, 0, 0, image.Width, image.Height, 0, alpha); + } catch (e) { + console.log('\n\n'); + } + + tempImg.ReleaseGraphics(g); + tempImg = this._blurStyleBlendImage(tempImg, grCol.imgBrightness); + + if (grCfg.settings.showDebugThemeLog) console.log(`Blended image alpha: ${alpha}\nTheme brightness: ${grSet.themeBrightness}`); + if (grCfg.settings.showDebugThemeOverlay) grm.ui.blendedImgAlpha = alpha; + + return tempImg; + } + + /** + * Main method to set the style Blend 1 & Blend 2 for the album art based on the current theme. + */ + setStyleBlend() { + const setStyleBlendProfiler = grm.ui.showDebugTiming && fb.CreateProfiler('setStyleBlend'); + + grCol.imgBlended = this._formatStyleBlendImage(grm.ui.albumArt, grm.ui.ww, grm.ui.wh, grCol.imgBrightness); + + if (setStyleBlendProfiler) setStyleBlendProfiler.Print(); + } + + /** + * Sets calculated image brightness from album art, mainly used when using style Blend 1 and 2 or style Black and white reborn. + */ + setImageBrightness() { + if (grm.ui.albumArt && (libSet.theme !== 0 || grSet.styleBlend || grSet.styleBlend2 || grSet.styleBlackAndWhite || grSet.styleBlackAndWhite2 || grSet.styleBlackAndWhiteReborn)) { + grCol.imgBrightness = CalcImgBrightness(grm.ui.albumArt); + } + } + + /** + * Sets noAlbumArtColors, change col.primary when streaming, reset to default when playing from CD or using noAlbumArtStub. + */ + setNoAlbumArtColors() { + if (grm.ui.isStreaming && (['white', 'black', 'reborn', 'random'].includes(grSet.theme))) { + grCol.primary = RGB(207, 0, 5); + } + if (grm.ui.isPlayingCD || grm.ui.noAlbumArtStub) { + if (grSet.theme.startsWith('custom')) grm.ui.initCustomTheme(); + if (!grm.ui.isStreaming) grm.color.setThemeColors(); + bio.ui.updateProp(1); // Needed to update color for NO PHOTO/COVER stub in Biography when changing themes + } + } + + /** + * Sets primary and optional secondary theme color as well as accents. + * @param {number} color - The primary color. + * @param {number} color2 - The secondary color. + */ + setTheme(color, color2) { + if (color2 === undefined) color2 = color; + let themeCol = new Color(color.primary); + const customThemes = grSet.theme.startsWith('custom'); + + if (ColorDistance(color.primary, grCol.bg, true) < (themeCol.isCloseToGrayscale ? 60 : 45) && + (grSet.theme !== 'reborn' && grSet.theme !== 'random' && (grSet.theme !== 'black' && !grSet.styleBlackReborn) && !customThemes)) { + if (grCfg.settings.showDebugThemeLog) console.log('>>> Theme primary color is too close to bg color. Tinting theme color.'); + color.primary = TintColor(color.primary, 15); + color.accent = TintColor(color.primary, 10); + themeCol = new Color(color.primary); + } + grCol.primary = color.primary; + grCol.primary_alt = color2.primary_alt; + + if (ColorDistance(color.primary, grCol.progressBar, true) < (themeCol.isCloseToGrayscale ? 60 : 45)) { + // Progress bar fill is too close in color to bg + if (grCfg.settings.showDebugThemeLog) console.log('>>> Theme primary color is too close to progress bar. Adjusting progressBar'); + if (grSet.theme === 'white' && themeCol.brightness < 125) { + grCol.progressBar = RGB(180, 180, 180); + } + } + + if (grm.timeline) { + grm.timeline.setColors(grCol.timelineAdded, grCol.timelinePlayed, grCol.timelineUnplayed); + } + + grCol.primary = color.primary; + grCol.primary_alt = color2.primary_alt; + + // * Reborn/Random theme main tone palette + grCol.darkAccent_100 = ShadeColor(color.primary, 100); + grCol.darkAccent_100_alt = ShadeColor(color2.primary_alt, 100); + grCol.darkAccent_75 = ShadeColor(color.primary, 75); + grCol.darkAccent_75_alt = ShadeColor(color2.primary_alt, 75); + grCol.darkAccent_65 = ShadeColor(color.primary, 65); + grCol.darkAccent_65_alt = ShadeColor(color2.primary_alt, 65); + grCol.darkAccent_50 = ShadeColor(color.primary, 50); + grCol.darkAccent_50_alt = ShadeColor(color2.primary_alt, 50); + grCol.darkAccent = color.darkAccent; + grCol.darkAccent_alt = color2.darkAccent_alt; + grCol.accent = color.accent; + grCol.accent_alt = color2.accent_alt; + + grCol.lightAccent_2 = TintColor(color.primary, 2); + grCol.lightAccent_2_alt = TintColor(color2.primary_alt, 2); + grCol.lightAccent_7 = TintColor(color.primary, 7); + grCol.lightAccent_7_alt = TintColor(color2.primary_alt, 7); + grCol.lightAccent_10 = TintColor(color.primary, 10); + grCol.lightAccent_10_alt = TintColor(color2.primary_alt, 10); + + grCol.lightAccent = color.lightAccent; + grCol.lightAccent_alt = color2.lightAccent_alt; + grCol.lightAccent_35 = TintColor(color.primary, 35); + grCol.lightAccent_35_alt = TintColor(color2.primary_alt, 35); + grCol.lightAccent_50 = TintColor(color.primary, 50); + grCol.lightAccent_50_alt = TintColor(color2.primary_alt, 50); + grCol.lightAccent_65 = TintColor(color.primary, 65); + grCol.lightAccent_65_alt = TintColor(color2.primary_alt, 65); + grCol.lightAccent_80 = TintColor(color.primary, 80); + grCol.lightAccent_80_alt = TintColor(color2.primary_alt, 80); + grCol.lightAccent_100 = TintColor(color.primary, 100); + grCol.lightAccent_100_alt = TintColor(color2.primary_alt, 100); + + // * Change col.primary if too bright or too dark + if (grSet.theme === 'white' && (ColorDistance(grCol.primary, grCol.progressBar)) < 60) { + grCol.primary = grCol.darkAccent; + } + } + + /** + * Sets default theme colors, used on startup when nothing has been played or using noAlbumArtStub. + */ + setThemeColors() { + const themeColors = { + white: grm.theme.whiteTheme.colors, + black: grm.theme.blackTheme.colors, + reborn: grm.theme.rebornTheme.colors, + random: grm.theme.randomTheme.colors, + blue: grm.theme.blueTheme.colors, + darkblue: grm.theme.darkblueTheme.colors, + red: grm.theme.redTheme.colors, + cream: grm.theme.creamTheme.colors, + nblue: grm.theme.nblueTheme.colors, + ngreen: grm.theme.ngreenTheme.colors, + nred: grm.theme.nredTheme.colors, + ngold: grm.theme.ngoldTheme.colors + }; + + if (themeColors[grSet.theme]) { + this.setTheme(themeColors[grSet.theme]); + } else if (grSet.theme.startsWith('custom')) { + this.setTheme(grm.theme.customTheme.colors); + } + } + + /** + * Sets and saves currently used colors, used when transferring colors to a custom theme. + * @param {string} slot - The custom theme slot in which to save. + */ + setCurrentColorsToCustomTheme(slot) { + const currentColors = { + // * PRELOADER COLORS * // + grCol_preloaderBg: RGBFtoHEX(grCol.bg), + grCol_preloaderLogo: '_custom-logo.png', + grCol_preloaderLowerBarTitle: RGBFtoHEX(grCol.lowerBarTitle), + grCol_preloaderProgressBar: RGBFtoHEX(grCol.progressBar), + grCol_preloaderProgressBarFill: RGBFtoHEX(grCol.progressBarFill), + grCol_preloaderProgressBarFrame: RGBFtoHEX(grCol.progressBarFrame), + grCol_preloaderUIHacksFrame: RGBFtoHEX(grCol.bg), + // * PLAYLIST COLORS * // + pl_col_bg: RGBFtoHEX(pl.col.bg), + pl_col_plman_text_normal: RGBFtoHEX(pl.col.plman_text_normal), + pl_col_plman_text_hovered: RGBFtoHEX(pl.col.plman_text_hovered), + pl_col_plman_text_pressed: RGBFtoHEX(pl.col.plman_text_pressed), + pl_col_header_nowplaying_bg: RGBFtoHEX(pl.col.header_nowplaying_bg), + pl_col_header_sideMarker: RGBFtoHEX(pl.col.header_sideMarker), + pl_col_header_artist_normal: RGBFtoHEX(pl.col.header_artist_normal), + pl_col_header_artist_playing: RGBFtoHEX(pl.col.header_artist_playing), + pl_col_header_album_normal: RGBFtoHEX(pl.col.header_album_normal), + pl_col_header_album_playing: RGBFtoHEX(pl.col.header_album_playing), + pl_col_header_info_normal: RGBFtoHEX(pl.col.header_info_normal), + pl_col_header_info_playing: RGBFtoHEX(pl.col.header_info_playing), + pl_col_header_date_normal: RGBFtoHEX(pl.col.header_date_normal), + pl_col_header_date_playing: RGBFtoHEX(pl.col.header_date_playing), + pl_col_header_line_normal: RGBFtoHEX(pl.col.header_line_normal), + pl_col_header_line_playing: RGBFtoHEX(pl.col.header_line_playing), + pl_col_row_nowplaying_bg: RGBFtoHEX(pl.col.row_nowplaying_bg), + pl_col_row_stripes_bg: RGBFtoHEX(pl.col.row_stripes_bg), + pl_col_row_selection_frame: RGBFtoHEX(pl.col.row_selection_frame), + pl_col_row_sideMarker: RGBFtoHEX(pl.col.row_sideMarker), + pl_col_row_title_normal: RGBFtoHEX(pl.col.row_title_normal), + pl_col_row_title_playing: RGBFtoHEX(pl.col.row_title_playing), + pl_col_row_title_selected: RGBFtoHEX(pl.col.row_title_selected), + pl_col_row_title_hovered: RGBFtoHEX(pl.col.row_title_hovered), + pl_col_row_rating_color: RGBFtoHEX(pl.col.row_rating_color), + pl_col_row_disc_subheader_line: RGBFtoHEX(pl.col.row_disc_subheader_line), + pl_col_row_drag_line: RGBFtoHEX(pl.col.row_drag_line), + pl_col_row_drag_line_reached: RGBFtoHEX(pl.col.row_drag_line_reached), + pl_col_sbar_btn_normal: RGBFtoHEX(pl.col.sbar_btn_normal), + pl_col_sbar_btn_hovered: RGBFtoHEX(pl.col.sbar_btn_hovered), + pl_col_sbar_thumb_normal: RGBFtoHEX(pl.col.sbar_thumb_normal), + pl_col_sbar_thumb_hovered: RGBFtoHEX(pl.col.sbar_thumb_hovered), + pl_col_sbar_thumb_drag: RGBFtoHEX(pl.col.sbar_thumb_drag), + + // * LIBRARY COLORS * // + lib_ui_col_bg: RGBFtoHEX(lib.ui.col.bg), + lib_ui_col_rowStripes: RGBFtoHEX(lib.ui.col.rowStripes), + lib_ui_col_nowPlayingBg: RGBFtoHEX(lib.ui.col.nowPlayingBg), + lib_ui_col_sideMarker: RGBFtoHEX(lib.ui.col.sideMarker), + lib_ui_col_selectionFrame: RGBFtoHEX(lib.ui.col.selectionFrame), + lib_ui_col_selectionFrame2: RGBFtoHEX(lib.ui.col.selectionFrame2), + lib_ui_col_hoverFrame: RGBFtoHEX(lib.ui.col.hoverFrame), + lib_ui_col_iconPlus: RGBFtoHEX(lib.ui.col.iconPlus), + lib_ui_col_iconPlus_h: RGBFtoHEX(lib.ui.col.iconPlus_h), + lib_ui_col_iconPlus_sel: RGBFtoHEX(lib.ui.col.iconPlus_sel), + lib_ui_col_iconPlusBg: RGBFtoHEX(lib.ui.col.iconPlusBg), + lib_ui_col_iconMinus_e: RGBFtoHEX(lib.ui.col.iconMinus_e), + lib_ui_col_iconMinus_c: RGBFtoHEX(lib.ui.col.iconMinus_c), + lib_ui_col_iconMinus_h: RGBFtoHEX(lib.ui.col.iconMinus_h), + lib_ui_col_text: RGBFtoHEX(lib.ui.col.text), + lib_ui_col_text_h: RGBFtoHEX(lib.ui.col.text_h), + lib_ui_col_text_nowp: RGBFtoHEX(lib.ui.col.text_nowp), + lib_ui_col_textSel: RGBFtoHEX(lib.ui.col.textSel), + lib_ui_col_txt: RGBFtoHEX(lib.ui.col.txt), + lib_ui_col_txt_h: RGBFtoHEX(lib.ui.col.txt_h), + lib_ui_col_txt_box: RGBFtoHEX(lib.ui.col.txt_box), + lib_ui_col_search: RGBFtoHEX(lib.ui.col.search), + lib_ui_col_searchBtn: RGBFtoHEX(lib.ui.col.searchBtn), + lib_ui_col_crossBtn: RGBFtoHEX(lib.ui.col.crossBtn), + lib_ui_col_filterBtn: RGBFtoHEX(lib.ui.col.filterBtn), + lib_ui_col_settingsBtn: RGBFtoHEX(lib.ui.col.settingsBtn), + lib_ui_col_line: RGBFtoHEX(lib.ui.col.line), + lib_ui_col_s_line: RGBFtoHEX(lib.ui.col.s_line), + lib_ui_col_sbarBtns: RGBFtoHEX(lib.ui.col.sbarBtns), + lib_ui_col_sbarNormal: RGBFtoHEX(lib.ui.col.sbarNormal), + lib_ui_col_sbarHovered: RGBFtoHEX(lib.ui.col.sbarHovered), + lib_ui_col_sbarDrag: RGBFtoHEX(lib.ui.col.sbarDrag), + + // * BIOGRAPHY COLORS * // + bio_ui_col_bg: RGBFtoHEX(bio.ui.col.bg), + bio_ui_col_rowStripes: RGBFtoHEX(bio.ui.col.rowStripes), + bio_ui_col_headingText: RGBFtoHEX(bio.ui.col.headingText), + bio_ui_col_bottomLine: RGBFtoHEX(bio.ui.col.bottomLine), + bio_ui_col_centerLine: RGBFtoHEX(bio.ui.col.centerLine), + bio_ui_col_sectionLine: RGBFtoHEX(bio.ui.col.sectionLine), + bio_ui_col_accent: RGBFtoHEX(bio.ui.col.accent), + bio_ui_col_source: RGBFtoHEX(bio.ui.col.source), + bio_ui_col_summary: RGBFtoHEX(bio.ui.col.summary), + bio_ui_col_text: RGBFtoHEX(bio.ui.col.text), + bio_ui_col_lyricsNormal: RGBFtoHEX(bio.ui.col.lyricsNormal), + bio_ui_col_lyricsHighlight: RGBFtoHEX(bio.ui.col.lyricsHighlight), + bio_ui_col_noPhotoStubBg: RGBFtoHEX(bio.ui.col.noPhotoStubBg), + bio_ui_col_noPhotoStubText: RGBFtoHEX(bio.ui.col.noPhotoStubText), + bio_ui_col_sbarBtns: RGBFtoHEX(bio.ui.col.sbarBtns), + bio_ui_col_sbarNormal: RGBFtoHEX(bio.ui.col.sbarNormal), + bio_ui_col_sbarHovered: RGBFtoHEX(bio.ui.col.sbarHovered), + bio_ui_col_sbarDrag: RGBFtoHEX(bio.ui.col.sbarDrag), + + // * MAIN COLORS * // + grCol_bg: RGBFtoHEX(grCol.bg), + grCol_shadow: RGBFtoHEX(grCol.shadow), + grCol_discArtShadow: RGBFtoHEX(grCol.discArtShadow), + grCol_noAlbumArtStub: RGBFtoHEX(grCol.noAlbumArtStub), + grCol_lowerBarArtist: RGBFtoHEX(grCol.lowerBarArtist), + grCol_lowerBarTitle: RGBFtoHEX(grCol.lowerBarTitle), + grCol_lowerBarTime: RGBFtoHEX(grCol.lowerBarTime), + grCol_lowerBarLength: RGBFtoHEX(grCol.lowerBarLength), + grCol_lyricsNormal: RGBFtoHEX(grCol.lyricsNormal), + grCol_lyricsHighlight: RGBFtoHEX(grCol.lyricsHighlight), + grCol_lyricsShadow: RGBFtoHEX(grCol.lyricsShadow), + grCol_detailsBg: RGBFtoHEX(grCol.detailsBg), + grCol_detailsText: RGBFtoHEX(grCol.detailsText), + grCol_detailsRating: RGBFtoHEX(grCol.detailsRating), + grCol_timelineAdded: RGBFtoHEX(grCol.timelineAdded), + grCol_timelinePlayed: RGBFtoHEX(grCol.timelinePlayed), + grCol_timelineUnplayed: RGBFtoHEX(grCol.timelineUnplayed), + grCol_timelineFrame: RGBFtoHEX(grCol.timelineFrame), + grCol_popupBg: RGBFtoHEX(grCol.popupBg), + grCol_popupText: RGBFtoHEX(grCol.popupText), + grCol_menuBgColor: RGBFtoHEX(grCol.menuBgColor), + grCol_menuStyleBg: RGBFtoHEX(grCol.menuStyleBg), + grCol_menuRectStyleEmbossTop: RGBFtoHEX(grCol.menuRectStyleEmbossTop), + grCol_menuRectStyleEmbossBottom: RGBFtoHEX(grCol.menuRectStyleEmbossBottom), + grCol_menuRectNormal: RGBFtoHEX(grCol.menuRectNormal), + grCol_menuRectHovered: RGBFtoHEX(grCol.menuRectHovered), + grCol_menuRectDown: RGBFtoHEX(grCol.menuRectDown), + grCol_menuTextNormal: RGBFtoHEX(grCol.menuTextNormal), + grCol_menuTextHovered: RGBFtoHEX(grCol.menuTextHovered), + grCol_menuTextDown: RGBFtoHEX(grCol.menuTextDown), + grCol_transportEllipseBg: RGBFtoHEX(grCol.transportEllipseBg), + grCol_transportEllipseNormal: RGBFtoHEX(grCol.transportEllipseNormal), + grCol_transportEllipseHovered: RGBFtoHEX(grCol.transportEllipseHovered), + grCol_transportEllipseDown: RGBFtoHEX(grCol.transportEllipseDown), + grCol_transportStyleBg: RGBFtoHEX(grCol.transportStyleBg), + grCol_transportStyleTop: RGBFtoHEX(grCol.transportStyleTop), + grCol_transportStyleBottom: RGBFtoHEX(grCol.transportStyleBottom), + grCol_transportIconNormal: RGBFtoHEX(grCol.transportIconNormal), + grCol_transportIconHovered: RGBFtoHEX(grCol.transportIconHovered), + grCol_transportIconDown: RGBFtoHEX(grCol.transportIconDown), + grCol_progressBar: RGBFtoHEX(grCol.progressBar), + grCol_progressBarStreaming: RGBFtoHEX(grCol.progressBarStreaming), + grCol_progressBarFrame: RGBFtoHEX(grCol.progressBarFrame), + grCol_progressBarFill: RGBFtoHEX(grCol.progressBarFill), + grCol_peakmeterBarProg: RGBFtoHEX(grCol.peakmeterBarProg), + grCol_peakmeterBarProgFill: RGBFtoHEX(grCol.peakmeterBarProgFill), + grCol_peakmeterBarFillTop: RGBFtoHEX(grCol.peakmeterBarFillTop), + grCol_peakmeterBarFillMiddle: RGBFtoHEX(grCol.peakmeterBarFillMiddle), + grCol_peakmeterBarFillBack: RGBFtoHEX(grCol.peakmeterBarFillBack), + grCol_peakmeterBarVertProgFill: RGBFtoHEX(grCol.peakmeterBarVertProgFill), + grCol_peakmeterBarVertFill: RGBFtoHEX(grCol.peakmeterBarVertFill), + grCol_peakmeterBarVertFillPeaks: RGBFtoHEX(grCol.peakmeterBarVertFillPeaks), + grCol_waveformBarFillFront: RGBFtoHEX(grCol.waveformBarFillFront), + grCol_waveformBarFillBack: RGBFtoHEX(grCol.waveformBarFillBack), + grCol_waveformBarFillPreFront: RGBFtoHEX(grCol.waveformBarFillPreFront), + grCol_waveformBarFillPreBack: RGBFtoHEX(grCol.waveformBarFillPreBack), + grCol_waveformBarIndicator: RGBFtoHEX(grCol.waveformBarIndicator), + grCol_volumeBar: RGBFtoHEX(grCol.volumeBar), + grCol_volumeBarFrame: RGBFtoHEX(grCol.volumeBarFrame), + grCol_volumeBarFill: RGBFtoHEX(grCol.volumeBarFill), + grCol_styleBevel: RGBFtoHEX(grCol.styleBevel), + grCol_styleGradient: RGBFtoHEX(grCol.styleGradient), + grCol_styleGradient2: RGBFtoHEX(grCol.styleGradient2), + grCol_styleProgressBar: RGBFtoHEX(grCol.styleProgressBar), + grCol_styleProgressBarLineTop: RGBFtoHEX(grCol.styleProgressBarLineTop), + grCol_styleProgressBarLineBottom: RGBFtoHEX(grCol.styleProgressBarLineBottom), + grCol_styleProgressBarFill: RGBFtoHEX(grCol.styleProgressBarFill), + grCol_styleVolumeBar: RGBFtoHEX(grCol.styleVolumeBar), + grCol_styleVolumeBarFill: RGBFtoHEX(grCol.styleVolumeBarFill) + } + + const customThemes = { + custom01: { schema: grDef.customTheme01Schema, object: 'customTheme01' }, + custom02: { schema: grDef.customTheme02Schema, object: 'customTheme02' }, + custom03: { schema: grDef.customTheme03Schema, object: 'customTheme03' }, + custom04: { schema: grDef.customTheme04Schema, object: 'customTheme04' }, + custom05: { schema: grDef.customTheme05Schema, object: 'customTheme05' }, + custom06: { schema: grDef.customTheme06Schema, object: 'customTheme06' }, + custom07: { schema: grDef.customTheme07Schema, object: 'customTheme07' }, + custom08: { schema: grDef.customTheme08Schema, object: 'customTheme08' }, + custom09: { schema: grDef.customTheme09Schema, object: 'customTheme09' }, + custom10: { schema: grDef.customTheme10Schema, object: 'customTheme10' } + }; + + if (!customThemes[slot]) return; + + const customTheme = customThemes[slot].object; + const { schema } = customThemes[slot]; + this[customTheme] = grCfg.configCustom.addConfigurationObject(schema, currentColors, grDef.customThemeComments); + grCfg.cTheme = this[customTheme]; + grCfg.configCustom.updateConfigObjValues(customTheme, currentColors, true); + } + // #endregion + + // * PUBLIC METHODS - RANDOM COLOR * // + // #region PUBLIC METHODS - RANDOM COLOR + /** + * Generates a random theme color, used in Random theme. + */ + getRandomThemeColor() { + if (!grm.ui.getRandomThemeColorContextMenu && ($('[%GR_THEMECOLOR%]') || $('[%GR_THEMECOLOR2%]'))) return; + + const generateRandomColor = () => { + const R = Math.floor((Math.random() * (grSet.styleRandomPastel ? 127 : 27)) + (grSet.styleRandomPastel ? 127 : 27)); + const G = Math.floor((Math.random() * (grSet.styleRandomPastel ? 127 : 27)) + (grSet.styleRandomPastel ? 127 : 27)); + const B = Math.floor((Math.random() * (grSet.styleRandomPastel ? 127 : 27)) + (grSet.styleRandomPastel ? 127 : 27)); + return grSet.styleRandomPastel || grSet.styleRandomDark ? (R << 16) + (G << 8) + B : ((1 << 24) * Math.random() | 0); + }; + + const color = new Color(generateRandomColor()); + const tObj = this.createThemeColorObject(color); + grm.color.setTheme(tObj); + + if (grCfg.settings.showDebugThemeLog) { + console.log('Random generated color:', color.getRGB(true)); + console.log('Random color brightness:', color.brightness); + } + if (grCfg.settings.showDebugThemeOverlay) grm.ui.selectedPrimaryColor = color.getRGB(true); + } + + /** + * Auto generates new colors depending on time interval, used in style Random theme auto color. + */ + getRandomThemeAutoColor() { + clearInterval(grm.ui.randomThemeAutoColorTimer); + grm.ui.randomThemeAutoColorTimer = null; + + if (grSet.styleRandomAutoColor !== 'off' && grSet.styleRandomAutoColor !== 'track') { + grm.ui.randomThemeAutoColorTimer = setInterval(() => { + grm.ui.initTheme(); + }, grSet.styleRandomAutoColor); + } + else if (grSet.styleRandomAutoColor === 'track') { + grm.ui.initTheme(); + } + DebugLog('\n>>> initTheme -> getRandomThemeAutoColor <<<\n'); + } + // #endregion + + // * PUBLIC METHODS - ALBUM ART COLOR * // + // #region PUBLIC METHODS - ALBUM ART COLOR + /** + * Finds the brightest color based on given parameters. + * @param {Array} colorsWeighted - Array of color objects with added weight properties. + * @param {number} currentBrightness - The brightness level that colors must exceed. + * @param {number} maxBrightness - The maximum brightness level that colors must not exceed. + * @param {number} minFrequency - The minimum frequency threshold that colors must exceed. + * @returns {Color|null} The brightest Color object or null if no color meets the criteria. + */ + findBrightestColor(colorsWeighted, currentBrightness, maxBrightness, minFrequency) { + let brightestColor; + let maxWeight = 0; + + for (const c of colorsWeighted) { + if (!c.col.isCloseToGrayscale && + (c.col.brightness > currentBrightness) && + (c.col.brightness < maxBrightness) && + (c.freq > minFrequency) && + (c.weight > maxWeight)) { + maxWeight = c.weight; + brightestColor = c.col; + } + } + + return brightestColor ? new Color(brightestColor.val) : null; + } + + /** + * Extracts the primary and secondary optional color from an image. + * @param {GdiBitmap} image - The image to extract the colors from. + * @param {number} maxColorsToPull - The max number of colors in the palette. + * @param {number} [secondaryColor] - The secondary picked color, used in Reborn fusion. + * @returns {number|null} The primary color value if secondaryColor is not provided, otherwise the secondary color value. + * Returns null on error. + */ + getThemeColorsJson(image, maxColorsToPull, secondaryColor) { + const debugThemeLog = grCfg.settings.showDebugThemeLog; + const debugThemeOverlay = grCfg.settings.showDebugThemeOverlay; + const minFreq = 0.015; + const minFreqBrightestCol1 = 0.01; + const minFreqBrightestCol2 = 0.05; + const maxBrightness = grSet.theme === 'black' || grSet.styleBlend || ['reborn', 'random'].includes(grSet.theme) && grSet.styleBlend2 ? 255 : 212; + const midBrightness2Value = RandomMinMax(60, 120); + + try { + const colorsWeighted = JSON.parse(image.GetColourSchemeJSON(maxColorsToPull)).map(c => ({ ...c, col: new Color(c.col) })); + let maxWeight = 0; + let maxWeight2 = 0; + let selectedColor = new Color(colorsWeighted[0].col); // Use first color in case no color selected below + let selectedColor2 = secondaryColor ? new Color(colorsWeighted[1].col) : null; // Use second color in case no color selected below + + if (debugThemeLog) console.log('idx color bright freq weight'); + + for (const [i, c] of colorsWeighted.entries()) { + const col = c.col; + const midBrightness = 127 - Math.abs(127 - col.brightness); // Favors colors with brightness around 127 + const midBrightness2 = midBrightness2Value - Math.abs(midBrightness2Value - col.brightness); // Favors colors with random brightness from 60 - 120 + c.weight = c.freq * midBrightness * 10; + c.weight2 = c.freq * midBrightness2 * 10; + + if (c.freq >= minFreq && !col.isCloseToGrayscale && col.brightness < maxBrightness) { + if (debugThemeLog) { + console.log(LeftPad(i, 2), col.getRGB(true, true), LeftPad(col.brightness, 4), ' ', `${LeftPad((c.freq * 100).toFixed(2), 5)}%`, LeftPad(c.weight.toFixed(2), 7)); + } + if (c.weight > maxWeight) { + maxWeight = c.weight; + selectedColor = col; + } + if (secondaryColor && c.weight2 > maxWeight2) { + maxWeight2 = c.weight2; + selectedColor2 = col; + } + } + else if (debugThemeLog) { + console.log(' -', col.getRGB(true, true), LeftPad(col.brightness, 4), ' ', `${LeftPad((c.freq * 100).toFixed(2), 5)}%`, col.isCloseToGrayscale ? ' grey' : (c.freq < minFreq) ? ' freq' : ' bright'); + } + } + + if (selectedColor.brightness < 37) { + selectedColor = this.findBrightestColor(colorsWeighted, selectedColor.brightness, maxBrightness, minFreqBrightestCol1) || selectedColor; + if (debugThemeLog) console.log(selectedColor.getRGB(true), 'brightness:', selectedColor.brightness, 'too dark -- searching for highlight color'); + } + if (secondaryColor && selectedColor2.brightness < 37) { + selectedColor2 = this.findBrightestColor(colorsWeighted, selectedColor2.brightness, maxBrightness, minFreqBrightestCol2) || selectedColor2; + if (debugThemeLog) console.log(selectedColor2.getRGB(true), 'brightness:', selectedColor2.brightness, 'too dark -- searching for highlight color'); + } + + if (debugThemeLog) { + console.log('Primary color:', selectedColor.getRGB(true)); + if (secondaryColor) console.log('Primary color 2:', selectedColor2.getRGB(true)); + } + if (debugThemeOverlay) { + grm.ui.selectedPrimaryColor = selectedColor.getRGB(true); + if (secondaryColor) grm.ui.selectedPrimaryColor2 = selectedColor2.getRGB(true); + } + + return secondaryColor ? selectedColor2.val : selectedColor.val; + } + catch (e) { + console.log('\n\n'); + return null; + } + } + + /** + * Sets the primary or secondary color from the value of getThemeColorsJson or from the custom GR-tag. + * @param {GdiBitmap} image - The image from which the colors will be picked. + */ + getThemeColors(image) { + const debugThemeLog = grCfg.settings.showDebugThemeLog; + const rebornFusion = grSet.theme === 'reborn' && (grSet.styleRebornFusion || grSet.styleRebornFusion2 || grSet.styleRebornFusionAccent); + const val = $('[%GR_THEMECOLOR%]'); + const val2 = $('[%GR_THEMECOLOR2%]'); + let color; + let color2; + let calculatedColor; + let calculatedColor2; + + if (val.length) { + calculatedColor = ColStringToRGB(val); + calculatedColor2 = ColStringToRGB(val2); + } else { + calculatedColor = this.getThemeColorsJson(image, 14, false); + calculatedColor2 = rebornFusion ? this.getThemeColorsJson(image, 14, true) : undefined; + } + + if (isNaN(calculatedColor)) return; + + color = new Color(calculatedColor); + if (rebornFusion) color2 = new Color(calculatedColor2); + + if (grSet.theme !== 'black') { + const shadeAmount = grSet.theme === 'white' ? 12 : 3; + while (color.brightness > 220) { + calculatedColor = ShadeColor(calculatedColor, shadeAmount); + color = new Color(calculatedColor); + if (debugThemeLog) console.log(' >> Shading: ', ColToRgb(calculatedColor), ' - brightness: ', color.brightness); + } + } + + if (!color.isGrayscale) { + while (color.brightness <= 17) { + calculatedColor = TintColor(calculatedColor, 3); + color = new Color(calculatedColor); + if (debugThemeLog) console.log(' >> Tinting: ', ColToRgb(calculatedColor), ' - brightness: ', color.brightness); + } + } + + const tObj = this.createThemeColorObject(color); + if (rebornFusion) { + const tObj2 = this.createThemeColorObject(color, color2); + grm.color.setTheme(tObj, tObj2); + } else { + grm.color.setTheme(tObj); + } + + if (debugThemeLog) { + console.log('Primary color brightness:', color.brightness); + if (color2) console.log('Primary color 2 brightness:', color2.brightness); + } + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Base/gr-theme-presets.js b/profile/georgia-reborn/scripts/Base/gr-theme-presets.js index 23341ce5..2be7f0b2 100644 --- a/profile/georgia-reborn/scripts/Base/gr-theme-presets.js +++ b/profile/georgia-reborn/scripts/Base/gr-theme-presets.js @@ -1,13 +1,13 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Theme Presets * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-11 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Theme Presets * // +// * Author: TT * // +// * Org. Author: Mordred * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; @@ -17,2535 +17,2513 @@ // * THEME PRESETS * // /////////////////////// /** - * Contains all theme preset functions that configure various theme and style settings. + * A class that contains all theme presets and methods to apply various theme and style settings. * These theme presets can be accessed through the Options -> Preset menu. */ -const presets = { - ///////////////////////////// +class ThemePreset { // * WHITE THEME PRESETS * // - ///////////////////////////// + // #region WHITE THEME PRESETS /** * White theme preset -> Options > Preset > White > Beveled. * @returns {void} */ - whiteP01: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBarFill = 'bevel'; - }, + whiteP01() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * White theme preset -> Options > Preset > White > Black and white. * @returns {void} */ - whiteP02: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleBlackAndWhite = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'default'; - pref.styleVolumeBar = 'inner'; - }, + whiteP02() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleBlackAndWhite = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'default'; + grSet.styleVolumeBar = 'inner'; + } /** * White theme preset -> Options > Preset > White > Black and white blended. * @returns {void} */ - whiteP03: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleBlackAndWhite = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + whiteP03() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleBlackAndWhite = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * White theme preset -> Options > Preset > White > Black and white 2. * @returns {void} */ - whiteP04: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleBlackAndWhite2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + whiteP04() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleBlackAndWhite2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * White theme preset -> Options > Preset > White > Black and white 2 blended. * @returns {void} */ - whiteP05: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleBlackAndWhite2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + whiteP05() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleBlackAndWhite2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * White theme preset -> Options > Preset > White > Black and white reborn. * @returns {void} */ - whiteP06: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleBlackAndWhiteReborn = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + whiteP06() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleBlackAndWhiteReborn = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * White theme preset -> Options > Preset > White > Black and white reborn blended. * @returns {void} */ - whiteP07: () => { - pref.theme = 'white'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleBlackAndWhiteReborn = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + whiteP07() { + grSet.theme = 'white'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleBlackAndWhiteReborn = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * White theme preset -> Options > Preset > White > Minimalized. * @returns {void} */ - whiteP08: () => { - pref.theme = 'white'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + whiteP08() { + grSet.theme = 'white'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ///////////////////////////// // * BLACK THEME PRESETS * // - ///////////////////////////// + // #region BLACK THEME PRESETS /** * Black theme preset -> Options > Preset > Black > Beveled. * @returns {void} */ - blackP01: () => { - pref.theme = 'black'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + blackP01() { + grSet.theme = 'black'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Black theme preset -> Options > Preset > Black > Blended. * @returns {void} */ - blackP02: () => { - pref.theme = 'black'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + blackP02() { + grSet.theme = 'black'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Black theme preset -> Options > Preset > Black > Blended alternative. * @returns {void} */ - blackP03: () => { - pref.theme = 'black'; - pref.styleBlend = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + blackP03() { + grSet.theme = 'black'; + grSet.styleBlend = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Black theme preset -> Options > Preset > Black > Blended alternative 2. * @returns {void} */ - blackP04: () => { - pref.theme = 'black'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + blackP04() { + grSet.theme = 'black'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Black theme preset -> Options > Preset > Black > Black reborn. * @returns {void} */ - blackP05: () => { - pref.theme = 'black'; - pref.styleBevel = true; - pref.styleBlackReborn = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + blackP05() { + grSet.theme = 'black'; + grSet.styleBevel = true; + grSet.styleBlackReborn = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Black theme preset -> Options > Preset > Black > Black reborn blended. * @returns {void} */ - blackP06: () => { - pref.theme = 'black'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleBlackReborn = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + blackP06() { + grSet.theme = 'black'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleBlackReborn = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Black theme preset -> Options > Preset > Black > Dark gray. - * @returns {string|boolean|number} - */ - blackP07: () => { - pref.theme = 'black'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + blackP07() { + grSet.theme = 'black'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + grSet.themeBrightness = 10; + } /** * Black theme preset -> Options > Preset > Black > Dark gray blended. - * @returns {string|boolean|number} - */ - blackP08: () => { - pref.theme = 'black'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + blackP08() { + grSet.theme = 'black'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Black theme preset -> Options > Preset > Black > Dark gray 2 blended. - * @returns {string|boolean|number} - */ - blackP09: () => { - pref.theme = 'black'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + blackP09() { + grSet.theme = 'black'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Black theme preset -> Options > Preset > Black > Minimalized. * @returns {void} */ - blackP10: () => { - pref.theme = 'black'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + blackP10() { + grSet.theme = 'black'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ////////////////////////////// // * REBORN THEME PRESETS * // - ////////////////////////////// + // #region REBORN THEME PRESETS /** * Reborn theme preset -> Options > Preset > Reborn > Beveled. * @returns {void} */ - rebornP01: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP01() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Blended. * @returns {void} */ - rebornP02: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP02() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Blended 2. * @returns {void} */ - rebornP03: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP03() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Gradiented. * @returns {void} */ - rebornP04: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP04() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Gradiented 2. * @returns {void} */ - rebornP05: () => { - pref.theme = 'reborn'; - pref.styleGradient2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP05() { + grSet.theme = 'reborn'; + grSet.styleGradient2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Minimalized. * @returns {void} */ - rebornP06: () => { - pref.theme = 'reborn'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, + rebornP06() { + grSet.theme = 'reborn'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Minimalized blended. * @returns {void} */ - rebornP07: () => { - pref.theme = 'reborn'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, + rebornP07() { + grSet.theme = 'reborn'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn white beveled. * @returns {void} */ - rebornP08: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleRebornWhite = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP08() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleRebornWhite = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn white blended. * @returns {void} */ - rebornP09: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRebornWhite = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP09() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRebornWhite = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn white blended 2. * @returns {void} */ - rebornP10: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleRebornWhite = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP10() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleRebornWhite = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn black beveled. * @returns {void} */ - rebornP11: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleRebornBlack = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP11() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleRebornBlack = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn black blended. * @returns {void} */ - rebornP12: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRebornBlack = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP12() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRebornBlack = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn black blended 2. * @returns {void} */ - rebornP13: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleRebornBlack = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP13() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleRebornBlack = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn black gradiented. * @returns {void} */ - rebornP14: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient = true; - pref.styleRebornBlack = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP14() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient = true; + grSet.styleRebornBlack = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn black gradiented 2. * @returns {void} */ - rebornP15: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient2 = true; - pref.styleRebornBlack = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP15() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient2 = true; + grSet.styleRebornBlack = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion beveled. * @returns {void} */ - rebornP16: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleRebornFusion = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP16() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleRebornFusion = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion blended. * @returns {void} */ - rebornP17: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRebornFusion = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP17() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRebornFusion = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion blended 2. * @returns {void} */ - rebornP18: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleRebornFusion = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP18() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleRebornFusion = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion gradiented. * @returns {void} */ - rebornP19: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient = true; - pref.styleRebornFusion = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP19() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient = true; + grSet.styleRebornFusion = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion gradiented 2. * @returns {void} */ - rebornP20: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient2 = true; - pref.styleRebornFusion = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP20() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient2 = true; + grSet.styleRebornFusion = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion 2 beveled. * @returns {void} */ - rebornP21: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleRebornFusion2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP21() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleRebornFusion2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion 2 blended. * @returns {void} */ - rebornP22: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRebornFusion2 = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP22() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRebornFusion2 = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion 2 blended 2. * @returns {void} */ - rebornP23: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleRebornFusion2 = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP23() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleRebornFusion2 = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion 2 gradiented. * @returns {void} */ - rebornP24: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient = true; - pref.styleRebornFusion2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP24() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient = true; + grSet.styleRebornFusion2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion 2 gradiented 2. * @returns {void} */ - rebornP25: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient2 = true; - pref.styleRebornFusion2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP25() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient2 = true; + grSet.styleRebornFusion2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion accent beveled. * @returns {void} */ - rebornP26: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleRebornFusionAccent = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP26() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleRebornFusionAccent = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion accent blended. * @returns {void} */ - rebornP27: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRebornFusionAccent = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP27() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRebornFusionAccent = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion accent blended 2. * @returns {void} */ - rebornP28: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleRebornFusionAccent = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP28() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleRebornFusionAccent = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion accent gradiented. * @returns {void} */ - rebornP29: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient = true; - pref.styleRebornFusionAccent = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + rebornP29() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient = true; + grSet.styleRebornFusionAccent = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Reborn theme preset -> Options > Preset > Reborn > Reborn fusion accent gradiented 2. * @returns {void} */ - rebornP30: () => { - pref.theme = 'reborn'; - pref.styleBevel = true; - pref.styleGradient2 = true; - pref.styleRebornFusionAccent = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, - + rebornP30() { + grSet.theme = 'reborn'; + grSet.styleBevel = true; + grSet.styleGradient2 = true; + grSet.styleRebornFusionAccent = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } + // #endregion - ////////////////////////////// // * RANDOM THEME PRESETS * // - ////////////////////////////// + // #region RANDOM THEME PRESETS /** * Random theme preset -> Options > Preset > Random > Beveled blended alternative. * @returns {void} */ - randomP01: () => { - pref.theme = 'random'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP01() { + grSet.theme = 'random'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Beveled blended pastel. * @returns {void} */ - randomP02: () => { - pref.theme = 'random'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRandomPastel = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP02() { + grSet.theme = 'random'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRandomPastel = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Beveled blended dark. * @returns {void} */ - randomP03: () => { - pref.theme = 'random'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRandomDark = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP03() { + grSet.theme = 'random'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRandomDark = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Beveled blended auto dark. * @returns {void} */ - randomP04: () => { - pref.theme = 'random'; - pref.styleBevel = true; - pref.styleBlend = true; - pref.styleRandomDark = true; - pref.styleRandomAutoColor = 'track'; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP04() { + grSet.theme = 'random'; + grSet.styleBevel = true; + grSet.styleBlend = true; + grSet.styleRandomDark = true; + grSet.styleRandomAutoColor = 'track'; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Beveled auto dark. * @returns {void} */ - randomP05: () => { - pref.theme = 'random'; - pref.styleBevel = true; - pref.styleRandomDark = true; - pref.styleRandomAutoColor = 'track'; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP05() { + grSet.theme = 'random'; + grSet.styleBevel = true; + grSet.styleRandomDark = true; + grSet.styleRandomAutoColor = 'track'; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Beveled dark. * @returns {void} */ - randomP06: () => { - pref.theme = 'random'; - pref.styleBevel = true; - pref.styleRandomDark = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP06() { + grSet.theme = 'random'; + grSet.styleBevel = true; + grSet.styleRandomDark = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Gradiented. * @returns {void} */ - randomP07: () => { - pref.theme = 'random'; - pref.styleGradient = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP07() { + grSet.theme = 'random'; + grSet.styleGradient = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Gradiented 2. * @returns {void} */ - randomP08: () => { - pref.theme = 'random'; - pref.styleGradient2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + randomP08() { + grSet.theme = 'random'; + grSet.styleGradient2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Random theme preset -> Options > Preset > Random > Minimalized. * @returns {void} */ - randomP09: () => { - pref.theme = 'random'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, + randomP09() { + grSet.theme = 'random'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } /** * Random theme preset -> Options > Preset > Random > Minimalized blended. * @returns {void} */ - randomP10: () => { - pref.theme = 'random'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + randomP10() { + grSet.theme = 'random'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - //////////////////////////// // * BLUE THEME PRESETS * // - //////////////////////////// + // #region BLUE THEME PRESETS /** * Blue theme preset -> Options > Preset > Blue > Beveled. * @returns {void} */ - blueP01: () => { - pref.theme = 'blue'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + blueP01() { + grSet.theme = 'blue'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Blue theme preset -> Options > Preset > Blue > Beveled 2. * @returns {void} */ - blueP02: () => { - pref.theme = 'blue'; - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + blueP02() { + grSet.theme = 'blue'; + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Blue theme preset -> Options > Preset > Blue > Gradiented. * @returns {void} */ - blueP03: () => { - pref.theme = 'blue'; - pref.styleGradient = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + blueP03() { + grSet.theme = 'blue'; + grSet.styleGradient = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Blue theme preset -> Options > Preset > Blue > Gradiented 2. * @returns {void} */ - blueP04: () => { - pref.theme = 'blue'; - pref.styleGradient2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'inner'; - }, + blueP04() { + grSet.theme = 'blue'; + grSet.styleGradient2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Blue theme preset -> Options > Preset > Blue > Minimalized. * @returns {void} */ - blueP05: () => { - pref.theme = 'blue'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, - + blueP05() { + grSet.theme = 'blue'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ///////////////////////////////// // * DARK BLUE THEME PRESETS * // - ///////////////////////////////// + // #region DARK BLUE THEME PRESETS /** * Dark blue theme preset -> Options > Preset > Dark blue > Beveled. * @returns {void} */ - darkblueP01: () => { - pref.theme = 'darkblue'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + darkblueP01() { + grSet.theme = 'darkblue'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Dark blue theme preset -> Options > Preset > Dark blue > Beveled 2. * @returns {void} */ - darkblueP02: () => { - pref.theme = 'darkblue'; - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + darkblueP02() { + grSet.theme = 'darkblue'; + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Dark blue theme preset -> Options > Preset > Dark blue > Gradiented. * @returns {void} */ - darkblueP03: () => { - pref.theme = 'darkblue'; - pref.styleGradient = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + darkblueP03() { + grSet.theme = 'darkblue'; + grSet.styleGradient = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Dark blue theme preset -> Options > Preset > Dark blue > Gradiented 2. * @returns {void} */ - darkblueP04: () => { - pref.theme = 'darkblue'; - pref.styleGradient2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'inner'; - }, + darkblueP04() { + grSet.theme = 'darkblue'; + grSet.styleGradient2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Dark blue theme preset -> Options > Preset > Dark blue > Minimalized. * @returns {void} */ - darkblueP05: () => { - pref.theme = 'darkblue'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, - + darkblueP05() { + grSet.theme = 'darkblue'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - /////////////////////////// // * RED THEME PRESETS * // - /////////////////////////// + // #region RED THEME PRESETS /** * Red theme preset -> Options > Preset > Red > Beveled. * @returns {void} */ - redP01: () => { - pref.theme = 'red'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + redP01() { + grSet.theme = 'red'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Red theme preset -> Options > Preset > Red > Beveled 2. * @returns {void} */ - redP02: () => { - pref.theme = 'red'; - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + redP02() { + grSet.theme = 'red'; + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Red theme preset -> Options > Preset > Red > Gradiented. * @returns {void} */ - redP03: () => { - pref.theme = 'red'; - pref.styleGradient = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + redP03() { + grSet.theme = 'red'; + grSet.styleGradient = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Red theme preset -> Options > Preset > Red > Gradiented 2. * @returns {void} */ - redP04: () => { - pref.theme = 'red'; - pref.styleGradient2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'inner'; - }, + redP04() { + grSet.theme = 'red'; + grSet.styleGradient2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Red theme preset -> Options > Preset > Red > Minimalized. * @returns {void} */ - redP05: () => { - pref.theme = 'red'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, - + redP05() { + grSet.theme = 'red'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ///////////////////////////// // * CREAM THEME PRESETS * // - ///////////////////////////// + // #region CREAM THEME PRESETS /** * Cream theme preset -> Options > Preset > Cream > Beveled. * @returns {void} */ - creamP01: () => { - pref.theme = 'cream'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + creamP01() { + grSet.theme = 'cream'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Cream theme preset -> Options > Preset > Cream > Beveled 2. * @returns {void} */ - creamP02: () => { - pref.theme = 'cream'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + creamP02() { + grSet.theme = 'cream'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Cream theme preset -> Options > Preset > Cream > Alternative. * @returns {void} */ - creamP03: () => { - pref.theme = 'cream'; - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + creamP03() { + grSet.theme = 'cream'; + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Cream theme preset -> Options > Preset > Cream > Alternative 2. * @returns {void} */ - creamP04: () => { - pref.theme = 'cream'; - pref.styleBevel = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'bevel'; - }, + creamP04() { + grSet.theme = 'cream'; + grSet.styleBevel = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Cream theme preset -> Options > Preset > Cream > Minimalized. * @returns {void} */ - creamP05: () => { - pref.theme = 'cream'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, - + creamP05() { + grSet.theme = 'cream'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ///////////////////////////////// // * NEON BLUE THEME PRESETS * // - ///////////////////////////////// + // #region NEON BLUE THEME PRESETS /** * Neon blue theme preset -> Options > Preset > Neon blue > Beveled. * @returns {void} */ - nblueP01: () => { - pref.theme = 'nblue'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + nblueP01() { + grSet.theme = 'nblue'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Beveled 2. * @returns {void} */ - nblueP02: () => { - pref.theme = 'nblue'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + nblueP02() { + grSet.theme = 'nblue'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Blended. * @returns {void} */ - nblueP03: () => { - pref.theme = 'nblue'; - pref.styleBlend = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + nblueP03() { + grSet.theme = 'nblue'; + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Blended 2. * @returns {void} */ - nblueP04: () => { - pref.theme = 'nblue'; - pref.styleBlend2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + nblueP04() { + grSet.theme = 'nblue'; + grSet.styleBlend2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Alternative. * @returns {void} */ - nblueP05: () => { - pref.theme = 'nblue'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + nblueP05() { + grSet.theme = 'nblue'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Alternative 2. * @returns {void} */ - nblueP06: () => { - pref.theme = 'nblue'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + nblueP06() { + grSet.theme = 'nblue'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Dark gray. - * @returns {string|boolean|number} - */ - nblueP07: () => { - pref.theme = 'nblue'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + nblueP07() { + grSet.theme = 'nblue'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + grSet.themeBrightness = 10; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Dark gray blended. - * @returns {string|boolean|number} - */ - nblueP08: () => { - pref.theme = 'nblue'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + nblueP08() { + grSet.theme = 'nblue'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Dark gray 2 blended. - * @returns {string|boolean|number} - */ - nblueP09: () => { - pref.theme = 'nblue'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + nblueP09() { + grSet.theme = 'nblue'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon blue theme preset -> Options > Preset > Neon blue > Minimalized. * @returns {void} */ - nblueP10: () => { - pref.theme = 'nblue'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + nblueP10() { + grSet.theme = 'nblue'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ////////////////////////////////// // * NEON GREEN THEME PRESETS * // - ////////////////////////////////// + // #region NEON GREEN THEME PRESETS /** * Neon green theme preset -> Options > Preset > Neon green > Beveled. * @returns {void} */ - ngreenP01: () => { - pref.theme = 'ngreen'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + ngreenP01() { + grSet.theme = 'ngreen'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon green theme preset -> Options > Preset > Neon green > Beveled 2. * @returns {void} */ - ngreenP02: () => { - pref.theme = 'ngreen'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + ngreenP02() { + grSet.theme = 'ngreen'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon green theme preset -> Options > Preset > Neon green > Blended. * @returns {void} */ - ngreenP03: () => { - pref.theme = 'ngreen'; - pref.styleBlend = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + ngreenP03() { + grSet.theme = 'ngreen'; + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon green theme preset -> Options > Preset > Neon green > Blended 2. * @returns {void} */ - ngreenP04: () => { - pref.theme = 'ngreen'; - pref.styleBlend2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + ngreenP04() { + grSet.theme = 'ngreen'; + grSet.styleBlend2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon green theme preset -> Options > Preset > Neon green > Alternative. * @returns {void} */ - ngreenP05: () => { - pref.theme = 'ngreen'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + ngreenP05() { + grSet.theme = 'ngreen'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon green theme preset -> Options > Preset > Neon green > Alternative 2. * @returns {void} */ - ngreenP06: () => { - pref.theme = 'ngreen'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + ngreenP06() { + grSet.theme = 'ngreen'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon green theme preset -> Options > Preset > Neon green > Dark gray. - * @returns {string|boolean|number} - */ - ngreenP07: () => { - pref.theme = 'ngreen'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + ngreenP07() { + grSet.theme = 'ngreen'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + grSet.themeBrightness = 10; + } /** * Neon green theme preset -> Options > Preset > Neon green > Dark gray blended. - * @returns {string|boolean|number} - */ - ngreenP08: () => { - pref.theme = 'ngreen'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + ngreenP08() { + grSet.theme = 'ngreen'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon green theme preset -> Options > Preset > Neon green > Dark gray 2 blended. - * @returns {string|boolean|number} - */ - ngreenP09: () => { - pref.theme = 'ngreen'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + ngreenP09() { + grSet.theme = 'ngreen'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon green theme preset -> Options > Preset > Neon green > Minimalized. * @returns {void} */ - ngreenP10: () => { - pref.theme = 'ngreen'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + ngreenP10() { + grSet.theme = 'ngreen'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - //////////////////////////////// // * NEON RED THEME PRESETS * // - //////////////////////////////// + // #region NEON RED THEME PRESETS /** * Neon red theme preset -> Options > Preset > Neon red > Beveled. * @returns {void} */ - nredP01: () => { - pref.theme = 'nred'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + nredP01() { + grSet.theme = 'nred'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon red theme preset -> Options > Preset > Neon red > Beveled 2. * @returns {void} */ - nredP02: () => { - pref.theme = 'nred'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + nredP02() { + grSet.theme = 'nred'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon red theme preset -> Options > Preset > Neon red > Blended. * @returns {void} */ - nredP03: () => { - pref.theme = 'nred'; - pref.styleBlend = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + nredP03() { + grSet.theme = 'nred'; + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon red theme preset -> Options > Preset > Neon red > Blended 2. * @returns {void} */ - nredP04: () => { - pref.theme = 'nred'; - pref.styleBlend2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + nredP04() { + grSet.theme = 'nred'; + grSet.styleBlend2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon red theme preset -> Options > Preset > Neon red > Alternative. * @returns {void} */ - nredP05: () => { - pref.theme = 'nred'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + nredP05() { + grSet.theme = 'nred'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon red theme preset -> Options > Preset > Neon red > Alternative 2. * @returns {void} */ - nredP06: () => { - pref.theme = 'nred'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + nredP06() { + grSet.theme = 'nred'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon red theme preset -> Options > Preset > Neon red > Dark gray. - * @returns {string|boolean|number} - */ - nredP07: () => { - pref.theme = 'nred'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + nredP07() { + grSet.theme = 'nred'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + grSet.themeBrightness = 10; + } /** * Neon red theme preset -> Options > Preset > Neon red > Dark gray blended. - * @returns {string|boolean|number} - */ - nredP08: () => { - pref.theme = 'nred'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + nredP08() { + grSet.theme = 'nred'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon red theme preset -> Options > Preset > Neon red > Dark gray 2 blended. - * @returns {string|boolean|number} - */ - nredP09: () => { - pref.theme = 'nred'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + nredP09() { + grSet.theme = 'nred'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon red theme preset -> Options > Preset > Neon red > Minimalized. * @returns {void} */ - nredP10: () => { - pref.theme = 'nred'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + nredP10() { + grSet.theme = 'nred'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ///////////////////////////////// // * NEON GOLD THEME PRESETS * // - ///////////////////////////////// + // #region NEON GOLD THEME PRESETS /** * Neon gold theme preset -> Options > Preset > Neon gold > Beveled. * @returns {void} */ - ngoldP01: () => { - pref.theme = 'ngold'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + ngoldP01() { + grSet.theme = 'ngold'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Beveled 2. * @returns {void} */ - ngoldP02: () => { - pref.theme = 'ngold'; - pref.styleBevel = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + ngoldP02() { + grSet.theme = 'ngold'; + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Blended. * @returns {void} */ - ngoldP03: () => { - pref.theme = 'ngold'; - pref.styleBlend = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + ngoldP03() { + grSet.theme = 'ngold'; + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Blended 2. * @returns {void} */ - ngoldP04: () => { - pref.theme = 'ngold'; - pref.styleBlend2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + ngoldP04() { + grSet.theme = 'ngold'; + grSet.styleBlend2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Alternative. * @returns {void} */ - ngoldP05: () => { - pref.theme = 'ngold'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + ngoldP05() { + grSet.theme = 'ngold'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Alternative 2. * @returns {void} */ - ngoldP06: () => { - pref.theme = 'ngold'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + ngoldP06() { + grSet.theme = 'ngold'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Dark gray. - * @returns {string|boolean|number} - */ - ngoldP07: () => { - pref.theme = 'ngold'; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + ngoldP07() { + grSet.theme = 'ngold'; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + grSet.themeBrightness = 10; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Dark gray blended. - * @returns {string|boolean|number} - */ - ngoldP08: () => { - pref.theme = 'ngold'; - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + ngoldP08() { + grSet.theme = 'ngold'; + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Dark gray 2 blended. - * @returns {string|boolean|number} - */ - ngoldP09: () => { - pref.theme = 'ngold'; - pref.styleBevel = true; - pref.styleBlend2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - pref.themeBrightness = 10; - }, + * @returns {void} + */ + ngoldP09() { + grSet.theme = 'ngold'; + grSet.styleBevel = true; + grSet.styleBlend2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + grSet.themeBrightness = 10; + } /** * Neon gold theme preset -> Options > Preset > Neon gold > Minimalized. * @returns {void} */ - ngoldP10: () => { - pref.theme = 'ngold'; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; - }, - + ngoldP10() { + grSet.theme = 'ngold'; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; + } + // #endregion - ////////////////////////////// // * CUSTOM THEME PRESETS * // - ////////////////////////////// + // #region CUSTOM THEME PRESETS /** * Initializes the custom theme and if no custom theme is currently active, select one randomly from the list. - * @returns {string} */ - customThemeSetup: () => { + customThemeSetup() { const customTheme = ['custom01', 'custom02', 'custom03', 'custom04', 'custom05', 'custom06', 'custom07', 'custom08', 'custom09', 'custom10']; - if (!customTheme.includes(pref.theme)) pref.theme = customTheme[Math.floor(Math.random() * customTheme.length)]; // If no custom theme active, pick a random one - initCustomTheme(); - }, + if (!customTheme.includes(grSet.theme)) grSet.theme = customTheme[Math.floor(Math.random() * customTheme.length)]; // If no custom theme active, pick a random one + grm.ui.initCustomTheme(); + } /** * Custom theme preset -> Options > Preset > Custom > Beveled. * @returns {void} */ - customP01: () => { - presets.customThemeSetup(); - pref.styleBevel = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + customP01() { + this.customThemeSetup(); + grSet.styleBevel = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Custom theme preset -> Options > Preset > Custom > Beveled 2. * @returns {void} */ - customP02: () => { - presets.customThemeSetup(); - pref.styleBevel = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'bevel'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + customP02() { + this.customThemeSetup(); + grSet.styleBevel = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'bevel'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Custom theme preset -> Options > Preset > Custom > Blended. * @returns {void} */ - customP03: () => { - presets.customThemeSetup(); - pref.styleBlend = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + customP03() { + this.customThemeSetup(); + grSet.styleBlend = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Custom theme preset -> Options > Preset > Custom > Blended 2. * @returns {void} */ - customP04: () => { - presets.customThemeSetup(); - pref.styleBlend2 = true; - pref.styleTopMenuButtons = 'emboss'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'inner'; - }, + customP04() { + this.customThemeSetup(); + grSet.styleBlend2 = true; + grSet.styleTopMenuButtons = 'emboss'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Custom theme preset -> Options > Preset > Custom > Gradiented. * @returns {void} */ - customP05: () => { - presets.customThemeSetup(); - pref.styleGradient = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'emboss'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + customP05() { + this.customThemeSetup(); + grSet.styleGradient = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'emboss'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Custom theme preset -> Options > Preset > Custom > Gradiented 2. * @returns {void} */ - customP06: () => { - presets.customThemeSetup(); - pref.styleGradient2 = true; - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'bevel'; - pref.styleProgressBar = 'bevel'; - pref.styleProgressBarFill = 'inner'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'bevel'; - pref.styleVolumeBarFill = 'inner'; - }, + customP06() { + this.customThemeSetup(); + grSet.styleGradient2 = true; + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'bevel'; + grSet.styleProgressBar = 'bevel'; + grSet.styleProgressBarFill = 'inner'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'bevel'; + grSet.styleVolumeBarFill = 'inner'; + } /** * Custom theme preset -> Options > Preset > Custom > Alternative. * @returns {void} */ - customP07: () => { - presets.customThemeSetup(); - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + customP07() { + this.customThemeSetup(); + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Custom theme preset -> Options > Preset > Custom > Alternative 2. * @returns {void} */ - customP08: () => { - presets.customThemeSetup(); - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'inner'; - pref.styleTransportButtons = 'inner'; - pref.styleProgressBarDesign = 'rounded'; - pref.styleProgressBar = 'inner'; - pref.styleProgressBarFill = 'bevel'; - pref.styleVolumeBarDesign = 'rounded'; - pref.styleVolumeBar = 'inner'; - pref.styleVolumeBarFill = 'bevel'; - }, + customP08() { + this.customThemeSetup(); + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'inner'; + grSet.styleTransportButtons = 'inner'; + grSet.styleProgressBarDesign = 'rounded'; + grSet.styleProgressBar = 'inner'; + grSet.styleProgressBarFill = 'bevel'; + grSet.styleVolumeBarDesign = 'rounded'; + grSet.styleVolumeBar = 'inner'; + grSet.styleVolumeBarFill = 'bevel'; + } /** * Custom theme preset -> Options > Preset > Custom > Minimalized. * @returns {void} */ - customP09: () => { - presets.customThemeSetup(); - pref.styleAlternative = true; - pref.styleTopMenuButtons = 'filled'; - pref.styleTransportButtons = 'minimal'; - }, + customP09() { + this.customThemeSetup(); + grSet.styleAlternative = true; + grSet.styleTopMenuButtons = 'filled'; + grSet.styleTransportButtons = 'minimal'; + } /** * Custom theme preset -> Options > Preset > Custom > Minimalized blended. * @returns {void} */ - customP10: () => { - presets.customThemeSetup(); - pref.styleBlend = true; - pref.styleAlternative2 = true; - pref.styleTopMenuButtons = 'minimal'; - pref.styleTransportButtons = 'minimal'; + customP10() { + this.customThemeSetup(); + grSet.styleBlend = true; + grSet.styleAlternative2 = true; + grSet.styleTopMenuButtons = 'minimal'; + grSet.styleTransportButtons = 'minimal'; } -}; - - -////////////////////////// -// * SET THEME PRESET * // -////////////////////////// -/** - * Sets chosen theme preset when using Options > Preset. - * @param {string} preset The name of the theme preset. - */ -function setThemePreset(preset) { - if (!Object.prototype.hasOwnProperty.call(presets, preset)) return; - themePresetMatchMode = false; - resetStyle('all'); - presets[preset](); - updateStyle(); -} - + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Theme preset initialization to determine if active styles match any theme presets, checks when user uses top menu Options > Style. + */ + initThemePresetState() { + const THEME = grSet.theme; + const CTHEME = grSet.theme.startsWith('custom'); + const BEVEL = grSet.styleBevel; + const BLEND = grSet.styleBlend; + const BLEND2 = grSet.styleBlend2; + const GRAD = grSet.styleGradient; + const GRAD2 = grSet.styleGradient2; + const ALT = grSet.styleAlternative; + const ALT2 = grSet.styleAlternative2; + const BW = grSet.styleBlackAndWhite; + const BW2 = grSet.styleBlackAndWhite2; + const BWR = grSet.styleBlackAndWhiteReborn; + const BR = grSet.styleBlackReborn; + const RW = grSet.styleRebornWhite; + const RB = grSet.styleRebornBlack; + const RF = grSet.styleRebornFusion; + const RF2 = grSet.styleRebornFusion2; + const RFA = grSet.styleRebornFusionAccent; + const RP = grSet.styleRandomPastel; + const RD = grSet.styleRandomDark; + const RAC = grSet.styleRandomAutoColor; + const TMB = grSet.styleTopMenuButtons; + const TPB = grSet.styleTransportButtons; + const PBD = grSet.styleProgressBarDesign; + const PB = grSet.styleProgressBar; + const PBF = grSet.styleProgressBarFill; + const VBD = grSet.styleVolumeBarDesign; + const VB = grSet.styleVolumeBar; + const VBF = grSet.styleVolumeBarFill; + const BRT = grSet.themeBrightness; + + const themePresets = [ + // * White theme presets settings + { preset: 'whiteP01', name: 'White preset: Beveled', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'default', VBF === 'bevel', BRT === 'default'] }, + { preset: 'whiteP02', name: 'White preset: Black and white', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'default', VBD === 'default', VB === 'inner', VBF === 'default', BRT === 'default'] }, + { preset: 'whiteP03', name: 'White preset: Black and white blended', value: [THEME === 'white', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'whiteP04', name: 'White preset: Black and white 2', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'whiteP05', name: 'White preset: Black and white 2 blended', value: [THEME === 'white', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'whiteP06', name: 'White preset: Black and white reborn', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, (BW || BW2 || BWR), !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'whiteP07', name: 'White preset: Black and white reborn blended', value: [THEME === 'white', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, (BW || BW2 || BWR), !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'whiteP08', name: 'White preset: Minimalized', value: [THEME === 'white', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Black theme presets settings + { preset: 'blackP01', name: 'Black preset: Beveled', value: [THEME === 'black', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'bevel', PBF === 'bevel', VBD === 'default', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'blackP02', name: 'Black preset: Blended', value: [THEME === 'black', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'blackP03', name: 'Black preset: Blended alternative', value: [THEME === 'black', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'blackP04', name: 'Black preset: Blended alternative 2', value: [THEME === 'black', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'blackP05', name: 'Black preset: Black reborn', value: [THEME === 'black', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'blackP06', name: 'Black preset: Black reborn blended', value: [THEME === 'black', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'blackP07', name: 'Black preset: Dark gray', value: [THEME === 'black', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, + { preset: 'blackP08', name: 'Black preset: Dark gray blended', value: [THEME === 'black', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'blackP09', name: 'Black preset: Dark gray 2 blended', value: [THEME === 'black', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'blackP10', name: 'Black preset: Minimalized', value: [THEME === 'black', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Reborn theme presets settings + { preset: 'rebornP01', name: 'Reborn preset: Beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP02', name: 'Reborn preset: Blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP03', name: 'Reborn preset: Blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP04', name: 'Reborn preset: Gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP05', name: 'Reborn preset: Gradiented 2', value: [THEME === 'reborn', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP06', name: 'Reborn preset: Minimalized', value: [THEME === 'reborn', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + { preset: 'rebornP07', name: 'Reborn preset: Minimalized blended', value: [THEME === 'reborn', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + { preset: 'rebornP08', name: 'Reborn preset: Reborn white beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'default', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP09', name: 'Reborn preset: Reborn white blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP10', name: 'Reborn preset: Reborn white blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP11', name: 'Reborn preset: Reborn black beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'bevel', PBF === 'bevel', VBD === 'default', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP12', name: 'Reborn preset: Reborn black blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP13', name: 'Reborn preset: Reborn black blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP14', name: 'Reborn preset: Reborn black gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP15', name: 'Reborn preset: Reborn black gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP16', name: 'Reborn preset: Reborn fusion beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP17', name: 'Reborn preset: Reborn fusion blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP18', name: 'Reborn preset: Reborn fusion blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP19', name: 'Reborn preset: Reborn fusion gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP20', name: 'Reborn preset: Reborn fusion gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP21', name: 'Reborn preset: Reborn fusion 2 beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP22', name: 'Reborn preset: Reborn fusion 2 blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP23', name: 'Reborn preset: Reborn fusion 2 blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP24', name: 'Reborn preset: Reborn fusion 2 gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP25', name: 'Reborn preset: Reborn fusion 2 gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP26', name: 'Reborn preset: Reborn fusion accent beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP27', name: 'Reborn preset: Reborn fusion accent blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP28', name: 'Reborn preset: Reborn fusion accent blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP29', name: 'Reborn preset: Reborn fusion accent gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'rebornP30', name: 'Reborn preset: Reborn fusion accent gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + + // * Random theme presets settings + { preset: 'randomP01', name: 'Random preset: Beveled blended alternative', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP02', name: 'Random preset: Beveled blended pastel', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP03', name: 'Random preset: Beveled blended dark', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP04', name: 'Random preset: Beveled blended auto dark', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'track', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP05', name: 'Random preset: Beveled auto dark', value: [THEME === 'random', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'track', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP06', name: 'Random preset: Beveled dark', value: [THEME === 'random', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP07', name: 'Random preset: Gradiented', value: [THEME === 'random', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP08', name: 'Random preset: Gradiented 2', value: [THEME === 'random', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'default', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'randomP09', name: 'Random preset: Minimalized', value: [THEME === 'random', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + { preset: 'randomP10', name: 'Random preset: Minimalized blended', value: [THEME === 'random', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Blue theme presets settings + { preset: 'blueP01', name: 'Blue preset: Beveled', value: [THEME === 'blue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'blueP02', name: 'Blue preset: Beveled 2', value: [THEME === 'blue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'blueP03', name: 'Blue preset: Gradiented', value: [THEME === 'blue', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'blueP04', name: 'Blue preset: Gradiented 2', value: [THEME === 'blue', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, + { preset: 'blueP05', name: 'Blue preset: Minimalized', value: [THEME === 'blue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Dark blue theme presets settings + { preset: 'darkblueP01', name: 'Dark blue preset: Beveled', value: [THEME === 'darkblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'darkblueP02', name: 'Dark blue preset: Beveled 2', value: [THEME === 'darkblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'darkblueP03', name: 'Dark blue preset: Gradiented', value: [THEME === 'darkblue', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'darkblueP04', name: 'Dark blue preset: Gradiented 2', value: [THEME === 'darkblue', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, + { preset: 'darkblueP05', name: 'Dark blue preset: Minimalized', value: [THEME === 'darkblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Red theme presets settings + { preset: 'redP01', name: 'Red preset: Beveled', value: [THEME === 'red', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'redP02', name: 'Red preset: Beveled 2', value: [THEME === 'red', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'redP03', name: 'Red preset: Gradiented', value: [THEME === 'red', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'redP04', name: 'Red preset: Gradiented 2', value: [THEME === 'red', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, + { preset: 'redP05', name: 'Red preset: Minimalized', value: [THEME === 'red', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Cream theme presets settings + { preset: 'creamP01', name: 'Cream preset: Beveled', value: [THEME === 'cream', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'creamP02', name: 'Cream preset: Beveled 2', value: [THEME === 'cream', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'creamP03', name: 'Cream preset: Gradiented', value: [THEME === 'cream', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'creamP04', name: 'Cream preset: Gradiented 2', value: [THEME === 'cream', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, + { preset: 'creamP05', name: 'Cream preset: Minimalized', value: [THEME === 'cream', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Neon blue theme presets settings + { preset: 'nblueP01', name: 'Neon blue preset: Beveled', value: [THEME === 'nblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'nblueP02', name: 'Neon blue preset: Beveled 2', value: [THEME === 'nblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'nblueP03', name: 'Neon blue preset: Blended', value: [THEME === 'nblue', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'nblueP04', name: 'Neon blue preset: Blended 2', value: [THEME === 'nblue', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'nblueP05', name: 'Neon blue preset: Alternative', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'nblueP06', name: 'Neon blue preset: Alternative 2', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'nblueP07', name: 'Neon blue preset: Dark gray', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, + { preset: 'nblueP08', name: 'Neon blue preset: Dark gray blended', value: [THEME === 'nblue', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'nblueP09', name: 'Neon blue preset: Dark gray 2 blended', value: [THEME === 'nblue', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'nblueP10', name: 'Neon blue preset: Minimalized', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Neon green theme presets settings + { preset: 'ngreenP01', name: 'Neon green preset: Beveled', value: [THEME === 'ngreen', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'ngreenP02', name: 'Neon green preset: Beveled 2', value: [THEME === 'ngreen', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'ngreenP03', name: 'Neon green preset: Blended', value: [THEME === 'ngreen', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'ngreenP04', name: 'Neon green preset: Blended 2', value: [THEME === 'ngreen', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'ngreenP05', name: 'Neon green preset: Alternative', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'ngreenP06', name: 'Neon green preset: Alternative 2', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'ngreenP07', name: 'Neon green preset: Dark gray', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, + { preset: 'ngreenP08', name: 'Neon green preset: Dark gray blended', value: [THEME === 'ngreen', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'ngreenP09', name: 'Neon green preset: Dark gray 2 blended', value: [THEME === 'ngreen', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'ngreenP10', name: 'Neon green preset: Minimalized', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Neon red theme presets settings + { preset: 'nredP01', name: 'Neon red preset: Beveled', value: [THEME === 'nred', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'nredP02', name: 'Neon red preset: Beveled 2', value: [THEME === 'nred', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'nredP03', name: 'Neon red preset: Blended', value: [THEME === 'nred', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'nredP04', name: 'Neon red preset: Blended 2', value: [THEME === 'nred', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'nredP05', name: 'Neon red preset: Alternative', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'nredP06', name: 'Neon red preset: Alternative 2', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'nredP07', name: 'Neon red preset: Dark gray', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, + { preset: 'nredP08', name: 'Neon red preset: Dark gray blended', value: [THEME === 'nred', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'nredP09', name: 'Neon red preset: Dark gray 2 blended', value: [THEME === 'nred', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'nredP10', name: 'Neon red preset: Minimalized', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Neon gold theme presets settings + { preset: 'ngoldP01', name: 'Neon gold preset: Beveled', value: [THEME === 'ngold', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'ngoldP02', name: 'Neon gold preset: Beveled 2', value: [THEME === 'ngold', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'ngoldP03', name: 'Neon gold preset: Blended', value: [THEME === 'ngold', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'ngoldP04', name: 'Neon gold preset: Blended 2', value: [THEME === 'ngold', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'ngoldP05', name: 'Neon gold preset: Alternative', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'ngoldP06', name: 'Neon gold preset: Alternative 2', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'ngoldP07', name: 'Neon gold preset: Dark gray', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, + { preset: 'ngoldP08', name: 'Neon gold preset: Dark gray blended', value: [THEME === 'ngold', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'ngoldP09', name: 'Neon gold preset: Dark gray 2 blended', value: [THEME === 'ngold', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, + { preset: 'ngoldP10', name: 'Neon gold preset: Minimalized', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + + // * Custom theme presets settings + { preset: 'customP01', name: `${grSet.theme} preset: Beveled`, value: [CTHEME, BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'customP02', name: `${grSet.theme} preset: Beveled 2`, value: [CTHEME, BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'customP03', name: `${grSet.theme} preset: Blended`, value: [CTHEME, !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'customP04', name: `${grSet.theme} preset: Blended 2`, value: [CTHEME, !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, + { preset: 'customP05', name: `${grSet.theme} preset: Gradiented`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'customP06', name: `${grSet.theme} preset: Gradiented 2`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, + { preset: 'customP07', name: `${grSet.theme} preset: Alternative`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'customP08', name: `${grSet.theme} preset: Alternative 2`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, + { preset: 'customP09', name: `${grSet.theme} preset: Minimalized`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, + { preset: 'customP10', name: `${grSet.theme} preset: Minimalized blended`, value: [CTHEME, !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] } + ]; -/////////////////////////////// -// * THEME PRESET MATCHING * // -/////////////////////////////// -/** - * Theme preset initialization to determine if active styles match any theme presets, checks when user uses top menu Options > Style. - */ -function initThemePresetState() { - const THEME = pref.theme; - const BEVEL = pref.styleBevel; - const BLEND = pref.styleBlend; - const BLEND2 = pref.styleBlend2; - const GRAD = pref.styleGradient; - const GRAD2 = pref.styleGradient2; - const ALT = pref.styleAlternative; - const ALT2 = pref.styleAlternative2; - const BW = pref.styleBlackAndWhite; - const BW2 = pref.styleBlackAndWhite2; - const BWR = pref.styleBlackAndWhiteReborn; - const BR = pref.styleBlackReborn; - const RW = pref.styleRebornWhite; - const RB = pref.styleRebornBlack; - const RF = pref.styleRebornFusion; - const RF2 = pref.styleRebornFusion2; - const RFA = pref.styleRebornFusionAccent; - const RP = pref.styleRandomPastel; - const RD = pref.styleRandomDark; - const RAC = pref.styleRandomAutoColor; - const TMB = pref.styleTopMenuButtons; - const TPB = pref.styleTransportButtons; - const PBD = pref.styleProgressBarDesign; - const PB = pref.styleProgressBar; - const PBF = pref.styleProgressBarFill; - const VBD = pref.styleVolumeBarDesign; - const VB = pref.styleVolumeBar; - const VBF = pref.styleVolumeBarFill; - const BRT = pref.themeBrightness; - const CTHEME = pref.theme.startsWith('custom'); - - const themePresets = [ - // * White theme presets settings - { preset: 'whiteP01', name: 'White preset: Beveled', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'default', VBF === 'bevel', BRT === 'default'] }, - { preset: 'whiteP02', name: 'White preset: Black and white', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'default', VBD === 'default', VB === 'inner', VBF === 'default', BRT === 'default'] }, - { preset: 'whiteP03', name: 'White preset: Black and white blended', value: [THEME === 'white', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'whiteP04', name: 'White preset: Black and white 2', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'whiteP05', name: 'White preset: Black and white 2 blended', value: [THEME === 'white', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'whiteP06', name: 'White preset: Black and white reborn', value: [THEME === 'white', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, (BW || BW2 || BWR), !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'whiteP07', name: 'White preset: Black and white reborn blended', value: [THEME === 'white', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, (BW || BW2 || BWR), !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'whiteP08', name: 'White preset: Minimalized', value: [THEME === 'white', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Black theme presets settings - { preset: 'blackP01', name: 'Black preset: Beveled', value: [THEME === 'black', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'bevel', PBF === 'bevel', VBD === 'default', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'blackP02', name: 'Black preset: Blended', value: [THEME === 'black', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'blackP03', name: 'Black preset: Blended alternative', value: [THEME === 'black', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'blackP04', name: 'Black preset: Blended alternative 2', value: [THEME === 'black', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'blackP05', name: 'Black preset: Black reborn', value: [THEME === 'black', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'blackP06', name: 'Black preset: Black reborn blended', value: [THEME === 'black', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'blackP07', name: 'Black preset: Dark gray', value: [THEME === 'black', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, - { preset: 'blackP08', name: 'Black preset: Dark gray blended', value: [THEME === 'black', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'blackP09', name: 'Black preset: Dark gray 2 blended', value: [THEME === 'black', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'blackP10', name: 'Black preset: Minimalized', value: [THEME === 'black', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Reborn theme presets settings - { preset: 'rebornP01', name: 'Reborn preset: Beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP02', name: 'Reborn preset: Blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP03', name: 'Reborn preset: Blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP04', name: 'Reborn preset: Gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP05', name: 'Reborn preset: Gradiented 2', value: [THEME === 'reborn', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP06', name: 'Reborn preset: Minimalized', value: [THEME === 'reborn', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - { preset: 'rebornP07', name: 'Reborn preset: Minimalized blended', value: [THEME === 'reborn', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - { preset: 'rebornP08', name: 'Reborn preset: Reborn white beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'default', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP09', name: 'Reborn preset: Reborn white blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP10', name: 'Reborn preset: Reborn white blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP11', name: 'Reborn preset: Reborn black beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'bevel', PBF === 'bevel', VBD === 'default', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP12', name: 'Reborn preset: Reborn black blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP13', name: 'Reborn preset: Reborn black blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP14', name: 'Reborn preset: Reborn black gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP15', name: 'Reborn preset: Reborn black gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP16', name: 'Reborn preset: Reborn fusion beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP17', name: 'Reborn preset: Reborn fusion blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP18', name: 'Reborn preset: Reborn fusion blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP19', name: 'Reborn preset: Reborn fusion gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP20', name: 'Reborn preset: Reborn fusion gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP21', name: 'Reborn preset: Reborn fusion 2 beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP22', name: 'Reborn preset: Reborn fusion 2 blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP23', name: 'Reborn preset: Reborn fusion 2 blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP24', name: 'Reborn preset: Reborn fusion 2 gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP25', name: 'Reborn preset: Reborn fusion 2 gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP26', name: 'Reborn preset: Reborn fusion accent beveled', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP27', name: 'Reborn preset: Reborn fusion accent blended', value: [THEME === 'reborn', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP28', name: 'Reborn preset: Reborn fusion accent blended 2', value: [THEME === 'reborn', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP29', name: 'Reborn preset: Reborn fusion accent gradiented', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'rebornP30', name: 'Reborn preset: Reborn fusion accent gradiented 2', value: [THEME === 'reborn', BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - - // * Random theme presets settings - { preset: 'randomP01', name: 'Random preset: Beveled blended alternative', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP02', name: 'Random preset: Beveled blended pastel', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP03', name: 'Random preset: Beveled blended dark', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP04', name: 'Random preset: Beveled blended auto dark', value: [THEME === 'random', BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'track', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP05', name: 'Random preset: Beveled auto dark', value: [THEME === 'random', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'track', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP06', name: 'Random preset: Beveled dark', value: [THEME === 'random', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP07', name: 'Random preset: Gradiented', value: [THEME === 'random', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP08', name: 'Random preset: Gradiented 2', value: [THEME === 'random', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'default', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'randomP09', name: 'Random preset: Minimalized', value: [THEME === 'random', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - { preset: 'randomP10', name: 'Random preset: Minimalized blended', value: [THEME === 'random', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Blue theme presets settings - { preset: 'blueP01', name: 'Blue preset: Beveled', value: [THEME === 'blue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'blueP02', name: 'Blue preset: Beveled 2', value: [THEME === 'blue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'blueP03', name: 'Blue preset: Gradiented', value: [THEME === 'blue', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'blueP04', name: 'Blue preset: Gradiented 2', value: [THEME === 'blue', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, - { preset: 'blueP05', name: 'Blue preset: Minimalized', value: [THEME === 'blue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Dark blue theme presets settings - { preset: 'darkblueP01', name: 'Dark blue preset: Beveled', value: [THEME === 'darkblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'darkblueP02', name: 'Dark blue preset: Beveled 2', value: [THEME === 'darkblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'darkblueP03', name: 'Dark blue preset: Gradiented', value: [THEME === 'darkblue', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'darkblueP04', name: 'Dark blue preset: Gradiented 2', value: [THEME === 'darkblue', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, - { preset: 'darkblueP05', name: 'Dark blue preset: Minimalized', value: [THEME === 'darkblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Red theme presets settings - { preset: 'redP01', name: 'Red preset: Beveled', value: [THEME === 'red', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'redP02', name: 'Red preset: Beveled 2', value: [THEME === 'red', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'redP03', name: 'Red preset: Gradiented', value: [THEME === 'red', !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'redP04', name: 'Red preset: Gradiented 2', value: [THEME === 'red', !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, - { preset: 'redP05', name: 'Red preset: Minimalized', value: [THEME === 'red', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Cream theme presets settings - { preset: 'creamP01', name: 'Cream preset: Beveled', value: [THEME === 'cream', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'creamP02', name: 'Cream preset: Beveled 2', value: [THEME === 'cream', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'creamP03', name: 'Cream preset: Gradiented', value: [THEME === 'cream', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'creamP04', name: 'Cream preset: Gradiented 2', value: [THEME === 'cream', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'bevel', VBF === 'bevel', BRT === 'default'] }, - { preset: 'creamP05', name: 'Cream preset: Minimalized', value: [THEME === 'cream', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Neon blue theme presets settings - { preset: 'nblueP01', name: 'Neon blue preset: Beveled', value: [THEME === 'nblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'nblueP02', name: 'Neon blue preset: Beveled 2', value: [THEME === 'nblue', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'nblueP03', name: 'Neon blue preset: Blended', value: [THEME === 'nblue', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'nblueP04', name: 'Neon blue preset: Blended 2', value: [THEME === 'nblue', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'nblueP05', name: 'Neon blue preset: Alternative', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'nblueP06', name: 'Neon blue preset: Alternative 2', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'nblueP07', name: 'Neon blue preset: Dark gray', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, - { preset: 'nblueP08', name: 'Neon blue preset: Dark gray blended', value: [THEME === 'nblue', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'nblueP09', name: 'Neon blue preset: Dark gray 2 blended', value: [THEME === 'nblue', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'nblueP10', name: 'Neon blue preset: Minimalized', value: [THEME === 'nblue', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Neon green theme presets settings - { preset: 'ngreenP01', name: 'Neon green preset: Beveled', value: [THEME === 'ngreen', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'ngreenP02', name: 'Neon green preset: Beveled 2', value: [THEME === 'ngreen', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'ngreenP03', name: 'Neon green preset: Blended', value: [THEME === 'ngreen', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'ngreenP04', name: 'Neon green preset: Blended 2', value: [THEME === 'ngreen', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'ngreenP05', name: 'Neon green preset: Alternative', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'ngreenP06', name: 'Neon green preset: Alternative 2', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'ngreenP07', name: 'Neon green preset: Dark gray', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, - { preset: 'ngreenP08', name: 'Neon green preset: Dark gray blended', value: [THEME === 'ngreen', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'ngreenP09', name: 'Neon green preset: Dark gray 2 blended', value: [THEME === 'ngreen', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'ngreenP10', name: 'Neon green preset: Minimalized', value: [THEME === 'ngreen', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Neon red theme presets settings - { preset: 'nredP01', name: 'Neon red preset: Beveled', value: [THEME === 'nred', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'nredP02', name: 'Neon red preset: Beveled 2', value: [THEME === 'nred', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'nredP03', name: 'Neon red preset: Blended', value: [THEME === 'nred', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'nredP04', name: 'Neon red preset: Blended 2', value: [THEME === 'nred', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'nredP05', name: 'Neon red preset: Alternative', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'nredP06', name: 'Neon red preset: Alternative 2', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'nredP07', name: 'Neon red preset: Dark gray', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, - { preset: 'nredP08', name: 'Neon red preset: Dark gray blended', value: [THEME === 'nred', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'nredP09', name: 'Neon red preset: Dark gray 2 blended', value: [THEME === 'nred', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'nredP10', name: 'Neon red preset: Minimalized', value: [THEME === 'nred', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Neon gold theme presets settings - { preset: 'ngoldP01', name: 'Neon gold preset: Beveled', value: [THEME === 'ngold', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'ngoldP02', name: 'Neon gold preset: Beveled 2', value: [THEME === 'ngold', BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'default', PB === 'inner', PBF === 'bevel', VBD === 'default', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'ngoldP03', name: 'Neon gold preset: Blended', value: [THEME === 'ngold', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'ngoldP04', name: 'Neon gold preset: Blended 2', value: [THEME === 'ngold', !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'ngoldP05', name: 'Neon gold preset: Alternative', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'ngoldP06', name: 'Neon gold preset: Alternative 2', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'ngoldP07', name: 'Neon gold preset: Dark gray', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'default', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 10] }, - { preset: 'ngoldP08', name: 'Neon gold preset: Dark gray blended', value: [THEME === 'ngold', !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'ngoldP09', name: 'Neon gold preset: Dark gray 2 blended', value: [THEME === 'ngold', BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 10] }, - { preset: 'ngoldP10', name: 'Neon gold preset: Minimalized', value: [THEME === 'ngold', !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - - // * Custom theme presets settings - { preset: 'customP01', name: `${pref.theme} preset: Beveled`, value: [CTHEME, BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'customP02', name: `${pref.theme} preset: Beveled 2`, value: [CTHEME, BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'bevel', TPB === 'bevel', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'customP03', name: `${pref.theme} preset: Blended`, value: [CTHEME, !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'customP04', name: `${pref.theme} preset: Blended 2`, value: [CTHEME, !BEVEL, !BLEND, BLEND2, !GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'emboss', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'inner', VBD === 'rounded', VB === 'inner', VBF === 'inner', BRT === 'default'] }, - { preset: 'customP05', name: `${pref.theme} preset: Gradiented`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, GRAD, !GRAD2, !ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'emboss', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'customP06', name: `${pref.theme} preset: Gradiented 2`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'bevel', PBD === 'default', PB === 'bevel', PBF === 'inner', VBD === 'rounded', VB === 'bevel', VBF === 'inner', BRT === 'default'] }, - { preset: 'customP07', name: `${pref.theme} preset: Alternative`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'customP08', name: `${pref.theme} preset: Alternative 2`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'inner', TPB === 'inner', PBD === 'rounded', PB === 'inner', PBF === 'bevel', VBD === 'rounded', VB === 'inner', VBF === 'bevel', BRT === 'default'] }, - { preset: 'customP09', name: `${pref.theme} preset: Minimalized`, value: [CTHEME, !BEVEL, !BLEND, !BLEND2, !GRAD, !GRAD2, ALT, !ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'filled', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] }, - { preset: 'customP10', name: `${pref.theme} preset: Minimalized blended`, value: [CTHEME, !BEVEL, BLEND, !BLEND2, !GRAD, !GRAD2, !ALT, ALT2, !BW, !BW2, !BWR, !BR, !RW, !RB, !RF, !RF2, !RFA, !RP, !RD, RAC === 'off', TMB === 'minimal', TPB === 'minimal', PBD === 'default', PB === 'default', PBF === 'default', VBD === 'default', VB === 'default', VBF === 'default', BRT === 'default'] } - ]; - - const hidePresetIndicator = () => { - presetIndicatorTimer = setTimeout(() => { - themePresetName = ''; - clearTimeout(presetIndicatorTimer); - presetIndicatorTimer = null; - window.Repaint(); - }, 5000); - }; - - for (const preset of themePresets) { - if (preset.value.every(Boolean)) { - pref.preset = preset.preset; - themePresetName = preset.name; - hidePresetIndicator(); - break; - } - else { - pref.preset = false; - themePresetName = ''; + const hidePresetIndicator = () => { + grm.ui.presetIndicatorTimer = setTimeout(() => { + grm.ui.themePresetName = ''; + clearTimeout(grm.ui.presetIndicatorTimer); + grm.ui.presetIndicatorTimer = null; + window.Repaint(); + }, 5000); + }; + + for (const preset of themePresets) { + if (preset.value.every(Boolean)) { + grSet.preset = preset.preset; + grm.ui.themePresetName = preset.name; + hidePresetIndicator(); + break; + } + else { + grSet.preset = false; + grm.ui.themePresetName = ''; + } } } -} - -///////////////////////////// -// * THEME PRESET PICKER * // -///////////////////////////// -/** - * Gets a random theme preset, used in Options > Preset > Auto random. - */ -function getRandomThemePreset() { - clearInterval(presetAutoRandomModeTimer); - presetAutoRandomModeTimer = null; - - if ($('[%GR_THEME%]') || $('[%GR_STYLE%]') || $('[%GR_PRESET%]')) return; - - const pickRandomPreset = () => { - let randomThemePreset; - let lastIndex; - - // * Random presets - const themePresetsRandom = [ - ...pref.presetSelectWhite ? [presets.whiteP01, presets.whiteP02, presets.whiteP03, presets.whiteP04, presets.whiteP05, presets.whiteP06, presets.whiteP07, presets.whiteP08] : [], - ...pref.presetSelectBlack ? [presets.blackP01, presets.blackP02, presets.blackP03, presets.blackP04, presets.blackP05, presets.blackP06, presets.blackP07, presets.blackP08, presets.blackP09, presets.blackP10] : [], - ...pref.presetSelectReborn ? [presets.rebornP01, presets.rebornP02, presets.rebornP03, presets.rebornP04, presets.rebornP05, presets.rebornP06, presets.rebornP07, presets.rebornP08, presets.rebornP09, presets.rebornP10, presets.rebornP11, presets.rebornP12, presets.rebornP13, presets.rebornP14, presets.rebornP15, presets.rebornP16, presets.rebornP17, presets.rebornP18, presets.rebornP19, presets.rebornP20, presets.rebornP21, presets.rebornP22, presets.rebornP23, presets.rebornP24, presets.rebornP25, presets.rebornP26, presets.rebornP27, presets.rebornP28, presets.rebornP29, presets.rebornP30] : [], - ...pref.presetSelectRandom ? [presets.randomP01, presets.randomP02, presets.randomP03, presets.randomP04, presets.randomP05, presets.randomP06, presets.randomP07, presets.randomP08, presets.randomP09, presets.randomP10] : [], - ...pref.presetSelectBlue ? [presets.blueP01, presets.blueP02, presets.blueP03, presets.blueP04, presets.blueP05] : [], - ...pref.presetSelectDarkblue ? [presets.darkblueP01, presets.darkblueP02, presets.darkblueP03, presets.darkblueP04, presets.darkblueP05] : [], - ...pref.presetSelectRed ? [presets.redP01, presets.redP02, presets.redP03, presets.redP04, presets.redP05] : [], - ...pref.presetSelectCream ? [presets.creamP01, presets.creamP02, presets.creamP03, presets.creamP04, presets.creamP05] : [], - ...pref.presetSelectNblue ? [presets.nblueP01, presets.nblueP02, presets.nblueP03, presets.nblueP04, presets.nblueP05, presets.nblueP06, presets.nblueP07, presets.nblueP08, presets.nblueP09, presets.nblueP10] : [], - ...pref.presetSelectNgreen ? [presets.ngreenP01, presets.ngreenP02, presets.ngreenP03, presets.ngreenP04, presets.ngreenP05, presets.ngreenP06, presets.ngreenP07, presets.ngreenP08, presets.ngreenP09, presets.ngreenP10] : [], - ...pref.presetSelectNred ? [presets.nredP01, presets.nredP02, presets.nredP03, presets.nredP04, presets.nredP05, presets.nredP06, presets.nredP07, presets.nredP08, presets.nredP09, presets.nredP10] : [], - ...pref.presetSelectNgold ? [presets.ngoldP01, presets.ngoldP02, presets.ngoldP03, presets.ngoldP04, presets.ngoldP05, presets.ngoldP06, presets.ngoldP07, presets.ngoldP08, presets.ngoldP09, presets.ngoldP10] : [], - ...pref.presetSelectCustom ? Array(10).fill([presets.customP01, presets.customP02, presets.customP03, presets.customP04, presets.customP05, presets.customP06, presets.customP07, presets.customP08, presets.customP09, presets.customP10]).flat() /* Increase pick probability ten times ( 10 custom themes ) */ : [] - ]; - - // * Harmonic presets - const themePresetsLight = [ - ...pref.presetSelectWhite ? [presets.whiteP01, presets.whiteP02, presets.whiteP03, presets.whiteP04, presets.whiteP05, presets.whiteP06, presets.whiteP07, presets.whiteP08] : [], - ...pref.presetSelectReborn ? [presets.rebornP08, presets.rebornP09, presets.rebornP10] : [] - ]; - const themePresetsMiddle = [ - ...pref.presetSelectReborn ? [presets.rebornP01, presets.rebornP02, presets.rebornP03, presets.rebornP04, presets.rebornP05, presets.rebornP06, presets.rebornP07] : [] - ]; - const themePresetsFusion = [ - ...pref.presetSelectReborn ? [presets.rebornP16, presets.rebornP17, presets.rebornP18, presets.rebornP19, presets.rebornP20, presets.rebornP21, presets.rebornP22, presets.rebornP23, presets.rebornP24, presets.rebornP25, presets.rebornP26, presets.rebornP27, presets.rebornP28, presets.rebornP29, presets.rebornP30] : [] - ]; - const themePresetsDark = [ - ...pref.presetSelectBlack ? [presets.blackP01, presets.blackP02, presets.blackP03, presets.blackP04, presets.blackP05, presets.blackP06, presets.blackP07, presets.blackP08, presets.blackP09, presets.blackP10] : [], - ...pref.presetSelectReborn ? [presets.rebornP11, presets.rebornP12, presets.rebornP13, presets.rebornP14, presets.rebornP15] : [], - ...pref.presetSelectRandom ? [presets.randomP03, presets.randomP06] : [], - ...pref.presetSelectNblue ? [presets.nblueP01, presets.nblueP02, presets.nblueP03, presets.nblueP04, presets.nblueP05, presets.nblueP06, presets.nblueP07, presets.nblueP08, presets.nblueP09, presets.nblueP10] : [], - ...pref.presetSelectNgreen ? [presets.ngreenP01, presets.ngreenP02, presets.ngreenP03, presets.ngreenP04, presets.ngreenP05, presets.ngreenP06, presets.ngreenP07, presets.ngreenP08, presets.ngreenP09, presets.ngreenP10] : [], - ...pref.presetSelectNred ? [presets.nredP01, presets.nredP02, presets.nredP03, presets.nredP04, presets.nredP05, presets.nredP06, presets.nredP07, presets.nredP08, presets.nredP09, presets.nredP10] : [], - ...pref.presetSelectNgold ? [presets.ngoldP01, presets.ngoldP02, presets.ngoldP03, presets.ngoldP04, presets.ngoldP05, presets.ngoldP06, presets.ngoldP07, presets.ngoldP08, presets.ngoldP09, presets.ngoldP10] : [] - ]; - - if (pref.presetSelectMode === 'harmonic') { - colBrightness = new Color(col.primary).brightness; - colBrightness2 = new Color(col.primary_alt).brightness; - imgBrightness = CalcImgBrightness(albumArt); - - if (colBrightness > 200 || imgBrightness > 180) { // * Light - while ((randomThemePreset = Math.floor(Math.random() * themePresetsLight.length)) === lastIndex); - themePresetsLight[(lastIndex = randomThemePreset)](); - console.log('themePresetsLight'); - } - else if (colBrightness < 200 && colBrightness > 50 || imgBrightness < 180 && imgBrightness > 130) { // * Middle - if (ColorDistance(col.primary, col.primary_alt) > 200) { // * Reborn fusion - while ((randomThemePreset = Math.floor(Math.random() * themePresetsFusion.length)) === lastIndex); - themePresetsFusion[(lastIndex = randomThemePreset)](); - console.log('themePresetsFusion'); - } else { - while ((randomThemePreset = Math.floor(Math.random() * themePresetsMiddle.length)) === lastIndex); - themePresetsMiddle[(lastIndex = randomThemePreset)](); - console.log('themePresetsMiddle'); + /** + * Gets a random theme preset, used in Options > Preset > Auto random. + */ + getRandomThemePreset() { + clearInterval(grm.ui.presetAutoRandomModeTimer); + grm.ui.presetAutoRandomModeTimer = null; + + if ($('[%GR_THEME%]') || $('[%GR_STYLE%]') || $('[%GR_PRESET%]')) return; + + const pickRandomPreset = () => { + let randomThemePreset; + let lastIndex; + + // * Random presets + const themePresetsRandom = [ + ...grSet.presetSelectWhite ? [this.whiteP01, this.whiteP02, this.whiteP03, this.whiteP04, this.whiteP05, this.whiteP06, this.whiteP07, this.whiteP08] : [], + ...grSet.presetSelectBlack ? [this.blackP01, this.blackP02, this.blackP03, this.blackP04, this.blackP05, this.blackP06, this.blackP07, this.blackP08, this.blackP09, this.blackP10] : [], + ...grSet.presetSelectReborn ? [this.rebornP01, this.rebornP02, this.rebornP03, this.rebornP04, this.rebornP05, this.rebornP06, this.rebornP07, this.rebornP08, this.rebornP09, this.rebornP10, this.rebornP11, this.rebornP12, this.rebornP13, this.rebornP14, this.rebornP15, this.rebornP16, this.rebornP17, this.rebornP18, this.rebornP19, this.rebornP20, this.rebornP21, this.rebornP22, this.rebornP23, this.rebornP24, this.rebornP25, this.rebornP26, this.rebornP27, this.rebornP28, this.rebornP29, this.rebornP30] : [], + ...grSet.presetSelectRandom ? [this.randomP01, this.randomP02, this.randomP03, this.randomP04, this.randomP05, this.randomP06, this.randomP07, this.randomP08, this.randomP09, this.randomP10] : [], + ...grSet.presetSelectBlue ? [this.blueP01, this.blueP02, this.blueP03, this.blueP04, this.blueP05] : [], + ...grSet.presetSelectDarkblue ? [this.darkblueP01, this.darkblueP02, this.darkblueP03, this.darkblueP04, this.darkblueP05] : [], + ...grSet.presetSelectRed ? [this.redP01, this.redP02, this.redP03, this.redP04, this.redP05] : [], + ...grSet.presetSelectCream ? [this.creamP01, this.creamP02, this.creamP03, this.creamP04, this.creamP05] : [], + ...grSet.presetSelectNblue ? [this.nblueP01, this.nblueP02, this.nblueP03, this.nblueP04, this.nblueP05, this.nblueP06, this.nblueP07, this.nblueP08, this.nblueP09, this.nblueP10] : [], + ...grSet.presetSelectNgreen ? [this.ngreenP01, this.ngreenP02, this.ngreenP03, this.ngreenP04, this.ngreenP05, this.ngreenP06, this.ngreenP07, this.ngreenP08, this.ngreenP09, this.ngreenP10] : [], + ...grSet.presetSelectNred ? [this.nredP01, this.nredP02, this.nredP03, this.nredP04, this.nredP05, this.nredP06, this.nredP07, this.nredP08, this.nredP09, this.nredP10] : [], + ...grSet.presetSelectNgold ? [this.ngoldP01, this.ngoldP02, this.ngoldP03, this.ngoldP04, this.ngoldP05, this.ngoldP06, this.ngoldP07, this.ngoldP08, this.ngoldP09, this.ngoldP10] : [], + ...grSet.presetSelectCustom ? Array(10).fill([this.customP01.bind(this), this.customP02.bind(this), this.customP03.bind(this), this.customP04.bind(this), this.customP05.bind(this), this.customP06.bind(this), this.customP07.bind(this), this.customP08.bind(this), this.customP09.bind(this), this.customP10.bind(this)]).flat() /* Increase pick probability ten times ( 10 custom themes ) */ : [] + ]; + + // * Harmonic presets + const themePresetsLight = [ + ...grSet.presetSelectWhite ? [this.whiteP01, this.whiteP02, this.whiteP03, this.whiteP04, this.whiteP05, this.whiteP06, this.whiteP07, this.whiteP08] : [], + ...grSet.presetSelectReborn ? [this.rebornP08, this.rebornP09, this.rebornP10] : [] + ]; + const themePresetsMiddle = [ + ...grSet.presetSelectReborn ? [this.rebornP01, this.rebornP02, this.rebornP03, this.rebornP04, this.rebornP05, this.rebornP06, this.rebornP07] : [] + ]; + const themePresetsFusion = [ + ...grSet.presetSelectReborn ? [this.rebornP16, this.rebornP17, this.rebornP18, this.rebornP19, this.rebornP20, this.rebornP21, this.rebornP22, this.rebornP23, this.rebornP24, this.rebornP25, this.rebornP26, this.rebornP27, this.rebornP28, this.rebornP29, this.rebornP30] : [] + ]; + const themePresetsDark = [ + ...grSet.presetSelectBlack ? [this.blackP01, this.blackP02, this.blackP03, this.blackP04, this.blackP05, this.blackP06, this.blackP07, this.blackP08, this.blackP09, this.blackP10] : [], + ...grSet.presetSelectReborn ? [this.rebornP11, this.rebornP12, this.rebornP13, this.rebornP14, this.rebornP15] : [], + ...grSet.presetSelectRandom ? [this.randomP03, this.randomP06] : [], + ...grSet.presetSelectNblue ? [this.nblueP01, this.nblueP02, this.nblueP03, this.nblueP04, this.nblueP05, this.nblueP06, this.nblueP07, this.nblueP08, this.nblueP09, this.nblueP10] : [], + ...grSet.presetSelectNgreen ? [this.ngreenP01, this.ngreenP02, this.ngreenP03, this.ngreenP04, this.ngreenP05, this.ngreenP06, this.ngreenP07, this.ngreenP08, this.ngreenP09, this.ngreenP10] : [], + ...grSet.presetSelectNred ? [this.nredP01, this.nredP02, this.nredP03, this.nredP04, this.nredP05, this.nredP06, this.nredP07, this.nredP08, this.nredP09, this.nredP10] : [], + ...grSet.presetSelectNgold ? [this.ngoldP01, this.ngoldP02, this.ngoldP03, this.ngoldP04, this.ngoldP05, this.ngoldP06, this.ngoldP07, this.ngoldP08, this.ngoldP09, this.ngoldP10] : [] + ]; + + if (grSet.presetSelectMode === 'harmonic') { + grCol.colBrightness = new Color(grCol.primary).brightness; + grCol.colBrightness2 = new Color(grCol.primary_alt).brightness; + grCol.imgBrightness = CalcImgBrightness(grm.ui.albumArt); + + if (grCol.colBrightness > 200 || grCol.imgBrightness > 180) { // * Light + while ((randomThemePreset = Math.floor(Math.random() * themePresetsLight.length)) === lastIndex); + themePresetsLight[(lastIndex = randomThemePreset)](); + console.log('themePresetsLight'); + } + else if (grCol.colBrightness < 200 && grCol.colBrightness > 50 || grCol.imgBrightness < 180 && grCol.imgBrightness > 130) { // * Middle + if (ColorDistance(grCol.primary, grCol.primary_alt) > 200) { // * Reborn fusion + while ((randomThemePreset = Math.floor(Math.random() * themePresetsFusion.length)) === lastIndex); + themePresetsFusion[(lastIndex = randomThemePreset)](); + console.log('themePresetsFusion'); + } else { + while ((randomThemePreset = Math.floor(Math.random() * themePresetsMiddle.length)) === lastIndex); + themePresetsMiddle[(lastIndex = randomThemePreset)](); + console.log('themePresetsMiddle'); + } + } + else if (grCol.colBrightness < 50 || grCol.imgBrightness < 130) { // * Dark + while ((randomThemePreset = Math.floor(Math.random() * themePresetsDark.length)) === lastIndex); + themePresetsDark[(lastIndex = randomThemePreset)](); + console.log('themePresetsDark'); } } - else if (colBrightness < 50 || imgBrightness < 130) { // * Dark - while ((randomThemePreset = Math.floor(Math.random() * themePresetsDark.length)) === lastIndex); - themePresetsDark[(lastIndex = randomThemePreset)](); - console.log('themePresetsDark'); + else { + if (themePresetsRandom.length === 0) return; + while ((randomThemePreset = Math.floor(Math.random() * themePresetsRandom.length)) === lastIndex); + themePresetsRandom[(lastIndex = randomThemePreset)](); } } - else { - if (themePresetsRandom.length === 0) return; - while ((randomThemePreset = Math.floor(Math.random() * themePresetsRandom.length)) === lastIndex); - themePresetsRandom[(lastIndex = randomThemePreset)](); - } - } - const setRandomPreset = () => { - resetStyle('all'); - pickRandomPreset(); - updateStyle(); - themePresetMatchMode = false; - }; + const setRandomPreset = () => { + grm.ui.resetStyle('all'); + pickRandomPreset(); + grm.ui.updateStyle(); + grm.ui.themePresetMatchMode = false; + }; - if (['off', 'track', 'album', 'dblclick'].includes(pref.presetAutoRandomMode)) { - setRandomPreset(); - } - else { - themePresetIndicator = false; - presetAutoRandomModeTimer = setInterval(() => { - if (activeMenu) return; // * Workaround that pauses when a context menu is active which partially blocks color initialization; + if (['off', 'track', 'album', 'dblclick'].includes(grSet.presetAutoRandomMode)) { setRandomPreset(); - }, pref.presetAutoRandomMode); + } + else { + grm.ui.themePresetIndicator = false; + grm.ui.presetAutoRandomModeTimer = setInterval(() => { + if (grm.ui.activeMenu) return; // * Workaround that pauses when a context menu is active which partially blocks color initialization; + setRandomPreset(); + }, grSet.presetAutoRandomMode); + } + + if (grCfg.settings.showDebugThemeLog) console.log('preset:', grSet.preset); } - if (settings.showDebugThemeLog) console.log('preset:', pref.preset); + /** + * Sets chosen theme preset when using Options > Preset. + * @param {string} preset - The name of the theme preset. + */ + setThemePreset(preset) { + if (typeof this[preset] !== 'function') return; + grm.ui.themePresetMatchMode = false; + grm.ui.resetStyle('all'); + this[preset](); // Call the method on the ThemePreset instance + grm.ui.updateStyle(); + } + // #endregion } diff --git a/profile/georgia-reborn/scripts/Base/gr-themes.js b/profile/georgia-reborn/scripts/Base/gr-themes.js deleted file mode 100644 index afe01de2..00000000 --- a/profile/georgia-reborn/scripts/Base/gr-themes.js +++ /dev/null @@ -1,6360 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Themes * // -// * Author: TT * // -// * Org. Author: Mordred * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-15 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -//////////////////////////// -// * WHITE THEME COLORS * // -//////////////////////////// -/** - * The default colors for White theme used in Options > Theme > White. - */ -const whiteTheme = { - name: 'white', - colors: { - primary: RGB(25, 160, 240), - darkAccent: RGB(12, 144, 245), - accent: RGB(12, 137, 232), - lightAccent: RGB(10, 130, 220) - } -}; - - -/** - * The Playlist colors for White theme used in Options > Theme > White. - */ -function playlistColorsWhiteTheme() { - // * MAIN COLORS * // - g_pl_colors.bg = RGB(255, 255, 255); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(140, 140, 140); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(120, 120, 120) : RGB(80, 80, 80); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(80, 80, 80) : RGB(140, 140, 140); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = col.primary; - g_pl_colors.header_sideMarker = g_pl_colors.header_nowplaying_bg; - g_pl_colors.header_artist_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_artist_playing = pref.layout !== 'default' ? RGB(255, 255, 255) : pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_album_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_album_playing = pref.layout !== 'default' ? RGB(245, 245, 245) : pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_info_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_info_playing = pref.layout !== 'default' ? RGB(245, 245, 245) : g_pl_colors.header_info_normal; - g_pl_colors.header_date_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_date_playing = pref.layout !== 'default' ? RGB(245, 245, 245) : g_pl_colors.header_date_normal; - g_pl_colors.header_line_normal = RGB(200, 200, 200); - g_pl_colors.header_line_playing = RGB(200, 200, 200); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = col.primary; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(245, 245, 245, 130) : pref.styleAlternative ? RGB(255, 255, 255) : RGB(245, 245, 245); - g_pl_colors.row_selection_bg = RGB(200, 200, 200); - g_pl_colors.row_selection_frame = g_pl_colors.row_selection_bg; - g_pl_colors.row_sideMarker = col.primary; - g_pl_colors.row_title_normal = pref.layout !== 'compact' ? pref.styleBlend ? RGB(60, 60, 60) : RGB(100, 100, 100) : pref.layout === 'compact' ? pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120) : ''; - g_pl_colors.row_title_playing = RGB(245, 245, 245); - g_pl_colors.row_title_selected = RGB(0, 0, 0); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = RGB(200, 200, 200); - g_pl_colors.row_drag_line = ShadeColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = RGB(120, 120, 120); - g_pl_colors.sbar_btn_hovered = RGB(0, 0, 0); - g_pl_colors.sbar_thumb_normal = RGB(200, 200, 200); - g_pl_colors.sbar_thumb_hovered = RGB(120, 120, 120); - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for White theme used in Options > Theme > White. - */ -function libraryColorsWhiteTheme() { - // * MAIN COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - ui.col.imgOverlaySel = pref.styleBlackAndWhite ? RGBA(230, 230, 230, 175) : RGBtoRGBA(ui.col.bg, 175); - - // * ROW COLORS * // - ui.col.nowPlayingBg = pref.styleBlackAndWhite ? RGB(230, 230, 230) : g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = - pref.styleBlackAndWhite ? isStreaming ? RGB(207, 0, 5) : RGB(255, 255, 255) : - pref.styleBlackAndWhite2 ? isStreaming ? RGB(207, 0, 5) : RGB(40, 40, 40) : - ui.col.nowPlayingBg; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = pref.styleBlackAndWhite ? RGB(220, 220, 220) : pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - ui.col.iconPlus_h = pref.styleBlackAndWhite ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.iconPlus_sel = pref.styleBlackAndWhite ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.iconPlusBg = RGB(240, 240, 240); - ui.col.iconMinus_e = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(0, 0, 0); - - // * TEXT COLORS * // - ui.col.text = - ppt.albumArtShow && img.labels.overlayDark ? RGB(220, 220, 220) : - pref.styleBlackAndWhite ? RGB(200, 200, 200) : - pref.styleBlend ? RGB(60, 60, 60) : - RGB(100, 100, 100); - - ui.col.text_h = - ppt.albumArtShow && img.labels.overlayDark || - pref.styleBlackAndWhite && (!ppt.albumArtShow || ppt.albumArtShow && ppt.highLightRow !== 2) ? RGB(255, 255, 255) : - RGB(0, 0, 0); - - ui.col.text_nowp = - pref.styleBlackAndWhite || pref.styleBlackAndWhite2 || ppt.albumArtShow && img.labels.overlayDark || - lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); - - ui.col.textSel = - pref.styleBlackAndWhite ? - ppt.albumArtShow && ([1, 3].includes(ppt.albumArtLabelType) || ['coversLabelsBottom', 'coversLabelsBlend'].includes(pref.libraryDesign)) || - pref.libraryDesign === 'traditional' ? RGB(0, 0, 0) : RGB(255, 255, 255) : - ppt.albumArtShow && !img.labels.overlayDark && ppt.albumArtLabelType !== 2 && !pref.styleBlackAndWhite2 ? lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255) : - RGB(0, 0, 0); - - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = pref.styleBlackAndWhite ? RGB(200, 200, 200) : pref.styleBlend ? RGB(40, 40, 40) : RGB(80, 80, 80); - ui.col.count = ui.col.text; - - // * BUTTON COLORS * // - ui.col.search = pref.styleBlackAndWhite ? RGB(200, 200, 200) : pref.styleBlend ? RGB(60, 60, 60) : RGB(100, 100, 100); - ui.col.searchBtn = pref.styleBlackAndWhite ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.crossBtn = pref.styleBlackAndWhite ? RGB(255, 255, 255) : pref.styleBlend ? RGB(40, 40, 40) : RGB(80, 80, 80); - ui.col.filterBtn = pref.styleBlackAndWhite ? RGB(220, 220, 220) : pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - ui.col.settingsBtn = pref.styleBlackAndWhite ? RGB(220, 220, 220) : pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - ui.col.line = pref.styleBlackAndWhite ? RGB(45, 45, 45) : RGB(200, 200, 200); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = RGB(120, 120, 120); - ui.col.sbarNormal = RGB(114, 114, 114); - ui.col.sbarHovered = RGB(120, 120, 120); - ui.col.sbarDrag = RGB(120, 120, 120); -} - - -/** - * The Biography colors for White theme used in Options > Theme > White. - */ -function biographyColorsWhiteTheme() { - // * MAIN COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = - pref.layout === 'artwork' ? pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120) : - pref.styleBlackAndWhite ? RGB(220, 220, 220) : - pref.styleBlackAndWhite2 ? RGB(80, 80, 80) : - g_pl_colors.header_artist_playing; - - uiBio.col.bottomLine = (uiBio.blur.blend || uiBio.blur.light) ? RGB(120, 120, 120) : g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = - pref.styleBlackAndWhite ? RGB(200, 200, 200) : - pref.styleBlackAndWhite2 ? RGB(80, 80, 80) : - g_pl_colors.row_title_normal; - - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = pref.styleBlackAndWhite ? RGB(230, 230, 230) : pref.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = pref.styleBlackAndWhite ? RGB(0, 0, 0) : pref.styleBlackAndWhite2 ? RGB(255, 255, 255) : lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = RGB(220, 160, 40); - uiBio.col.noPhotoStubBg = RGB(245, 245, 245); - uiBio.col.noPhotoStubText = RGB(120, 120, 120); - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = RGB(120, 120, 120); - uiBio.col.sbarNormal = RGB(114, 114, 114); - uiBio.col.sbarHovered = RGB(120, 120, 120); - uiBio.col.sbarDrag = RGB(120, 120, 120); -} - - -/** - * The Main colors for White theme used in Options > Theme > White. - */ -function mainColorsWhiteTheme() { - // * MAIN COLORS * // - col.bg = pref.styleBevel ? RGB(255, 255, 255) : RGB(245, 245, 245); - col.loadingThemeBg = pref.styleBlackAndWhite ? RGB(230, 230, 230) : pref.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGB(245, 245, 245); - col.uiHacksFrame = - pref.styleBlackAndWhite ? pref.styleBevel ? RGB(255, 255, 255) : RGB(230, 230, 230) : - pref.styleBlackAndWhite2 ? pref.styleBevel ? RGB(50, 50, 50) : RGB(25, 25, 25) : - RGB(245, 245, 245); - col.shadow = pref.styleBlackAndWhite2 ? RGBA(0, 0, 0, 240) : RGBA(0, 0, 0, 25); - col.discArtShadow = pref.styleBlackAndWhite2 ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 10); - col.noAlbumArtStub = pref.styleBlend || pref.styleBlend2 ? RGB(80, 80, 80) : RGB(120, 120, 120); - col.lowerBarArtist = pref.styleBlend || pref.styleBlend2 ? RGB(80, 80, 80) : RGB(120, 120, 120); - col.lowerBarTitle = pref.styleBlend || pref.styleBlend2 ? RGB(80, 80, 80) : RGB(120, 120, 120); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = pref.lyricsAlbumArt ? RGB(255, 240, 150) : RGB(220, 160, 40); - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = col.primary !== RGB(25, 160, 240) && albumArt && !isStreaming ? col.primary : RGB(255, 255, 255); - col.detailsText = isStreaming || isPlayingCD || !albumArt ? RGB(120, 120, 120) : lightBg ? RGB(55, 55, 55) : RGB(255, 255, 255); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 50 : 40) : col.lightAccent_50; - col.timelinePlayed = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 35 : 25) : col.lightAccent_35; - col.timelineUnplayed = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 20 : 10) : col.lightAccent; - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = pref.styleBlackAndWhite ? RGB(230, 230, 230) : pref.styleBlackAndWhite2 ? RGB(25, 25, 25) : RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = pref.styleBlackAndWhite ? RGB(0, 0, 0) : pref.styleBlackAndWhite2 ? RGB(255, 255, 255) : lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(255, 255, 255) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(250, 250, 250) : RGB(255, 255, 255) : - RGB(255, 255, 255); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(235, 235, 235) : RGB(225, 225, 225) : - pref.styleBevel ? RGB(205, 205, 205) : RGB(220, 220, 220); - - col.menuRectStyleEmbossTop = RGB(255, 255, 255); - col.menuRectStyleEmbossBottom = pref.styleBevel ? RGB(200, 200, 200) : RGB(210, 210, 210); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGB(200, 200, 200) : - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(140, 140, 140) : RGB(150, 150, 150) : - pref.styleAlternative2 ? RGB(190, 190, 190) : - RGB(200, 200, 200); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGB(200, 200, 200) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(200, 200, 200) : RGB(220, 220, 220) : - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(140, 140, 140) : RGB(150, 150, 150) : - pref.styleAlternative2 ? RGB(190, 190, 190) : - RGB(200, 200, 200); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? RGB(80, 80, 80) : RGB(120, 120, 120); - col.menuTextHovered = RGB(80, 80, 80); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = (pref.styleBlend || pref.styleBlend2) && fb.IsPlaying ? RGB(230, 230, 230) : RGB(255, 255, 255); - col.transportEllipseNormal = pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(200, 200, 200) : RGB(210, 210, 210) : RGB(220, 220, 220); - col.transportEllipseHovered = pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(160, 160, 160) : RGB(170, 170, 170) : RGB(180, 180, 180); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(215, 215, 215) : RGB(220, 220, 220) : - pref.styleTransportButtons === 'emboss' ? RGB(225, 225, 225) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(255, 255, 255) : - pref.styleTransportButtons === 'emboss' ? RGB(255, 255, 255) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(190, 190, 190) : RGB(215, 215, 215) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGB(210, 210, 210) : RGB(225, 225, 225) : ''; - - col.transportIconNormal = pref.styleBlend || pref.styleBlend2 || pref.styleTransportButtons === 'minimal' ? RGB(80, 80, 80) : RGB(120, 120, 120); - col.transportIconHovered = pref.styleBlend || pref.styleBlend2 || pref.styleTransportButtons === 'minimal' ? RGB(0, 0, 0) : RGB(60, 60, 60); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGB(245, 245, 245) : RGB(220, 220, 220) : - pref.styleBevel ? pref.styleBlend || pref.styleBlend2 ? RGB(235, 235, 235) : RGB(200, 200, 200) : - (pref.styleBlend || pref.styleBlend2) && fb.IsPlaying && !noAlbumArtStub ? RGB(240, 240, 240) : - RGB(220, 220, 220); - - col.progressBarStreaming = RGB(207, 0, 5); - col.progressBarFrame = pref.styleBevel ? RGB(180, 180, 180) : col.bg; - col.progressBarFill = pref.styleBevel ? ShadeColor(col.primary, 5) : col.primary; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness < 75 ? TintColor(col.primary, 40) : ShadeColor(col.primary, 40); - col.peakmeterBarFillTop = TintColor(col.primary, 10); - col.peakmeterBarFillMiddle = TintColor(col.primary, 30); - col.peakmeterBarFillBack = TintColor(col.primary, 50); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = ShadeColor(col.primary, 10); - col.peakmeterBarVertFillPeaks = TintColor(col.primary, 20); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = col.primary; - col.waveformBarFillBack = ShadeColor(col.primary, 20); - col.waveformBarFillPreFront = pref.styleBevel || pref.styleBlend ? RGB(140, 140, 140) : RGB(180, 180, 180); - col.waveformBarFillPreBack = pref.styleBevel || pref.styleBlend ? RGB(120, 120, 120) : RGB(160, 160, 160); - col.waveformBarIndicator = colBrightness > 200 ? RGB(0, 0, 0) : TintColor(col.primary, 30); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(255, 255, 255); - col.volumeBarFrame = RGB(220, 220, 220); - col.volumeBarFill = col.primary; - - // * STYLE COLORS * // - col.styleBevel = pref.styleBevel && pref.styleBlackAndWhite2 ? RGB(0, 0, 0) : RGB(40, 40, 40); - col.styleGradient = ''; - col.styleGradient2 = ''; - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 40) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(0, 0, 0, 40) : - pref.styleBevel ? RGBA(255, 255, 255, 20) : RGBA(0, 0, 0, 0) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, pref.styleAlternative || pref.styleAlternative2 ? 30 : 50) : RGBA(0, 0, 0, 50) : - pref.styleBevel ? RGBA(0, 0, 0, pref.styleAlternative || pref.styleAlternative2 ? 15 : 20) : RGBA(0, 0, 0, pref.styleAlternative || pref.styleAlternative2 ? 10 : 20) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 160) : pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 140) : RGBA(255, 255, 255, 255) : - pref.styleBevel ? pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 100) : pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 220) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 100) : RGBA(255, 255, 255, 140) : pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 140) : RGBA(255, 255, 255, 255) : - pref.styleBevel ? pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 140) : pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 255) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? RGBA(0, 0, 0, 30) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 40) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; -} - - -//////////////////////////// -// * BLACK THEME COLORS * // -//////////////////////////// -/** - * The default colors for Black theme used in Options > Theme > Black. - */ -const blackTheme = { - name: 'black', - colors: { - primary: RGB(175, 205, 225), - darkAccent: RGB(160, 160, 160), - accent: RGB(180, 180, 180), - lightAccent: RGB(220, 220, 220) - } -}; - - -/** - * The Playlist colors for Black theme used in Options > Theme > Black. - */ -function playlistColorsBlackTheme() { - // * MAIN COLORS * // - g_pl_colors.bg = RGB(20, 20, 20); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(180, 180, 180); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(200, 200, 200) : RGB(240, 240, 240); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(240, 240, 240) : RGB(180, 180, 180); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = colBrightness < 25 ? col.lightAccent : col.primary; - g_pl_colors.header_sideMarker = g_pl_colors.header_nowplaying_bg; - g_pl_colors.header_artist_normal = RGB(220, 220, 220); - g_pl_colors.header_artist_playing = noAlbumArtStub && (pref.styleBlend || pref.styleBlend2) ? RGB(20, 20, 20) : RGB(255, 255, 255); - g_pl_colors.header_album_normal = RGB(200, 200, 200); - g_pl_colors.header_album_playing = noAlbumArtStub && (pref.styleBlend || pref.styleBlend2) ? RGB(20, 20, 20) : RGB(245, 245, 245); - g_pl_colors.header_info_normal = RGB(200, 200, 200); - g_pl_colors.header_info_playing = noAlbumArtStub && (pref.styleBlend || pref.styleBlend2) ? RGB(20, 20, 20) : RGB(245, 245, 245); - g_pl_colors.header_date_normal = RGB(220, 220, 220); - g_pl_colors.header_date_playing = noAlbumArtStub && (pref.styleBlend || pref.styleBlend2) ? RGB(20, 20, 20) : RGB(245, 245, 245); - g_pl_colors.header_line_normal = pref.styleBlend ? RGB(65, 65, 65) : RGB(45, 45, 45); - g_pl_colors.header_line_playing = RGB(25, 25, 25); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(25, 25, 25, 130) : pref.styleAlternative2 ? RGB(35, 35, 35) : RGB(25, 25, 25); - g_pl_colors.row_selection_bg = pref.styleBlend ? RGB(65, 65, 65) : RGB(45, 45, 45); - g_pl_colors.row_selection_frame = g_pl_colors.row_selection_bg; - g_pl_colors.row_sideMarker = colBrightness < 25 ? col.lightAccent_35 : col.primary; - g_pl_colors.row_title_normal = RGB(200, 200, 200); - g_pl_colors.row_title_playing = noAlbumArtStub && (pref.styleBlend || pref.styleBlend2) ? RGB(20, 20, 20) : RGB(245, 245, 245); - g_pl_colors.row_title_selected = RGB(255, 255, 255); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = pref.styleBlend ? RGB(65, 65, 65) : RGB(45, 45, 45); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = RGB(100, 100, 100); - g_pl_colors.sbar_btn_hovered = RGB(160, 160, 160); - g_pl_colors.sbar_thumb_normal = RGB(100, 100, 100); - g_pl_colors.sbar_thumb_hovered = RGB(160, 160, 160); - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for Black theme used in Options > Theme > Black. - */ -function libraryColorsBlackTheme() { - // * MAIN COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = RGB(220, 220, 220); - ui.col.iconPlus_h = RGB(255, 255, 255); - ui.col.iconPlus_sel = RGB(255, 255, 255); - ui.col.iconPlusBg = RGB(45, 45, 45); - ui.col.iconMinus_e = RGB(220, 220, 220); - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(255, 255, 255); - - // * TEXT COLORS * // - ui.col.text = RGB(200, 200, 200); - ui.col.text_h = - pref.styleBlackReborn && ppt.albumArtShow && lightBg ? - ppt.highLightRow === 2 ? RGB(0, 0, 0) : RGB(255, 255, 255) : - RGB(255, 255, 255); - - ui.col.text_nowp = - lightBg ? ppt.albumArtShow && img.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0) : - RGB(255, 255, 255); - - ui.col.textSel = - ppt.albumArtShow ? - lightBg ? img.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0) : RGB(255, 255, 255) : - RGB(255, 255, 255); - - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(200, 200, 200); - ui.col.count = ui.col.text; - ui.col.search = RGB(200, 200, 200); - - // * BUTTON COLORS * // - ui.col.searchBtn = RGB(255, 255, 255); - ui.col.crossBtn = RGB(255, 255, 255); - ui.col.filterBtn = RGB(220, 220, 220); - ui.col.settingsBtn = RGB(220, 220, 220); - ui.col.line = pref.styleBlend ? RGB(60, 60, 60) : RGB(45, 45, 45); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = RGB(100, 100, 100); - ui.col.sbarNormal = RGB(226, 226, 226); - ui.col.sbarHovered = RGB(160, 160, 160); - ui.col.sbarDrag = RGB(160, 160, 160); -} - - -/** - * The Biography colors for Black theme used in Options > Theme > Black. - */ -function biographyColorsBlackTheme() { - // * MAIN COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = - uiBio.blur.blend ? g_pl_colors.header_artist_playing : - uiBio.blur.light ? RGB(65, 65, 65) : - g_pl_colors.header_artist_playing; - - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = RGB(220, 160, 40); - uiBio.col.noPhotoStubBg = RGB(25, 25, 25); - uiBio.col.noPhotoStubText = g_pl_colors.header_artist_playing; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = RGB(100, 100, 100); - uiBio.col.sbarNormal = RGB(226, 226, 226); - uiBio.col.sbarHovered = RGB(160, 160, 160); - uiBio.col.sbarDrag = RGB(160, 160, 160); -} - - -/** - * The Main colors for Black theme used in Options > Theme > Black. - */ -function mainColorsBlackTheme() { - // * MAIN COLORS * // - col.bg = pref.styleBevel ? RGB(40, 40, 40) : RGB(25, 25, 25); - col.loadingThemeBg = RGB(25, 25, 25); - col.uiHacksFrame = pref.styleBlackReborn && fb.IsPlaying && !isStreaming && !isPlayingCD ? col.primary : RGB(35, 35, 35); - col.shadow = - pref.styleBevel && (pref.theme !== 'black' && !pref.styleBlackReborn) ? RGBA(0, 0, 0, 240) : - pref.styleAlternative ? RGBA(0, 0, 0, 100) : - pref.styleAlternative2 ? RGBA(0, 0, 0, 240) : - RGBA(0, 0, 0, 120); - col.discArtShadow = pref.styleBlackReborn ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 40); - col.noAlbumArtStub = isStreaming ? RGB(240, 240, 240) : RGB(175, 205, 225); - col.lowerBarArtist = RGB(240, 240, 240); - col.lowerBarTitle = pref.styleBlend || pref.styleBlend2 ? RGB(220, 220, 220) : RGB(200, 200, 200); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = pref.lyricsAlbumArt ? RGB(255, 240, 150) : RGB(220, 160, 40); - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = col.primary !== RGB(25, 160, 240) && albumArt && !isStreaming ? col.primary : RGB(20, 20, 20); - col.detailsText = isStreaming || isPlayingCD || !albumArt ? RGB(255, 255, 255) : lightBg ? col.darkAccent_75 : !albumArt ? RGB(120, 120, 120) : col.lightAccent_100; - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 50 : 40) : col.lightAccent_50; - col.timelinePlayed = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 35 : 25) : col.lightAccent_35; - col.timelineUnplayed = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 20 : 10) : col.lightAccent; - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = lightBg ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? pref.styleBevel ? RGB(40, 40, 40) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(55, 55, 55) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'emboss' ? RGB(45, 45, 45) : - RGB(35, 35, 35); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'inner' ? RGB(20, 20, 20) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(45, 45, 45) : RGB(50, 50, 50) : - pref.styleBevel ? RGB(30, 30, 30) : RGB(20, 20, 20); - - col.menuRectStyleEmbossTop = pref.styleBevel ? RGB(60, 60, 60) : RGB(70, 70, 70); - col.menuRectStyleEmbossBottom = RGB(0, 0, 0); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGBA(60, 60, 60, 100) : - pref.styleTopMenuButtons === 'bevel' ? RGB(0, 0, 0) : - RGB(60, 60, 60); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(120, 120, 120, 100) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(0, 0, 0) : - RGB(120, 120, 120); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? RGB(200, 200, 200) : RGB(180, 180, 180); - col.menuTextHovered = RGB(255, 255, 255); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = - (pref.styleBlend || pref.styleBlend2) && fb.IsPlaying ? - pref.styleProgressBar === 'bevel' ? RGB(36, 36, 36) : - pref.styleProgressBar === 'inner' ? RGB(37, 37, 37) : - RGB(35, 35, 35) : RGB(35, 35, 35); - col.transportEllipseNormal = RGB(60, 60, 60); - col.transportEllipseHovered = RGB(120, 120, 120); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(20, 20, 20) : - pref.styleTransportButtons === 'emboss' ? RGB(50, 50, 50) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(50, 50, 50) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(10, 10, 10) : - pref.styleTransportButtons === 'emboss' ? RGB(20, 20, 20) : ''; - - col.transportIconNormal = RGB(160, 160, 160); - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = - pref.styleProgressBar === 'bevel' ? RGB(36, 36, 36) : - pref.styleProgressBar === 'inner' ? RGB(37, 37, 37) : - RGB(35, 35, 35); - - col.progressBarStreaming = RGB(207, 0, 5); - col.progressBarFrame = pref.styleBevel ? RGB(0, 0, 0) : col.bg; - col.progressBarFill = colBrightness < 25 ? TintColor(col.primary, 25) : colBrightness < 50 ? col.lightAccent_7 : col.primary; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness > 200 ? ShadeColor(col.primary, 40) : colBrightness < 50 ? TintColor(col.primary, 50) : TintColor(col.primary, 40); - col.peakmeterBarFillTop = colBrightness < 50 ? TintColor(col.primary, 20) : TintColor(col.primary, 10); - col.peakmeterBarFillMiddle = colBrightness < 50 ? TintColor(col.primary, 40) : TintColor(col.primary, 30); - col.peakmeterBarFillBack = colBrightness < 50 ? TintColor(col.primary, 30) : ShadeColor(col.primary, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = colBrightness < 50 ? TintColor(col.primary, 20) : ShadeColor(col.primary, 10); - col.peakmeterBarVertFillPeaks = colBrightness < 50 ? TintColor(col.primary, 30) : TintColor(col.primary, 20); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = colBrightness < 50 ? TintColor(col.primary, 40) : colBrightness < 100 ? TintColor(col.primary, 20) : col.primary; - col.waveformBarFillBack = colBrightness < 50 ? TintColor(col.primary, 20) : colBrightness < 100 ? col.primary : ShadeColor(col.primary, 20); - col.waveformBarFillPreFront = RGB(100, 100, 100); - col.waveformBarFillPreBack = RGB(80, 80, 80); - col.waveformBarIndicator = colBrightness > 200 ? RGB(255, 255, 255) : RGB(220, 220, 220); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(35, 35, 35); - col.volumeBarFrame = RGB(60, 60, 60); - col.volumeBarFill = col.progressBarFill; - - // * STYLE COLORS * // - col.styleBevel = RGB(0, 0, 0); - col.styleGradient = ''; - col.styleGradient2 = ''; - col.styleAlternative = pref.styleBevel ? RGB(40, 40, 40) : RGB(25, 25, 25); - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : - pref.styleProgressBar === 'inner' ? RGBA(0, 0, 0, 100) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? RGBA(0, 0, 0, 255) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 150) : - pref.styleBevel ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 50) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(255, 255, 255, 25) : - pref.styleBevel ? RGBA(255, 255, 255, 25) : RGBA(255, 255, 255, 20) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 20) : RGBA(255, 255, 255, 10) : - pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 15) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; -} - - -//////////////////////////////////// -// * REBORN/RANDOM THEME COLORS * // -//////////////////////////////////// -/** - * The default colors for Reborn theme used in Options > Theme > Reborn. - */ -const rebornTheme = { - name: 'reborn', - colors: { - primary: RGB(90, 90, 90), - primary_alt: RGB(90, 90, 90), - darkAccent: RGB(60, 60, 60), - accent: RGB(80, 80, 80), - lightAccent: RGB(100, 100, 100) - } -}; - - -/** - * The default colors for Random theme used in Options > Theme > Random. - */ -const randomTheme = { - name: 'random', - colors: { - primary: RGB(65, 65, 65), - darkAccent: RGB(60, 60, 60), - accent: RGB(80, 80, 80), - lightAccent: RGB(100, 100, 100) - } -}; - - -/** - * The Playlist colors for Reborn/Random theme used in Options > Theme > Reborn/Random. - */ -function playlistColorsRebornRandomTheme() { - // * MAIN COLORS * // - g_pl_colors.bg = - // * Need this extra condition to overwrite col.primary when switching themes, no album art loaded i.e on startup and going back to Reborn/Random theme. - // * Reborn/Random theme should stay default white and not the defined col.primary dark gray - !fb.IsPlaying || !albumArt || col.primary === RGB(90, 90, 90) && !fb.IsPlaying || col.primary === RGB(25, 160, 240) && !fb.IsPlaying ? RGB(255, 255, 255) : - pref.layout !== 'default' ? col.lightAccent_2 : col.primary; - // * Assigned after background has been initialized - isColored = g_pl_colors.bg !== RGB(255, 255, 255); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(140, 140, 140); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(120, 120, 120) : RGB(80, 80, 80); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(80, 80, 80) : RGB(140, 140, 140); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = - pref.theme === 'reborn' ? isColored ? pref.styleBlend ? RGBtoRGBA(col.lightAccent_10, 130) : col.lightAccent_10 : - col.primary : - pref.theme === 'random' ? isColored ? pref.styleBlend ? RGBtoRGBA(col.lightAccent_10, 130) : lightBg ? ShadeColor(col.primary, 5) : col.lightAccent_10 : - col.primary : ''; - - g_pl_colors.header_sideMarker = isColored ? col.lightAccent_50 : col.primary; - g_pl_colors.header_artist_normal = RGB(120, 120, 120); - g_pl_colors.header_artist_playing = RGB(120, 120, 120); - g_pl_colors.header_album_normal = RGB(120, 120, 120); - g_pl_colors.header_album_playing = RGB(120, 120, 120); - g_pl_colors.header_info_normal = RGB(120, 120, 120); - g_pl_colors.header_info_playing = g_pl_colors.header_info_normal; - g_pl_colors.header_date_normal = RGB(120, 120, 120); - g_pl_colors.header_date_playing = g_pl_colors.header_date_normal; - g_pl_colors.header_line_normal = isColored ? pref.styleBlend ? ShadeColor(col.primary, 24) : col.accent : RGB(200, 200, 200); - g_pl_colors.header_line_playing = isColored ? pref.styleBlend ? ShadeColor(col.primary, 24) : col.accent : RGB(200, 200, 200); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = isColored ? pref.styleBlend ? RGBtoRGBA(col.lightAccent_10, 130) : TintColor(col.primary, pref.styleAlternative2 ? 0 : 5) : RGB(245, 245, 245); - g_pl_colors.row_selection_bg = isColored ? pref.styleBlend ? ShadeColor(col.primary, 24) : col.accent : RGB(200, 200, 200); - g_pl_colors.row_selection_frame = g_pl_colors.row_selection_bg; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(100, 100, 100); - g_pl_colors.row_title_playing = noAlbumArtStub && pref.styleAlternative2 ? RGB(20, 20, 20) : RGB(245, 245, 245); - g_pl_colors.row_title_selected = RGB(0, 0, 0); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = isColored ? pref.styleBlend ? ShadeColor(col.primary, 24) : col.accent : RGB(200, 200, 200); - g_pl_colors.row_drag_line = g_pl_colors.row_sideMarker; - g_pl_colors.row_drag_line_reached = colBrightness > 210 ? ShadeColor(g_pl_colors.row_sideMarker, 25) : TintColor(g_pl_colors.row_sideMarker, 50); - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = RGB(120, 120, 120); - g_pl_colors.sbar_btn_hovered = RGB(0, 0, 0); - g_pl_colors.sbar_thumb_normal = RGB(200, 200, 200); - g_pl_colors.sbar_thumb_hovered = RGB(120, 120, 120); - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; - - // * WHEN PLAYING * // - if (fb.IsPlaying && isColored) { - if (pref.styleGradient || pref.styleGradient2 || pref.styleRebornFusion || pref.styleRebornFusion2 ? lightBgPlaylist : lightBg) { - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : col.darkAccent_75; - g_pl_colors.plman_text_hovered = col.darkAccent_100; - g_pl_colors.plman_text_pressed = col.darkAccent_100; - - // * HEADER COLORS * // - g_pl_colors.header_artist_normal = col.darkAccent_75; - g_pl_colors.header_artist_playing = col.darkAccent_75; - g_pl_colors.header_album_normal = col.darkAccent_75; - g_pl_colors.header_album_playing = col.darkAccent_75; - g_pl_colors.header_info_normal = col.darkAccent_75; - g_pl_colors.header_info_playing = col.darkAccent_75; - g_pl_colors.header_date_normal = col.darkAccent_75; - g_pl_colors.header_date_playing = col.darkAccent_75; - - // * ROW COLORS * // - g_pl_colors.row_title_normal = col.darkAccent_65; - g_pl_colors.row_title_playing = col.darkAccent_100; - g_pl_colors.row_title_selected = col.darkAccent_100; - g_pl_colors.row_title_hovered = col.darkAccent_100; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = col.darkAccent_75; - g_pl_colors.sbar_btn_hovered = col.darkAccent_100; - g_pl_colors.sbar_thumb_normal = col.darkAccent; - g_pl_colors.sbar_thumb_hovered = pref.styleBlend ? col.lightAccent_80 : col.lightAccent_50; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; - } - else { - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : col.lightAccent_80; - g_pl_colors.plman_text_hovered = col.lightAccent_100; - g_pl_colors.plman_text_pressed = col.lightAccent_100; - - // * HEADER COLORS * // - g_pl_colors.header_artist_normal = col.lightAccent_80; - g_pl_colors.header_artist_playing = col.lightAccent_100; - g_pl_colors.header_album_normal = col.lightAccent_80; - g_pl_colors.header_album_playing = col.lightAccent_100; - g_pl_colors.header_info_normal = col.lightAccent_80; - g_pl_colors.header_info_playing = col.lightAccent_100; - g_pl_colors.header_date_normal = col.lightAccent_80; - g_pl_colors.header_date_playing = col.lightAccent_100; - - // * ROW COLORS * // - g_pl_colors.row_title_normal = col.lightAccent_80; - g_pl_colors.row_title_playing = col.lightAccent_100; - g_pl_colors.row_title_selected = col.lightAccent_100; - g_pl_colors.row_title_hovered = col.lightAccent_100; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = col.lightAccent_80; - g_pl_colors.sbar_btn_hovered = col.lightAccent_100; - g_pl_colors.sbar_thumb_normal = col.lightAccent_35; - g_pl_colors.sbar_thumb_hovered = pref.styleBlend ? col.lightAccent_80 : col.lightAccent_50; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; - } - } -} - - -/** - * The Library colors for Reborn/Random theme used in Options > Theme > Reborn/Random. - */ -function libraryColorsRebornRandomTheme() { - // * MAIN COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = ppt.albumArtShow ? TintColor(g_pl_colors.row_nowplaying_bg, 7) : g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = RGB(120, 120, 120); - ui.col.iconPlus_h = RGB(0, 0, 0); - ui.col.iconPlus_sel = !fb.IsPlaying && !ppt.albumArtShow && !pop.highlight.nowPlaying ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.iconPlusBg = RGB(240, 240, 240); - ui.col.iconMinus_e = RGB(120, 120, 120); - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(0, 0, 0); - - // * TEXT COLORS * // - ui.col.text = noAlbumArtStub && ppt.albumArtShow && img.labels.overlayDark ? RGB(220, 220, 220) : RGB(100, 100, 100); - ui.col.text_h = noAlbumArtStub && ppt.albumArtShow && img.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.text_nowp = noAlbumArtStub && ppt.albumArtShow && img.labels.overlayDark || noAlbumArtStub && pref.styleAlternative2 ? RGB(0, 0, 0) : RGB(255, 255, 255); - - ui.col.textSel = - !['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(pref.libraryDesign) || ![2, 1].includes(ppt.albumArtLabelType) || - (['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(pref.libraryDesign) || [2, 1].includes(ppt.albumArtLabelType)) && !pop.highlight.nowPlaying ? - !isColored && !noAlbumArtStub && !ppt.albumArtShow && pop.highlight.nowPlaying || noAlbumArtStub && !ppt.albumArtShow || noAlbumArtStub && ppt.albumArtShow && img.labels.overlayDark || noAlbumArtStub && pref.styleAlternative2 ? - RGB(0, 0, 0) : RGB(255, 255, 255) : - ui.col.text_h; - - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(80, 80, 80); - ui.col.count = ui.col.text; - ui.col.search = RGB(100, 100, 100); - - // * BUTTON COLORS * // - ui.col.searchBtn = RGB(0, 0, 0); - ui.col.crossBtn = RGB(80, 80, 80); - ui.col.filterBtn = RGB(120, 120, 120); - ui.col.settingsBtn = RGB(120, 120, 120); - ui.col.line = isColored ? pref.styleBlend ? ShadeColor(col.primary, 24) : col.accent : RGB(200, 200, 200); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = isColored ? ui.col.text : RGB(120, 120, 120); - ui.col.sbarNormal = RGB(200, 200, 200); - ui.col.sbarHovered = RGB(120, 120, 120); - ui.col.sbarDrag = RGB(120, 120, 120); - - // * WHEN PLAYING * // - if (fb.IsPlaying && isColored) { - if (pref.styleGradient || pref.styleGradient2 || pref.styleRebornFusion || pref.styleRebornFusion2 ? lightBgLibrary : lightBg) { - // * NODE COLORS * // - ui.col.iconPlus = col.darkAccent_75; - ui.col.iconPlus_h = col.darkAccent_100; - ui.col.iconPlus_sel = col.darkAccent_100; - ui.col.iconPlusBg = col.lightAccent_7; - ui.col.iconMinus_e = col.darkAccent_75; - ui.col.iconMinus_h = col.darkAccent_100; - - // * TEXT COLORS * // - ui.col.text = ppt.albumArtShow && img.labels.overlayDark ? RGB(220, 220, 220) : ppt.albumArtShow ? col.darkAccent_75 : col.darkAccent_65; - ui.col.text_h = ppt.albumArtShow && img.labels.overlayDark ? RGB(255, 255, 255) : col.darkAccent_100; - ui.col.text_nowp = col.darkAccent_100; - ui.col.textSel = col.darkAccent_100; - ui.col.txt_box = col.darkAccent_75; - ui.col.search = col.darkAccent_75; - - // * BUTTON COLORS * // - ui.col.searchBtn = col.darkAccent_75; - ui.col.crossBtn = col.darkAccent_75; - ui.col.filterBtn = col.darkAccent_75; - ui.col.settingsBtn = col.darkAccent_75; - ui.col.line = col.accent; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = ui.col.text; - ui.col.sbarNormal = col.darkAccent; - ui.col.sbarHovered = col.lightAccent_50; - ui.col.sbarDrag = col.lightAccent_50; - } - else { - // * NODE COLORS * // - ui.col.iconPlus = col.lightAccent_80; - ui.col.iconPlus_h = col.lightAccent_100; - ui.col.iconPlus_sel = col.lightAccent_100; - ui.col.iconPlusBg = col.lightAccent_7; - ui.col.iconMinus_e = col.lightAccent_80; - ui.col.iconMinus_h = col.lightAccent_100; - - // * TEXT COLORS * // - ui.col.text = ppt.albumArtShow && img.labels.overlayDark ? RGB(220, 220, 220) : col.lightAccent_80; - ui.col.text_h = ppt.albumArtShow && img.labels.overlayDark ? RGB(255, 255, 255) : col.lightAccent_100; - ui.col.text_nowp = col.lightAccent_100; - ui.col.textSel = col.lightAccent_100; - ui.col.txt_box = col.lightAccent_80; - ui.col.search = col.lightAccent_80; - - // * BUTTON COLORS * // - ui.col.searchBtn = col.lightAccent_80; - ui.col.crossBtn = col.lightAccent_80; - ui.col.filterBtn = col.lightAccent_80; - ui.col.settingsBtn = col.lightAccent_80; - ui.col.line = col.accent; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = ui.col.text; - ui.col.sbarNormal = col.lightAccent; - ui.col.sbarHovered = col.lightAccent_50; - ui.col.sbarDrag = col.lightAccent_50; - } - } -} - - -/** - * The Biography colors for Reborn/Random theme used in Options > Theme > Reborn/Random. - */ -function biographyColorsRebornRandomTheme() { - // * MAIN COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = g_pl_colors.header_artist_playing; - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = (lightBgBiography || lightBg) && !noAlbumArtStub ? col.darkAccent_75 : col.lightAccent_100; - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = (lightBgBiography || lightBg) && ColorDistance(RGB(255, 240, 150), uiBio.col.bg, true) < 200 ? RGB(220, 160, 40) : RGB(255, 240, 150); - uiBio.col.noPhotoStubBg = isColored ? col.lightAccent_7 : RGB(245, 245, 245); - uiBio.col.noPhotoStubText = g_pl_colors.header_artist_playing; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = isColored ? uiBio.col.text : RGB(120, 120, 120); - uiBio.col.sbarNormal = RGB(200, 200, 200); - uiBio.col.sbarHovered = RGB(120, 120, 120); - uiBio.col.sbarDrag = RGB(120, 120, 120); - - // * WHEN PLAYING * // - if (fb.IsPlaying && isColored) { - if (pref.styleGradient || pref.styleGradient2 || pref.styleRebornFusion || pref.styleRebornFusion2 ? lightBgBiography : lightBg) { - // * HEADER COLORS * // - uiBio.col.headingText = col.darkAccent_75; - uiBio.col.source = col.darkAccent_75; - uiBio.col.bottomLine = col.darkAccent; - uiBio.col.centerLine = col.darkAccent; - - // * TEXT COLORS * // - uiBio.col.text = col.darkAccent_75; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = - isColored && !uiBio.blur.dark && !uiBio.blur.blend && !uiBio.blur.light ? col.darkAccent_100 : - uiBio.blur.light ? col.darkAccent_100 : - uiBio.blur.dark ? col.lightAccent_100 : - RGB(20, 20, 20); - - uiBio.col.sbarBtns = uiBio.col.text; - uiBio.col.sbarNormal = col.darkAccent; - uiBio.col.sbarHovered = col.lightAccent_50; - uiBio.col.sbarDrag = col.lightAccent_50; - } - else { - // * HEADER COLORS * // - uiBio.col.headingText = uiBio.blur.light ? col.darkAccent_75 : col.lightAccent_80; - uiBio.col.source = uiBio.blur.light ? col.darkAccent_75 : col.lightAccent_80; - uiBio.col.bottomLine = col.darkAccent; - uiBio.col.centerLine = col.darkAccent; - - // * TEXT COLORS * // - uiBio.col.text = uiBio.blur.light ? col.darkAccent_75 : col.lightAccent_80; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = - isColored && !uiBio.blur.dark && !uiBio.blur.blend && !uiBio.blur.light ? col.lightAccent_100 : - uiBio.blur.dark ? col.lightAccent_100 : - uiBio.blur.light ? col.darkAccent_100 : - RGB(220, 220, 220); - - uiBio.col.sbarBtns = uiBio.col.text; - uiBio.col.sbarNormal = col.lightAccent; - uiBio.col.sbarHovered = col.lightAccent_50; - uiBio.col.sbarDrag = col.lightAccent_50; - } - } -} - - -/** - * The Main colors for Reborn/Random theme used in Options > Theme > Reborn/Random. - */ -function mainColorsRebornRandomTheme() { - const nighttime = (pref.styleNighttime || pref.themeDayNightMode && pref.themeDayNightTime === 'night') && !pref.styleRebornWhite; - - // * MAIN COLORS * // - col.bg = isColored ? col.primary : RGB(245, 245, 245); - col.loadingThemeBg = nighttime || pref.styleRebornBlack ? RGB(25, 25, 25) : RGB(245, 245, 245); - col.uiHacksFrame = - pref.styleRebornWhite ? RGB(245, 245, 245) : - pref.styleRebornBlack ? RGB(25, 25, 25) : - isColored ? col.primary : RGB(245, 245, 245); - col.shadow = - pref.styleRebornBlack ? RGBA(0, 0, 0, 255) : - isStreaming || isPlayingCD || noAlbumArtStub || !albumArt ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 35); - col.discArtShadow = RGBA(0, 0, 0, 30); - col.noAlbumArtStub = RGB(120, 120, 120); - col.lowerBarArtist = RGB(120, 120, 120); - col.lowerBarTitle = RGB(120, 120, 120); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = (lightBgMain || lightBg) && ColorDistance(RGB(255, 240, 150), col.bg, true) < 200 ? RGB(220, 160, 40) : RGB(255, 240, 150); - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = col.primary !== RGB(25, 160, 240) && albumArt && !isStreaming ? pref.styleRebornFusion2 ? col.primary_alt : col.primary : RGB(255, 255, 255); - col.detailsText = - isStreaming || isPlayingCD || !albumArt ? RGB(120, 120, 120) : - lightBg || lightBgDetails ? RGB(55, 55, 55) : - RGB(255, 255, 255); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 50 : 40) : col.lightAccent_50; - col.timelinePlayed = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 35 : 25) : col.lightAccent_35; - col.timelineUnplayed = isStreaming ? RGB(207, 0, 5) : lightBg ? ShadeColor(col.primary, pref.styleBlend ? 20 : 10) : col.lightAccent; - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = (lightBgBiography || lightBg) && !noAlbumArtStub ? col.darkAccent_75 : col.lightAccent_100; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - isColored ? - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(255, 255, 255) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(250, 250, 250) : RGB(255, 255, 255) : - col.lightAccent : - RGB(255, 255, 255); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(205, 205, 205) : RGB(220, 220, 220) : - pref.styleRebornBlack ? RGB(20, 20, 20) : - isColored ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 70) : RGBtoRGBA(col.darkAccent_75, 80) : - pref.styleBevel ? RGB(205, 205, 205) : RGB(220, 220, 220) : - pref.styleTopMenuButtons === 'emboss' ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(235, 235, 235) : RGB(225, 225, 225) : - pref.styleRebornBlack ? pref.styleBevel ? RGB(45, 45, 45) : RGB(50, 50, 50) : - isColored ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 30) : RGBtoRGBA(col.darkAccent_75, 40) : - pref.styleBevel ? RGB(235, 235, 235) : RGB(225, 225, 225) : - RGB(255, 255, 255); - - col.menuRectStyleEmbossTop = - isColored ? - pref.styleRebornWhite ? RGB(255, 255, 255) : - pref.styleRebornBlack ? pref.styleBevel ? RGB(60, 60, 60) : RGB(70, 70, 70) : - col.lightAccent : - RGB(255, 255, 255); - - col.menuRectStyleEmbossBottom = - isColored ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(200, 200, 200) : RGB(210, 210, 210) : - pref.styleRebornBlack ? RGB(0, 0, 0) : - col.darkAccent : - pref.styleBevel ? RGB(200, 200, 200) : RGB(210, 210, 210); - - col.menuRectNormal = RGB(140, 140, 140); - col.menuRectHovered = RGB(200, 200, 200); - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = RGB(120, 120, 120); - col.menuTextHovered = RGB(80, 80, 80); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = isColored ? col.lightAccent_50 : RGB(255, 255, 255); - col.transportEllipseNormal = isColored ? col.lightAccent_7 : RGB(220, 220, 220); - col.transportEllipseHovered = RGB(200, 200, 200); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' ? - isColored ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(215, 215, 215) : RGB(220, 220, 220) : - pref.styleRebornBlack ? RGB(20, 20, 20) : - RGBtoRGBA(col.darkAccent_75, 100) : - pref.styleBevel ? RGB(215, 215, 215) : RGB(220, 220, 220) : - pref.styleTransportButtons === 'inner' ? - isColored ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(215, 215, 215) : RGB(220, 220, 220) : - pref.styleRebornBlack ? RGB(20, 20, 20) : - RGBtoRGBA(col.darkAccent_75, 120) : - RGB(225, 225, 225) : - pref.styleTransportButtons === 'emboss' ? - isColored ? - pref.styleRebornWhite ? RGB(225, 225, 225) : - pref.styleRebornBlack ? RGB(50, 50, 50) : - RGBtoRGBA(col.darkAccent_75, 40) : - RGB(225, 225, 225) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? - isColored ? - pref.styleRebornWhite ? RGB(255, 255, 255) : - pref.styleRebornBlack ? RGB(50, 50, 50) : - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 220) : RGBtoRGBA(col.lightAccent_80, 230) : - RGB(255, 255, 255) : - pref.styleTransportButtons === 'emboss' ? - isColored ? - pref.styleRebornWhite ? RGB(255, 255, 255) : - pref.styleRebornBlack ? pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : - RGBtoRGBA(col.lightAccent_80, 100) : '' : - RGB(255, 255, 255); - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' ? - isColored ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(190, 190, 190) : RGB(220, 220, 220) : - pref.styleRebornBlack ? RGB(10, 10, 10) : - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 30) : RGBtoRGBA(col.darkAccent_75, 20) : - pref.styleBevel ? RGB(190, 190, 190) : RGB(220, 220, 220) : - pref.styleTransportButtons === 'inner' ? - isColored ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(190, 190, 190) : RGB(220, 220, 220) : - pref.styleRebornBlack ? RGB(10, 10, 10) : - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 30) : RGBtoRGBA(col.darkAccent_75, 40) : - pref.styleBevel ? RGB(190, 190, 190) : RGB(220, 220, 220) : - pref.styleTransportButtons === 'emboss' ? - isColored ? - pref.styleRebornWhite ? pref.styleBevel ? RGB(210, 210, 210) : RGB(225, 225, 225) : - pref.styleRebornBlack ? RGB(20, 20, 20) : - RGBtoRGBA(col.darkAccent_75, 60) : - pref.styleBevel ? RGB(210, 210, 210) : RGB(225, 225, 225) : ''; - - col.transportIconNormal = RGB(120, 120, 120); - col.transportIconHovered = RGB(80, 80, 80); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = isColored ? pref.styleBevel ? ShadeColor(col.primary, 28) : col.accent : RGB(220, 220, 220); - col.progressBarStreaming = RGB(207, 0, 5); - col.progressBarFrame = col.bg; - col.progressBarFill = isColored ? col.lightAccent_50 : col.primary; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness > 200 || colBrightness < 75 ? TintColor(col.primary, 100) : ShadeColor(col.primary, 100); - col.peakmeterBarFillTop = colBrightness > 200 ? ShadeColor(col.primary, 10) : TintColor(col.primary, 40); - col.peakmeterBarFillMiddle = colBrightness > 200 ? ShadeColor(col.primary, 20) : TintColor(col.primary, 60); - col.peakmeterBarFillBack = colBrightness > 200 ? ShadeColor(col.primary, 40) : TintColor(col.primary, 80); - col.peakmeterBarVertProgFill = colBrightness > 200 ? ShadeColor(col.primary, 50) : col.progressBarFill; - col.peakmeterBarVertFill = colBrightness > 200 ? ShadeColor(col.primary, 50) : TintColor(col.primary, 40); - col.peakmeterBarVertFillPeaks = colBrightness > 200 ? ShadeColor(col.primary, 20) : TintColor(col.primary, 60); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = colBrightness > 200 || noAlbumArtStub ? ShadeColor(col.primary, 80) : TintColor(col.primary, 90); - col.waveformBarFillBack = colBrightness > 200 || noAlbumArtStub ? ShadeColor(col.primary, 40) : TintColor(col.primary, 45); - col.waveformBarFillPreFront = colBrightness > 150 ? ShadeColor(col.primary, pref.styleBevel || pref.styleBlend ? 60 : 40) : TintColor(col.primary, 50); - col.waveformBarFillPreBack = colBrightness > 150 ? ShadeColor(col.primary, pref.styleBevel || pref.styleBlend ? 10 : 20) : TintColor(col.primary, 25); - col.waveformBarIndicator = colBrightness > 200 || noAlbumArtStub ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = isColored ? col.lightAccent_50 : RGB(255, 255, 255); - col.volumeBarFrame = isColored ? col.accent : RGB(220, 220, 220); - col.volumeBarFill = isColored ? col.accent : col.primary; - - // * STYLE COLORS * // - col.styleBevel = pref.styleRebornWhite ? RGB(40, 40, 40) : pref.styleRebornBlack ? RGB(0, 0, 0) : col.darkAccent_100; - col.styleGradient = isColored ? col.darkAccent : ''; - col.styleGradient2 = isColored ? col.darkAccent : ''; - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? - pref.styleRebornWhite ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleRebornBlack ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 50) : RGBtoRGBA(col.darkAccent_75, 40) : - pref.styleProgressBar === 'inner' ? - pref.styleRebornWhite ? pref.styleBevel ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 40) : - pref.styleRebornBlack ? RGBA(0, 0, 0, 100) : - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 50) : RGBtoRGBA(col.darkAccent_75, 60) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? ShadeColor(col.bg, 5) : TintColor(col.bg, 100) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? ShadeColor(col.bg, 10) : ShadeColor(col.bg, 25) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? ShadeColor(col.bg, 10) : ShadeColor(col.bg, 25) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? TintColor(col.bg, 10) : TintColor(col.bg, 100) : ''; - - col.styleProgressBarFill = isColored ? pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 80) : '' : col.primary; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 50) : RGBtoRGBA(col.darkAccent_75, 40) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 50) : RGBtoRGBA(col.darkAccent_75, 60) : ''; - - col.styleVolumeBarFill = isColored ? pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 80) : '' : col.primary; - - // * WHEN PLAYING * // - if (fb.IsPlaying && isColored) { - if (pref.styleGradient || pref.styleGradient2 || pref.styleRebornFusion || pref.styleRebornFusion2 ? lightBgMain : lightBg) { - // * MAIN COLORS * // - col.noAlbumArtStub = RGB(90, 90, 90); - col.lowerBarArtist = col.darkAccent_75; - col.lowerBarTitle = col.darkAccent_75; - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTONS COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 40) : RGBtoRGBA(col.lightAccent_80, 50) : - pref.styleTopMenuButtons !== 'default' ? col.lightAccent_10 : col.lightAccent_50; - - col.menuRectNormal = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 40) : RGBtoRGBA(col.lightAccent_80, 30) : - col.darkAccent; - - col.menuRectHovered = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 40) : RGBtoRGBA(col.darkAccent_75, 30) : - col.darkAccent; - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = col.darkAccent_75; - col.menuTextHovered = col.darkAccent_100; - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportIconNormal = pref.styleTransportButtons === 'emboss' || pref.styleTransportButtons === 'minimal' ? col.darkAccent_75 : col.darkAccent_65; - col.transportIconHovered = col.darkAccent_100; - col.transportIconDown = col.transportIconHovered; - col.transportEllipseNormal = col.accent; - col.transportEllipseHovered = col.darkAccent; - col.transportEllipseDown = col.transportEllipseHovered; - } - else { - // * MAIN * // - col.noAlbumArtStub = RGB(90, 90, 90); - col.lowerBarArtist = col.lightAccent_100; - col.lowerBarTitle = col.lightAccent_100; - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 40) : RGBtoRGBA(col.lightAccent_80, 50) : - pref.styleTopMenuButtons !== 'default' ? col.lightAccent_10 : col.darkAccent_50; - - col.menuRectNormal = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 60) : RGBtoRGBA(col.darkAccent_75, 50) : - col.lightAccent_50; - - col.menuRectHovered = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 60) : RGBtoRGBA(col.darkAccent_75, 50) : - col.lightAccent_50; - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = col.lightAccent_80; - col.menuTextHovered = col.lightAccent_100; - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportIconNormal = pref.styleTransportButtons === 'emboss' ? col.darkAccent_75 : pref.styleTransportButtons === 'minimal' ? col.lightAccent_80 : col.darkAccent_75; - col.transportIconHovered = col.darkAccent_100; - col.transportIconDown = col.transportIconHovered; - col.transportEllipseNormal = col.accent; - col.transportEllipseHovered = col.darkAccent; - col.transportEllipseDown = col.transportEllipseHovered; - } - } -} - - -/////////////////////////// -// * BLUE THEME COLORS * // -/////////////////////////// -/** - * The default colors for Blue theme used in Options > Theme > Blue. - */ -const blueTheme = { - name: 'blue', - colors: { - primary: RGB(10, 115, 200), - darkAccent: RGB(12, 144, 245), - accent: RGB(12, 137, 232), - lightAccent: RGB(10, 130, 220) - } -}; - - -/** - * The Playlist colors for Blue theme used in Options > Theme > Blue. - */ -function playlistColorsBlueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(242, 230, 170); - g_pl_colors.bg = RGB(10, 115, 200); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(220, 220, 220); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(230, 230, 230) : RGB(255, 255, 255); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(255, 255, 255) : RGB(220, 220, 220); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = RGB(10, 130, 220); - g_pl_colors.header_sideMarker = accentColor; - g_pl_colors.header_artist_normal = RGB(240, 240, 240); - g_pl_colors.header_artist_playing = accentColor; - g_pl_colors.header_album_normal = RGB(230, 230, 230); - g_pl_colors.header_album_playing = RGB(245, 245, 245); - g_pl_colors.header_info_normal = RGB(230, 230, 230); - g_pl_colors.header_info_playing = RGB(245, 245, 245); - g_pl_colors.header_date_normal = RGB(240, 240, 240); - g_pl_colors.header_date_playing = RGB(245, 245, 245); - g_pl_colors.header_line_normal = RGB(17, 100, 182); - g_pl_colors.header_line_playing = RGB(17, 100, 182); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(5, 110, 195, 130) : pref.styleAlternative ? RGB(20, 120, 205) : RGB(5, 110, 195); - g_pl_colors.row_selection_bg = RGB(10, 115, 200); - g_pl_colors.row_selection_frame = RGB(10, 135, 230); - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(230, 230, 230); - g_pl_colors.row_title_playing = RGB(255, 255, 255); - g_pl_colors.row_title_selected = RGB(255, 255, 255); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = RGB(17, 100, 182); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = RGB(220, 220, 220); - g_pl_colors.sbar_btn_hovered = RGB(255, 255, 255); - g_pl_colors.sbar_thumb_normal = RGB(10, 135, 225); - g_pl_colors.sbar_thumb_hovered = accentColor; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for Blue theme used in Options > Theme > Blue. - */ -function libraryColorsBlueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(242, 230, 170); - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = accentColor; - ui.col.iconPlus_h = RGB(255, 255, 255); - ui.col.iconPlus_sel = RGB(255, 255, 255); - ui.col.iconPlusBg = RGB(10, 130, 220); - ui.col.iconMinus_e = accentColor; - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(255, 255, 255); - - // * TEXT COLORS * // - ui.col.text = RGB(230, 230, 230); - ui.col.text_h = RGB(255, 255, 255); - ui.col.text_nowp = accentColor; - ui.col.textSel = RGB(255, 255, 255); - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(230, 230, 230); - ui.col.count = ui.col.text; - ui.col.search = RGB(230, 230, 230); - - // * BUTTON COLORS * // - ui.col.searchBtn = accentColor; - ui.col.crossBtn = accentColor; - ui.col.filterBtn = RGB(230, 230, 230); - ui.col.settingsBtn = RGB(230, 230, 230); - ui.col.line = RGB(17, 100, 182); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = RGB(220, 220, 220); - ui.col.sbarNormal = RGB(10, 150, 255); - ui.col.sbarHovered = accentColor; - ui.col.sbarDrag = accentColor; -} - - -/** - * The Biography colors for Blue theme used in Options > Theme > Blue. - */ -function biographyColorsBlueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(242, 230, 170); - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = g_pl_colors.header_artist_playing; - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = accentColor; - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = accentColor; - uiBio.col.noPhotoStubBg = RGB(10, 130, 220); - uiBio.col.noPhotoStubText = g_pl_colors.header_artist_playing; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = RGB(220, 220, 220); - uiBio.col.sbarNormal = RGB(10, 150, 255); - uiBio.col.sbarHovered = accentColor; - uiBio.col.sbarDrag = accentColor; -} - - -/** - * The Main colors for Blue theme used in Options > Theme > Blue. - */ -function mainColorsBlueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(242, 230, 170); - col.bg = RGB(5, 110, 195); - col.loadingThemeBg = RGB(5, 110, 195); - col.uiHacksFrame = RGB(63, 155, 202); - col.shadow = RGBA(0, 0, 0, 25); - col.discArtShadow = RGBA(0, 0, 0, 30); - col.noAlbumArtStub = accentColor; - col.lowerBarArtist = accentColor; - col.lowerBarTitle = RGB(245, 245, 245); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = accentColor; - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = RGB(10, 115, 200); - col.detailsText = RGB(255, 255, 255); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = accentColor; - col.timelinePlayed = RGB(195, 190, 130); - col.timelineUnplayed = RGB(155, 150, 130); - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = accentColor; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? pref.styleBevel && !pref.styleGradient2 ? RGB(10, 123, 209) : RGB(10, 130, 220) : - pref.styleTopMenuButtons === 'inner' ? pref.styleBevel && !pref.styleGradient2 ? RGB(10, 130, 220) : pref.styleGradient2 ? RGB(10, 130, 220) : RGB(10, 135, 230) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel && !pref.styleGradient2 ? RGB(10, 123, 209) : RGB(10, 130, 220) : - RGB(10, 130, 220); - - col.menuStyleBg = pref.styleTopMenuButtons === 'emboss' ? RGB(5, 110, 195) : pref.styleBevel ? RGB(5, 90, 160) : RGB(5, 100, 175); - col.menuRectStyleEmbossTop = RGB(10, 138, 228); - col.menuRectStyleEmbossBottom = RGB(6, 95, 160); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGBA(76, 175, 255, 130) : - pref.styleTopMenuButtons === 'bevel' ? pref.styleBevel ? RGB(5, 85, 150) : RGB(5, 100, 180) : - RGB(76, 175, 255); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(76, 175, 255, 130) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel && !pref.styleGradient2 ? RGB(5, 85, 150) : pref.styleGradient2 ? RGB(4, 68, 120) : RGB(5, 100, 180) : - RGB(76, 175, 255); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = RGB(230, 230, 230); - col.menuTextHovered = RGB(255, 255, 255); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = RGB(10, 130, 220); - col.transportEllipseNormal = RGB(22, 107, 186); - col.transportEllipseHovered = RGB(76, 175, 255); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' ? pref.styleGradient2 ? RGB(5, 90, 160) : RGB(5, 100, 180) : - pref.styleTransportButtons === 'inner' ? pref.styleGradient2 ? RGB(10, 110, 190) : pref.styleBevel && !pref.styleGradient2 ? RGB(5, 100, 180) : RGB(17, 100, 180) : - pref.styleTransportButtons === 'emboss' ? pref.styleGradient2 ? RGB(8, 110, 190) : RGB(11, 132, 224) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(7, 135, 240) : RGB(7, 130, 230) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel && !pref.styleGradient2 ? RGB(12, 138, 235) : pref.styleGradient2 ? RGB(10, 115, 200) : RGB(12, 138, 235) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(5, 85, 150) : RGB(5, 100, 180) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel && !pref.styleGradient2 ? RGB(6, 95, 160) : pref.styleGradient2 ? RGB(4, 68, 120) : RGB(5, 100, 175) : ''; - - col.transportIconNormal = accentColor; - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = RGB(10, 130, 220); - col.progressBarStreaming = accentColor; - col.progressBarFrame = RGB(22, 107, 186); - col.progressBarFill = accentColor; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = TintColor(accentColor, 40); - col.peakmeterBarFillTop = TintColor(accentColor, 10); - col.peakmeterBarFillMiddle = TintColor(accentColor, 20); - col.peakmeterBarFillBack = ShadeColor(accentColor, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = accentColor; - col.peakmeterBarVertFillPeaks = TintColor(accentColor, 80); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = accentColor; - col.waveformBarFillBack = ShadeColor(accentColor, 20); - col.waveformBarFillPreFront = RGB(75, 175, 255); - col.waveformBarFillPreBack = RGB(10, 145, 255); - col.waveformBarIndicator = RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(10, 130, 220); - col.volumeBarFrame = RGB(22, 107, 186); - col.volumeBarFill = accentColor; - - // * STYLE COLORS * // - col.styleBevel = RGB(0, 0, 0); - col.styleGradient = RGBA(0, 0, 0, 90); - col.styleGradient2 = RGB(3, 72, 128); - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(0, 0, 0, 40) : - pref.styleBevel ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 20) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleBevel ? RGBA(0, 0, 0, 45) : RGBA(0, 0, 0, 15) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGB(10, 125, 210) : RGB(10, 130, 220) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGB(10, 130, 220) : RGB(12, 138, 235) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; -} - - -//////////////////////////////// -// * DARK BLUE THEME COLORS * // -//////////////////////////////// -/** - * The default colors for Dark blue theme used in Options > Theme > Dark blue. - */ -const darkblueTheme = { - name: 'darkBlue', - colors: { - primary: RGB(21, 37, 56), - darkAccent: RGB(31, 65, 107), - accent: RGB(27, 58, 94), - lightAccent: RGB(24, 50, 82) - } -}; - - -/** - * The Playlist colors for Dark blue theme used in Options > Theme > Dark blue. - */ -function playlistColorsDarkblueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(255, 202, 128); - g_pl_colors.bg = RGB(21, 37, 56); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(220, 220, 220); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(220, 220, 220) : RGB(255, 255, 255); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(255, 255, 255) : RGB(220, 220, 220); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = RGB(24, 50, 82); - g_pl_colors.header_sideMarker = accentColor; - g_pl_colors.header_artist_normal = RGB(240, 240, 240); - g_pl_colors.header_artist_playing = accentColor; - g_pl_colors.header_album_normal = RGB(220, 220, 220); - g_pl_colors.header_album_playing = RGB(245, 245, 245); - g_pl_colors.header_info_normal = RGB(220, 220, 220); - g_pl_colors.header_info_playing = RGB(245, 245, 245); - g_pl_colors.header_date_normal = RGB(220, 220, 220); - g_pl_colors.header_date_playing = RGB(245, 245, 245); - g_pl_colors.header_line_normal = RGB(12, 21, 31); - g_pl_colors.header_line_playing = RGB(12, 21, 31); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(22, 40, 63, 130) : pref.styleAlternative ? RGB(18, 42, 70) : pref.styleAlternative2 ? RGB(17, 35, 57) : RGB(22, 40, 63); - g_pl_colors.row_selection_bg = RGB(21, 37, 56); - g_pl_colors.row_selection_frame = RGB(27, 55, 90); - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(230, 230, 230); - g_pl_colors.row_title_playing = RGB(255, 255, 255); - g_pl_colors.row_title_selected = RGB(255, 255, 255); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = RGB(12, 21, 31); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = RGB(220, 220, 220); - g_pl_colors.sbar_btn_hovered = RGB(255, 255, 255); - g_pl_colors.sbar_thumb_normal = RGB(27, 55, 90); - g_pl_colors.sbar_thumb_hovered = accentColor; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for Dark blue theme used in Options > Theme > Dark blue. - */ -function libraryColorsDarkblueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(255, 202, 128); - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = accentColor; - ui.col.iconPlus_h = RGB(255, 255, 255); - ui.col.iconPlus_sel = RGB(255, 255, 255); - ui.col.iconPlusBg = RGB(24, 50, 82); - ui.col.iconMinus_e = accentColor; - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(255, 255, 255); - - // * TEXT COLORS * // - ui.col.text = RGB(230, 230, 230); - ui.col.text_h = RGB(255, 255, 255); - ui.col.text_nowp = accentColor; - ui.col.textSel = RGB(255, 255, 255); - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(230, 230, 230); - ui.col.count = ui.col.text; - ui.col.search = RGB(230, 230, 230); - - // * BUTTON COLORS * // - ui.col.searchBtn = accentColor; - ui.col.crossBtn = accentColor; - ui.col.filterBtn = RGB(230, 230, 230); - ui.col.settingsBtn = RGB(230, 230, 230); - ui.col.line = RGB(12, 21, 31); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = RGB(220, 220, 220); - ui.col.sbarNormal = RGB(36, 84, 143); - ui.col.sbarHovered = accentColor; - ui.col.sbarDrag = accentColor; -} - - -/** - * The Biography colors for Dark blue theme used in Options > Theme > Dark blue. - */ -function biographyColorsDarkblueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(255, 202, 128); - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = g_pl_colors.header_artist_playing; - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = accentColor; - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = accentColor; - uiBio.col.noPhotoStubBg = RGB(24, 50, 82); - uiBio.col.noPhotoStubText = g_pl_colors.header_artist_playing; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = RGB(220, 220, 220); - uiBio.col.sbarNormal = RGB(36, 84, 143); - uiBio.col.sbarHovered = accentColor; - uiBio.col.sbarDrag = accentColor; -} - - -/** - * The Main colors for Dark blue theme used in Options > Theme > Dark blue. - */ -function mainColorsDarkblueTheme() { - // * MAIN COLORS * // - const accentColor = RGB(255, 202, 128); - col.bg = RGB(22, 40, 63); - col.loadingThemeBg = RGB(22, 40, 63); - col.uiHacksFrame = RGB(27, 55, 90); - col.shadow = RGBA(0, 0, 0, 75); - col.discArtShadow = RGBA(0, 0, 0, 80); - col.noAlbumArtStub = accentColor; - col.lowerBarArtist = accentColor; - col.lowerBarTitle = RGB(230, 230, 230); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = accentColor; - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = RGB(21, 37, 56); - col.detailsText = RGB(255, 255, 255); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = accentColor; - col.timelinePlayed = RGB(204, 161, 102); - col.timelineUnplayed = RGB(155, 110, 70); - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = accentColor; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? RGB(27, 55, 90) : - pref.styleTopMenuButtons === 'inner' ? RGB(38, 70, 110) : - RGB(27, 55, 90); - - col.menuStyleBg = pref.styleTopMenuButtons === 'emboss' ? RGB(27, 48, 77) : pref.styleBevel ? RGB(22, 40, 60) : RGB(25, 45, 70); - col.menuRectStyleEmbossTop = RGB(35, 70, 115); - col.menuRectStyleEmbossBottom = RGB(6, 10, 15); - col.menuRectNormal = pref.styleTopMenuButtons === 'filled' ? RGBA(200, 200, 200, 140) : RGB(200, 200, 200); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(50, 90, 150, 140) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(15, 25, 40) : RGB(18, 30, 50) : - RGB(50, 90, 150); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = RGB(230, 230, 230); - col.menuTextHovered = RGB(255, 255, 255); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = RGB(27, 55, 90); - col.transportEllipseNormal = RGB(20, 33, 48); - col.transportEllipseHovered = RGB(50, 90, 150); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' ? pref.styleBevel ? RGB(22, 40, 60) : RGB(25, 45, 70) : - pref.styleTransportButtons === 'inner' ? pref.styleGradient2 ? RGB(22, 40, 60) : RGB(25, 45, 70) : - pref.styleTransportButtons === 'emboss' ? RGB(27, 55, 90) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(38, 70, 110) : - pref.styleTransportButtons === 'emboss' ? RGB(35, 70, 115) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(15, 25, 40) : RGB(18, 30, 50) : - pref.styleTransportButtons === 'emboss' ? RGB(20, 36, 50) : ''; - - col.transportIconNormal = accentColor; - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = RGB(27, 55, 90); - col.progressBarStreaming = accentColor; - col.progressBarFrame = RGB(22, 37, 54); - col.progressBarFill = accentColor; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = TintColor(accentColor, 40); - col.peakmeterBarFillTop = TintColor(accentColor, 10); - col.peakmeterBarFillMiddle = TintColor(accentColor, 20); - col.peakmeterBarFillBack = ShadeColor(accentColor, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = accentColor; - col.peakmeterBarVertFillPeaks = TintColor(accentColor, 80); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = accentColor; - col.waveformBarFillBack = ShadeColor(accentColor, 20); - col.waveformBarFillPreFront = RGB(65, 110, 180); - col.waveformBarFillPreBack = RGB(45, 80, 130); - col.waveformBarIndicator = RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(27, 55, 90); - col.volumeBarFrame = RGB(20, 33, 48); - col.volumeBarFill = accentColor; - - // * STYLE COLORS * // - col.styleBevel = RGB(0, 0, 0); - col.styleGradient = RGBA(0, 0, 0, 140); - col.styleGradient2 = RGB(10, 20, 35); - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 60) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 160) : RGBA(0, 0, 0, 80) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 70) : - pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? RGB(30, 62, 102) : - pref.styleProgressBar === 'inner' ? RGB(30, 62, 102) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 60) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; -} - - -////////////////////////// -// * RED THEME COLORS * // -////////////////////////// -/** - * The default colors for Red theme used in Options > Theme > Red. - */ -const redTheme = { - name: 'red', - colors: { - primary: RGB(110, 20, 20), - darkAccent: RGB(156, 30, 30), - accent: RGB(143, 27, 27), - lightAccent: RGB(130, 25, 25) - } -}; - - -/** - * The Playlist colors for Red theme used in Options > Theme > Red. - */ -function playlistColorsRedTheme() { - // * MAIN COLORS * // - const accentColor = RGB(245, 212, 165); - g_pl_colors.bg = RGB(110, 20, 20); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(220, 220, 220); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(220, 220, 220) : RGB(255, 255, 255); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(255, 255, 255) : RGB(220, 220, 220); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = RGB(130, 25, 25); - g_pl_colors.header_sideMarker = accentColor; - g_pl_colors.header_artist_normal = RGB(240, 240, 240); - g_pl_colors.header_artist_playing = accentColor; - g_pl_colors.header_album_normal = RGB(220, 220, 220); - g_pl_colors.header_album_playing = RGB(245, 245, 245); - g_pl_colors.header_info_normal = RGB(220, 220, 220); - g_pl_colors.header_info_playing = RGB(245, 245, 245); - g_pl_colors.header_date_normal = RGB(220, 220, 220); - g_pl_colors.header_date_playing = RGB(245, 245, 245); - g_pl_colors.header_line_normal = RGB(75, 18, 18); - g_pl_colors.header_line_playing = RGB(75, 18, 18); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(100, 20, 20, 130) : pref.styleAlternative ? RGB(130, 25, 25) : RGB(100, 20, 20); - g_pl_colors.row_selection_bg = RGB(110, 20, 20); - g_pl_colors.row_selection_frame = RGB(145, 25, 25); - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(220, 220, 220); - g_pl_colors.row_title_playing = RGB(255, 255, 255); - g_pl_colors.row_title_selected = RGB(255, 255, 255); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = RGB(75, 18, 18); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = RGB(220, 220, 220); - g_pl_colors.sbar_btn_hovered = RGB(255, 255, 255); - g_pl_colors.sbar_thumb_normal = RGB(145, 25, 25); - g_pl_colors.sbar_thumb_hovered = accentColor; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for Red theme used in Options > Theme > Red. - */ -function libraryColorsRedTheme() { - // * MAIN COLORS * // - const accentColor = RGB(245, 212, 165); - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = accentColor; - ui.col.iconPlus_h = RGB(255, 255, 255); - ui.col.iconPlus_sel = RGB(255, 255, 255); - ui.col.iconPlusBg = RGB(140, 25, 25); - ui.col.iconMinus_e = accentColor; - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(255, 255, 255); - - // * TEXT COLORS * // - ui.col.text = RGB(230, 230, 230); - ui.col.text_h = RGB(255, 255, 255); - ui.col.text_nowp = accentColor; - ui.col.textSel = RGB(255, 255, 255); - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(230, 230, 230); - ui.col.count = ui.col.text; - ui.col.search = RGB(230, 230, 230); - - // * BUTTON COLORS * // - ui.col.searchBtn = accentColor; - ui.col.crossBtn = accentColor; - ui.col.filterBtn = RGB(230, 230, 230); - ui.col.settingsBtn = RGB(230, 230, 230); - ui.col.line = RGB(75, 18, 18); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = RGB(220, 220, 220); - ui.col.sbarNormal = RGB(198, 32, 32); - ui.col.sbarHovered = accentColor; - ui.col.sbarDrag = accentColor; -} - - -/** - * The Biography colors for Red theme used in Options > Theme > Red. - */ -function biographyColorsRedTheme() { - // * MAIN COLORS * // - const accentColor = RGB(245, 212, 165); - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = g_pl_colors.header_artist_playing; - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = accentColor; - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = accentColor; - uiBio.col.noPhotoStubBg = RGB(130, 25, 25); - uiBio.col.noPhotoStubText = g_pl_colors.header_artist_playing; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = RGB(220, 220, 220); - uiBio.col.sbarNormal = RGB(198, 32, 32); - uiBio.col.sbarHovered = accentColor; - uiBio.col.sbarDrag = accentColor; -} - - -/** - * The Main colors for Red theme used in Options > Theme > Red. - */ -function mainColorsRedTheme() { - // * MAIN COLORS * // - const accentColor = RGB(245, 212, 165); - col.bg = RGB(100, 20, 20); - col.loadingThemeBg = RGB(100, 20, 20); - col.uiHacksFrame = RGB(125, 0, 0); - col.shadow = RGBA(0, 0, 0, 75); - col.discArtShadow = RGBA(0, 0, 0, 80); - col.noAlbumArtStub = accentColor; - col.lowerBarArtist = accentColor; - col.lowerBarTitle = RGB(220, 220, 220); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = accentColor; - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = RGB(110, 20, 20); - col.detailsText = RGB(255, 255, 255); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = accentColor; - col.timelinePlayed = RGB(207, 170, 118); - col.timelineUnplayed = RGB(170, 120, 95); - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = accentColor; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? RGB(140, 25, 25) : - pref.styleTopMenuButtons === 'inner' ? RGB(160, 32, 32) : - RGB(140, 25, 25); - - col.menuStyleBg = pref.styleTopMenuButtons === 'emboss' ? RGB(125, 25, 25) : RGB(100, 20, 20); - col.menuRectStyleEmbossTop = RGB(158, 30, 30); - col.menuRectStyleEmbossBottom = RGB(54, 10, 10); - col.menuRectNormal = pref.styleTopMenuButtons === 'filled' ? RGBA(200, 200, 200, 140) : RGB(200, 200, 200); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(204, 45, 45, 140) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(66, 13, 13) : RGB(77, 15, 15) : - RGB(204, 45, 45); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = RGB(220, 220, 220); - col.menuTextHovered = RGB(255, 255, 255); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = RGB(140, 25, 25); - col.transportEllipseNormal = RGB(82, 19, 19); - col.transportEllipseHovered = RGB(204, 45, 45); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(100, 20, 20) : - pref.styleTransportButtons === 'emboss' ? RGB(140, 25, 25) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(160, 32, 32) : - pref.styleTransportButtons === 'emboss' ? RGB(166, 30, 30) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(66, 13, 13) : RGB(77, 15, 15) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel && !pref.styleGradient2 ? RGB(80, 15, 15) : pref.styleGradient2 ? RGB(54, 10, 10) : RGB(80, 15, 15) : ''; - - col.transportIconNormal = accentColor; - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = RGB(140, 25, 25); - col.progressBarStreaming = accentColor; - col.progressBarFrame = RGB(92, 21, 21); - col.progressBarFill = accentColor; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = TintColor(accentColor, 40); - col.peakmeterBarFillTop = TintColor(accentColor, 10); - col.peakmeterBarFillMiddle = TintColor(accentColor, 20); - col.peakmeterBarFillBack = ShadeColor(accentColor, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = accentColor; - col.peakmeterBarVertFillPeaks = TintColor(accentColor, 80); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = accentColor; - col.waveformBarFillBack = ShadeColor(accentColor, 20); - col.waveformBarFillPreFront = RGB(230, 45, 45); - col.waveformBarFillPreBack = RGB(180, 35, 35); - col.waveformBarIndicator = RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(140, 25, 25); - col.volumeBarFrame = RGB(82, 19, 19); - col.volumeBarFill = accentColor; - - // * STYLE COLORS * // - col.styleBevel = RGB(0, 0, 0); - col.styleGradient = RGBA(0, 0, 0, 90); - col.styleGradient2 = RGB(65, 13, 13); - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 50) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 70) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 80) : RGBA(0, 0, 0, 60) : - pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? RGB(145, 28, 28) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGB(158, 30, 30) : RGB(145, 28, 28) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 50) : RGBA(0, 0, 0, 40) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 50) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; -} - - -//////////////////////////// -// * CREAM THEME COLORS * // -//////////////////////////// -/** - * The default colors for Cream theme used in Options > Theme > Cream. - */ -const creamTheme = { - name: 'cream', - colors: { - primary: RGB(255, 247, 240), - darkAccent: RGB(120, 170, 130), - accent: RGB(130, 184, 141), - lightAccent: RGB(139, 196, 151) - } -}; - - -/** - * The Playlist colors for Cream theme used in Options > Theme > Cream. - */ -function playlistColorsCreamTheme() { - // * MAIN COLORS * // - const accentColor = RGB(120, 170, 130); - g_pl_colors.bg = RGB(255, 247, 245); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(220, 220, 220); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(110, 110, 110) : RGB(80, 80, 80); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(80, 80, 80) : RGB(130, 130, 130); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - g_pl_colors.header_sideMarker = g_pl_colors.header_nowplaying_bg; - g_pl_colors.header_artist_normal = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - g_pl_colors.header_artist_playing = RGB(255, 255, 255); - g_pl_colors.header_album_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(110, 110, 110); - g_pl_colors.header_album_playing = RGB(245, 245, 245); - g_pl_colors.header_info_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(110, 110, 110); - g_pl_colors.header_info_playing = RGB(245, 245, 245); - g_pl_colors.header_date_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - g_pl_colors.header_date_playing = RGB(245, 245, 245); - g_pl_colors.header_line_normal = RGB(200, 200, 200); - g_pl_colors.header_line_playing = RGB(220, 220, 220); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(255, 255, 255, 130) : pref.styleAlternative2 ? RGB(255, 247, 240) : RGB(255, 255, 255); - g_pl_colors.row_selection_bg = RGB(200, 200, 200); - g_pl_colors.row_selection_frame = g_pl_colors.row_selection_bg; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = pref.styleBlend ? RGB(60, 60, 60) : RGB(90, 90, 90); - g_pl_colors.row_title_playing = RGB(245, 245, 245); - g_pl_colors.row_title_selected = RGB(0, 0, 0); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = RGB(200, 200, 200); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = accentColor; - g_pl_colors.sbar_btn_hovered = RGB(100, 100, 100); - g_pl_colors.sbar_thumb_normal = RGB(200, 200, 200); - g_pl_colors.sbar_thumb_hovered = accentColor; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for Cream theme used in Options > Theme > Cream. - */ -function libraryColorsCreamTheme() { - // * MAIN COLORS * // - const accentColor = RGB(120, 170, 130); - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - ui.col.iconPlus_h = RGB(0, 0, 0); - ui.col.iconPlus_sel = ['modern', 'facet'].includes(pref.libraryDesign) || !pop.highlight.nowPlaying ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.iconPlusBg = RGB(255, 255, 255); - ui.col.iconMinus_e = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(0, 0, 0); - - // * TEXT COLORS * // - ui.col.text = - ppt.albumArtShow && img.labels.overlayDark ? RGB(220, 220, 220) : - pref.styleBlend ? RGB(65, 65, 65) : - RGB(90, 90, 90); - - ui.col.text_h = - ppt.albumArtShow && img.labels.overlayDark ? RGB(255, 255, 255) : - ['modern', 'facet'].includes(pref.libraryDesign) && panel.imgView ? RGB(255, 255, 255) : - RGB(0, 0, 0); - - ui.col.text_nowp = ppt.albumArtShow && img.labels.overlayDark ? RGB(0, 0, 0) : RGB(255, 255, 255); - - ui.col.textSel = - !['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(pref.libraryDesign) || ![2, 1].includes(ppt.albumArtLabelType) || - (['facet', 'coversLabelsRight', 'coversLabelsBottom'].includes(pref.libraryDesign) || [2, 1].includes(ppt.albumArtLabelType)) && !pop.highlight.nowPlaying ? - ppt.albumArtShow && img.labels.overlayDark ? RGB(0, 0, 0) : RGB(255, 255, 255) : - ui.col.text_h; - - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = pref.styleBlend ? RGB(60, 60, 60) : RGB(90, 90, 90); - ui.col.count = ui.col.text; - ui.col.search = pref.styleBlend ? RGB(60, 60, 60) : RGB(90, 90, 90); - - // * BUTTON COLORS * // - ui.col.searchBtn = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - ui.col.crossBtn = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - ui.col.filterBtn = pref.styleBlend ? RGB(60, 60, 60) : RGB(120, 120, 120); - ui.col.settingsBtn = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - ui.col.line = RGB(200, 200, 200); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = accentColor; - ui.col.sbarNormal = RGB(116, 127, 129); - ui.col.sbarHovered = accentColor; - ui.col.sbarDrag = accentColor; -} - - -/** - * The Biography colors for Cream theme used in Options > Theme > Cream. - */ -function biographyColorsCreamTheme() { - // * MAIN COLORS * // - const accentColor = RGB(120, 170, 130); - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = pref.styleBlend ? RGB(60, 60, 60) : RGB(100, 100, 100); - uiBio.col.bottomLine = (uiBio.blur.blend || uiBio.blur.light) ? RGB(120, 120, 120) : g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = RGB(255, 255, 255); - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - uiBio.col.noPhotoStubBg = RGB(255, 247, 240); - uiBio.col.noPhotoStubText = accentColor; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = accentColor; - uiBio.col.sbarNormal = RGB(116, 127, 129); - uiBio.col.sbarHovered = accentColor; - uiBio.col.sbarDrag = accentColor; -} - - -/** - * The Main colors for Cream theme used in Options > Theme > Cream. - */ -function mainColorsCreamTheme() { - // * MAIN COLORS * // - const accentColor = RGB(120, 170, 130); - col.bg = RGB(255, 247, 240); - col.loadingThemeBg = RGB(255, 247, 240); - col.uiHacksFrame = RGB(255, 247, 240); - col.shadow = RGBA(0, 0, 0, 25); - col.discArtShadow = RGBA(0, 0, 0, 10); - col.noAlbumArtStub = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - col.lowerBarArtist = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - col.lowerBarTitle = pref.styleBlend || pref.styleBlend2 ? RGB(90, 90, 90) : RGB(100, 100, 100); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = RGB(255, 247, 245); - col.detailsText = pref.styleBlend ? RGB(80, 80, 80) : RGB(120, 120, 120); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = accentColor; - col.timelinePlayed = RGB(139, 196, 151); - col.timelineUnplayed = RGB(158, 222, 171); - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = RGB(255, 255, 255); - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? RGB(255, 255, 255) : - pref.styleTopMenuButtons === 'inner' ? RGB(255, 255, 255) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(250, 250, 250) : RGB(255, 255, 255) : - RGB(247, 239, 233); - - col.menuStyleBg = pref.styleTopMenuButtons === 'emboss' ? RGB(240, 230, 220) : pref.styleBevel ? RGB(212, 205, 200) : RGB(229, 222, 216); - col.menuRectStyleEmbossTop = pref.styleBevel ? RGB(235, 235, 235) : RGB(255, 255, 255); - col.menuRectStyleEmbossBottom = pref.styleBevel ? RGB(205, 205, 205) : RGB(215, 215, 215); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGB(100, 150, 110, 100) : - pref.styleBlend || pref.styleBlend2 ? RGB(150, 150, 150) : - RGB(100, 150, 110); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(190, 190, 190, 100) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(200, 200, 200) : RGB(220, 220, 220) : - pref.styleBlend || pref.styleBlend2 ? RGB(150, 150, 150) : - RGB(190, 190, 190); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : RGB(100, 150, 110); - col.menuTextHovered = pref.styleBlend ? RGB(60, 60, 60) : RGB(100, 100, 100); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = RGB(255, 255, 255); - col.transportEllipseNormal = RGB(220, 220, 220); - col.transportEllipseHovered = RGB(200, 200, 200); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(212, 205, 200) : RGB(229, 222, 216) : - pref.styleTransportButtons === 'emboss' ? RGB(240, 225, 210) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(255, 255, 255) : - pref.styleTransportButtons === 'emboss' ? RGB(255, 255, 255) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(190, 190, 190) : RGB(220, 220, 220) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGB(210, 210, 210) : RGB(225, 225, 225) : ''; - - col.transportIconNormal = pref.styleBlend || pref.styleBlend2 || pref.styleTransportButtons === 'minimal' ? RGB(65, 135, 80) : RGB(100, 150, 110); - col.transportIconHovered = RGB(100, 100, 100); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = - pref.styleProgressBar === 'bevel' ? RGB(240, 240, 240) : - pref.styleBevel ? RGB(225, 225, 225) : RGB(255, 255, 255); - - col.progressBarStreaming = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - col.progressBarFrame = RGB(230, 230, 230); - col.progressBarFill = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = TintColor(accentColor, 40); - col.peakmeterBarFillTop = TintColor(accentColor, 10); - col.peakmeterBarFillMiddle = TintColor(accentColor, 20); - col.peakmeterBarFillBack = ShadeColor(accentColor, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = accentColor; - col.peakmeterBarVertFillPeaks = ShadeColor(accentColor, 20); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = pref.styleBevel || pref.styleBlend ? TintColor(accentColor, 10) : accentColor; - col.waveformBarFillBack = pref.styleBevel || pref.styleBlend ? ShadeColor(accentColor, 30) : ShadeColor(accentColor, 20); - col.waveformBarFillPreFront = pref.styleBevel || pref.styleBlend ? RGB(205, 200, 190) : RGB(180, 175, 165); - col.waveformBarFillPreBack = pref.styleBevel || pref.styleBlend ? RGB(115, 110, 105) : RGB(140, 135, 130); - col.waveformBarIndicator = RGB(60, 60, 60); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(255, 255, 255); - col.volumeBarFrame = RGB(220, 220, 220); - col.volumeBarFill = pref.styleBlend || pref.styleBlend2 ? RGB(65, 135, 80) : accentColor; - - // * STYLE COLORS * // - col.styleBevel = RGB(0, 0, 0); - col.styleGradient = ''; - col.styleGradient2 = ''; - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 25) : - pref.styleBevel ? RGBA(0, 0, 0, 20) : RGBA(0, 0, 0, 10) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 35) : RGBA(0, 0, 0, 30) : - pref.styleBevel ? RGBA(0, 0, 0, 15) : RGBA(0, 0, 0, 10) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 140) : RGBA(0, 0, 0, 15) : - pref.styleBevel ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 255) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 140) : RGBA(0, 0, 0, 15) : - pref.styleBevel ? RGBA(255, 255, 255, 120) : RGBA(0, 0, 0, 10) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 20) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; -} - - -/////////////////////////// -// * NEON THEME COLORS * // -/////////////////////////// -/** - * The default colors for Neon blue theme used in Options > Theme > Neon blue. - */ -const nblueTheme = { - name: 'neon blue', - colors: { - primary: RGB(10, 10, 10), - darkAccent: RGB(30, 30, 30), - accent: RGB(40, 40, 40), - lightAccent: RGB(50, 50, 50) - } -}; - - -/** - * The default colors for Neon green theme used in Options > Theme > Neon green. - */ -const ngreenTheme = { - name: 'neon green', - colors: { - primary: RGB(10, 10, 10), - darkAccent: RGB(30, 30, 30), - accent: RGB(40, 40, 40), - lightAccent: RGB(50, 50, 50) - } -}; - - -/** - * The default colors for Neon red theme used in Options > Theme > Neon red. - */ -const nredTheme = { - name: 'neon red', - colors: { - primary: RGB(10, 10, 10), - darkAccent: RGB(30, 30, 30), - accent: RGB(40, 40, 40), - lightAccent: RGB(50, 50, 50) - } -}; - - -/** - * The default colors for Neon gold theme used in Options > Theme > Neon gold. - */ -const ngoldTheme = { - name: 'neon gold', - colors: { - primary: RGB(10, 10, 10), - darkAccent: RGB(30, 30, 30), - accent: RGB(40, 40, 40), - lightAccent: RGB(50, 50, 50) - } -}; - - -/** - * The Playlist colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. - */ -function playlistColorsNeonThemes() { - // * MAIN COLORS * // - const accentColor = - pref.theme === 'nblue' ? RGB(0, 200, 255) : - pref.theme === 'ngreen' ? RGB(0, 200, 0) : - pref.theme === 'nred' ? RGB(240, 10, 60) : - pref.theme === 'ngold' ? RGB(255, 205, 5) : ''; - - const accentColorLight = - pref.theme === 'nblue' ? RGB(0, 238, 255) : - pref.theme === 'ngreen' ? RGB(0, 255, 0) : - pref.theme === 'nred' ? RGB(255, 8, 8) : - pref.theme === 'ngold' ? RGB(255, 242, 3) : ''; - - g_pl_colors.bg = RGB(10, 10, 10); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(200, 200, 200); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(220, 220, 220) : RGB(255, 255, 255); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(255, 255, 255) : RGB(200, 200, 200); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = pref.styleBlend || pref.styleBlend2 ? RGBA(25, 25, 25, 100) : RGB(25, 25, 25); - g_pl_colors.header_sideMarker = accentColor; - g_pl_colors.header_artist_normal = RGB(240, 240, 240); - g_pl_colors.header_artist_playing = accentColor; - g_pl_colors.header_album_normal = RGB(220, 220, 220); - g_pl_colors.header_album_playing = RGB(240, 240, 240); - g_pl_colors.header_info_normal = RGB(220, 220, 220); - g_pl_colors.header_info_playing = RGB(240, 240, 240); - g_pl_colors.header_date_normal = RGB(220, 220, 220); - g_pl_colors.header_date_playing = accentColor; - g_pl_colors.header_line_normal = RGB(45, 45, 45); - g_pl_colors.header_line_playing = RGB(50, 50, 50); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(20, 20, 20, 130) : pref.styleAlternative2 ? RGB(35, 35, 35) : RGB(20, 20, 20); - g_pl_colors.row_selection_bg = RGB(10, 10, 10); - g_pl_colors.row_selection_frame = RGB(40, 40, 40); - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(200, 200, 200); - g_pl_colors.row_title_playing = RGB(255, 255, 255); - g_pl_colors.row_title_selected = RGB(255, 255, 255); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = RGB(45, 45, 45); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = accentColor; - g_pl_colors.sbar_btn_hovered = accentColorLight; - g_pl_colors.sbar_thumb_normal = accentColor; - g_pl_colors.sbar_thumb_hovered = accentColorLight; - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; -} - - -/** - * The Library colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. - */ -function libraryColorsNeonThemes() { - // * MAIN COLORS * // - const accentColor = - pref.theme === 'nblue' ? RGB(0, 200, 255) : - pref.theme === 'ngreen' ? RGB(0, 200, 0) : - pref.theme === 'nred' ? RGB(240, 10, 60) : - pref.theme === 'ngold' ? RGB(255, 205, 5) : ''; - - const accentColorLight = - pref.theme === 'nblue' ? RGB(0, 238, 255) : - pref.theme === 'ngreen' ? RGB(0, 255, 0) : - pref.theme === 'nred' ? RGB(255, 8, 8) : - pref.theme === 'ngold' ? RGB(255, 242, 3) : ''; - - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * ROW COLORS * // - ui.col.nowPlayingBg = ppt.albumArtShow ? TintColor(g_pl_colors.row_nowplaying_bg, 7) : g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * NODE COLORS * // - ui.col.iconPlus = g_pl_colors.header_artist_playing; - ui.col.iconPlus_h = RGB(255, 255, 255); - ui.col.iconPlus_sel = RGB(255, 255, 255); - ui.col.iconPlusBg = RGB(45, 45, 45); - ui.col.iconMinus_e = g_pl_colors.header_artist_playing; - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(255, 255, 255); - - // * TEXT COLORS * // - ui.col.text = RGB(200, 200, 200); - ui.col.text_h = RGB(255, 255, 255); - ui.col.text_nowp = g_pl_colors.header_artist_playing; - ui.col.textSel = RGB(255, 255, 255); - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(200, 200, 200); - ui.col.count = ui.col.text; - ui.col.search = RGB(200, 200, 200); - - // * BUTTON COLORS * // - ui.col.searchBtn = g_pl_colors.header_artist_playing; - ui.col.crossBtn = g_pl_colors.header_artist_playing; - ui.col.filterBtn = RGB(200, 200, 200); - ui.col.settingsBtn = g_pl_colors.header_artist_playing; - ui.col.line = RGB(45, 45, 45); - ui.col.s_line = ui.col.line; - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = accentColor; - ui.col.sbarNormal = accentColor; - ui.col.sbarHovered = accentColorLight; - ui.col.sbarDrag = accentColorLight; -} - - -/** - * The Biography colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. - */ -function biographyColorsNeonThemes() { - // * MAIN COLORS * // - const accentColor = - pref.theme === 'nblue' ? RGB(0, 200, 255) : - pref.theme === 'ngreen' ? RGB(0, 200, 0) : - pref.theme === 'nred' ? RGB(240, 10, 60) : - pref.theme === 'ngold' ? RGB(255, 205, 5) : ''; - - const accentColorLight = - pref.theme === 'nblue' ? RGB(0, 238, 255) : - pref.theme === 'ngreen' ? RGB(0, 255, 0) : - pref.theme === 'nred' ? RGB(255, 8, 8) : - pref.theme === 'ngold' ? RGB(255, 242, 3) : ''; - - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * HEADER COLORS * // - uiBio.col.headingText = g_pl_colors.header_artist_playing; - uiBio.col.bottomLine = RGB(55, 55, 55); - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - - // * POPUP COLORS * // - uiBio.col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - uiBio.col.popupText = accentColor; - - // * MISC COLORS * // - uiBio.col.lyricsNormal = uiBio.col.text; - uiBio.col.lyricsHighlight = accentColor; - uiBio.col.noPhotoStubBg = RGB(25, 25, 25); - uiBio.col.noPhotoStubText = g_pl_colors.header_artist_playing; - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = accentColor; - uiBio.col.sbarNormal = accentColor; - uiBio.col.sbarHovered = accentColorLight; - uiBio.col.sbarDrag = accentColorLight; -} - - -/** - * The Main colors for Neon blue/green/red/gold theme used in Options > Theme > Neon blue/green/red/gold. - */ -function mainColorsNeonThemes() { - // * MAIN COLORS * // - const accentColor = - pref.theme === 'nblue' ? RGB(0, 200, 255) : - pref.theme === 'ngreen' ? RGB(0, 200, 0) : - pref.theme === 'nred' ? RGB(240, 10, 60) : - pref.theme === 'ngold' ? RGB(255, 205, 5) : ''; - - const accentColorLight = - pref.theme === 'nblue' ? RGB(0, 238, 255) : - pref.theme === 'ngreen' ? RGB(0, 255, 0) : - pref.theme === 'nred' ? RGB(255, 8, 8) : - pref.theme === 'ngold' ? RGB(255, 242, 3) : ''; - - const accentColorDark = - pref.theme === 'nblue' ? RGB(0, 160, 205) : - pref.theme === 'ngreen' ? RGB(0, 150, 0) : - pref.theme === 'nred' ? RGB(180, 5, 35) : - pref.theme === 'ngold' ? RGB(200, 160, 0) : ''; - - const accentColorDarker = - pref.theme === 'nblue' ? RGB(0, 120, 155) : - pref.theme === 'ngreen' ? RGB(0, 100, 0) : - pref.theme === 'nred' ? RGB(130, 5, 25) : - pref.theme === 'ngold' ? RGB(150, 120, 0) : ''; - - col.bg = pref.styleBevel ? RGB(30, 30, 30) : RGB(20, 20, 20); - col.loadingThemeBg = RGB(20, 20, 20); - col.uiHacksFrame = RGB(30, 30, 30); - col.shadow = RGBA(0, 0, 0, 255); - col.discArtShadow = RGBA(0, 0, 0, 40); - col.noAlbumArtStub = accentColor; - col.lowerBarArtist = accentColor; - col.lowerBarTitle = RGB(220, 220, 220); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - col.lyricsNormal = RGB(255, 255, 255); - col.lyricsHighlight = accentColor; - col.lyricsShadow = RGB(0, 0, 0); - - // * DETAILS COLORS * // - col.detailsBg = pref.styleBlend || pref.styleBlend2 ? RGBA(10, 10, 10, 100) : RGB(10, 10, 10); - col.detailsText = RGB(255, 255, 255); - col.detailsRating = RGB(255, 170, 32); - col.detailsHotness = col.detailsRating; - col.timelineAdded = accentColor; - col.timelinePlayed = accentColorDark; - col.timelineUnplayed = accentColorDarker; - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = RGBAtoRGB(g_pl_colors.header_nowplaying_bg, 255); - col.popupText = accentColor; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? pref.styleBevel ? RGB(40, 40, 40) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(45, 45, 45) : RGB(50, 50, 50) : - RGB(50, 50, 50); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(35, 35, 35) : RGB(40, 40, 40) : - pref.styleBevel ? RGB(25, 25, 25) : RGB(20, 20, 20); - - col.menuRectStyleEmbossTop = RGB(60, 60, 60); - col.menuRectStyleEmbossBottom = RGB(0, 0, 0); - col.menuRectNormal = pref.styleTopMenuButtons === 'filled' ? RGBtoRGBA(accentColor, 80) : accentColor; - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBtoRGBA(accentColorLight, 80) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(0, 0, 0) : - accentColorLight; - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = accentColor; - col.menuTextHovered = accentColorLight; - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = RGB(35, 35, 35); - col.transportEllipseNormal = RGB(50, 50, 50); - col.transportEllipseHovered = accentColorLight; - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(0, 0, 0) : - pref.styleTransportButtons === 'emboss' ? RGB(50, 50, 50) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(50, 50, 50) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(0, 0, 0) : - pref.styleTransportButtons === 'emboss' ? RGB(10, 10, 10) : ''; - - col.transportIconNormal = accentColor; - col.transportIconHovered = accentColorLight; - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = RGB(35, 35, 35); - col.progressBarStreaming = accentColorLight; - col.progressBarFrame = col.bg; - col.progressBarFill = accentColor; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = TintColor(accentColor, 40); - col.peakmeterBarFillTop = TintColor(accentColor, 10); - col.peakmeterBarFillMiddle = TintColor(accentColor, 20); - col.peakmeterBarFillBack = ShadeColor(accentColor, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = accentColor; - col.peakmeterBarVertFillPeaks = TintColor(accentColor, 60); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = accentColor; - col.waveformBarFillBack = ShadeColor(accentColor, 20); - col.waveformBarFillPreFront = RGB(100, 100, 100); - col.waveformBarFillPreBack = RGB(80, 80, 80); - col.waveformBarIndicator = RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(30, 30, 30); - col.volumeBarFrame = RGB(50, 50, 50); - col.volumeBarFill = accentColor; - - // * STYLE COLORS * // - col.styleBevel = RGB(0, 0, 0); - col.styleGradient = ''; - col.styleGradient2 = ''; - col.styleAlternative = pref.styleBevel ? RGB(30, 30, 30) : RGB(20, 20, 20); - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : ''; - - col.styleProgressBarLineTop = RGBA(0, 0, 0, 255); - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(255, 255, 255, 20) : - pref.styleBevel ? RGBA(255, 255, 255, 25) : RGBA(255, 255, 255, 20) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(255, 255, 255, 20) : - pref.styleBevel ? RGBA(255, 255, 255, 35) : RGBA(255, 255, 255, 25) : - RGBA(0, 0, 0, 100); - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 80) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 80) : ''; -} - - -///////////////////////////// -// * CUSTOM THEME COLORS * // -///////////////////////////// -/** - * The default colors for Custom theme used in Options > Theme > Custom. - */ -const customTheme = { - name: 'custom', - colors: { - primary: RGB(50, 25, 70), - darkAccent: RGB(30, 15, 45), - accent: RGB(65, 35, 95), - lightAccent: RGB(75, 40, 110) - } -}; - - -/** - * The Playlist colors for Custom theme used in Options > Theme > Custom. - */ -function playlistColorsCustomTheme() { - try { - // * MAIN COLORS * // - g_pl_colors.bg = HEXtoRGB(customColor.g_pl_colors_bg); - - // * PLAYLIST MANAGER COLORS * // - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? HEXtoRGB(customColor.g_pl_colors_bg) : HEXtoRGB(customColor.g_pl_colors_plman_text_normal); - g_pl_colors.plman_text_hovered = HEXtoRGB(customColor.g_pl_colors_plman_text_hovered); - g_pl_colors.plman_text_pressed = HEXtoRGB(customColor.g_pl_colors_plman_text_pressed); - - // * HEADER COLORS * // - g_pl_colors.header_nowplaying_bg = pref.styleBlend ? HEXtoRGBA(customColor.g_pl_colors_header_nowplaying_bg, 130) : HEXtoRGB(customColor.g_pl_colors_header_nowplaying_bg); - g_pl_colors.header_sideMarker = HEXtoRGB(customColor.g_pl_colors_header_sideMarker); - g_pl_colors.header_artist_normal = HEXtoRGB(customColor.g_pl_colors_header_artist_normal); - g_pl_colors.header_artist_playing = HEXtoRGB(customColor.g_pl_colors_header_artist_playing); - g_pl_colors.header_album_normal = HEXtoRGB(customColor.g_pl_colors_header_album_normal); - g_pl_colors.header_album_playing = HEXtoRGB(customColor.g_pl_colors_header_album_playing); - g_pl_colors.header_info_normal = HEXtoRGB(customColor.g_pl_colors_header_info_normal); - g_pl_colors.header_info_playing = HEXtoRGB(customColor.g_pl_colors_header_info_playing); - g_pl_colors.header_date_normal = HEXtoRGB(customColor.g_pl_colors_header_date_normal); - g_pl_colors.header_date_playing = HEXtoRGB(customColor.g_pl_colors_header_date_playing); - g_pl_colors.header_line_normal = HEXtoRGB(customColor.g_pl_colors_header_line_normal); - g_pl_colors.header_line_playing = HEXtoRGB(customColor.g_pl_colors_header_line_playing); - - // * ROW COLORS * // - g_pl_colors.row_nowplaying_bg = pref.styleBlend ? HEXtoRGBA(customColor.g_pl_colors_row_nowplaying_bg, 130) : HEXtoRGB(customColor.g_pl_colors_row_nowplaying_bg); - g_pl_colors.row_stripes_bg = pref.styleBlend ? HEXtoRGBA(customColor.g_pl_colors_row_stripes_bg, 130) : HEXtoRGB(customColor.g_pl_colors_row_stripes_bg); - g_pl_colors.row_selection_frame = HEXtoRGB(customColor.g_pl_colors_row_selection_frame); - g_pl_colors.row_sideMarker = HEXtoRGB(customColor.g_pl_colors_row_sideMarker); - g_pl_colors.row_title_normal = pref.styleBlend ? ShadeColor(HEXtoRGB(customColor.g_pl_colors_row_title_normal), 10) : HEXtoRGB(customColor.g_pl_colors_row_title_normal); - g_pl_colors.row_title_playing = HEXtoRGB(customColor.g_pl_colors_row_title_playing); - g_pl_colors.row_title_selected = HEXtoRGB(customColor.g_pl_colors_row_title_selected); - g_pl_colors.row_title_hovered = HEXtoRGB(customColor.g_pl_colors_row_title_hovered); - g_pl_colors.row_rating_color = HEXtoRGB(customColor.g_pl_colors_row_rating_color); - g_pl_colors.row_disc_subheader_line = HEXtoRGB(customColor.g_pl_colors_row_disc_subheader_line); - g_pl_colors.row_drag_line = HEXtoRGB(customColor.g_pl_colors_row_drag_line); - g_pl_colors.row_drag_line_reached = HEXtoRGB(customColor.g_pl_colors_row_drag_line_reached); - - // * SCROLLBAR COLORS * // - g_pl_colors.sbar_btn_normal = HEXtoRGB(customColor.g_pl_colors_sbar_btn_normal); - g_pl_colors.sbar_btn_hovered = HEXtoRGB(customColor.g_pl_colors_sbar_btn_hovered); - g_pl_colors.sbar_thumb_normal = HEXtoRGB(customColor.g_pl_colors_sbar_thumb_normal); - g_pl_colors.sbar_thumb_hovered = HEXtoRGB(customColor.g_pl_colors_sbar_thumb_hovered); - g_pl_colors.sbar_thumb_drag = HEXtoRGB(customColor.g_pl_colors_sbar_thumb_drag); - } - catch (e) { - const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; - fb.ShowPopupMessage(`Error when initializing playlist custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${configPathCustom}\n`, 'Playlist custom theme color error'); - } -} - - -/** - * The Library colors for Custom theme used in Options > Theme > Custom. - */ -function libraryColorsCustomTheme() { - try { - // * MAIN COLORS * // - ui.col.bg = pref.styleAlternative ? ShadeColor(HEXtoRGB(customColor.ui_col_bg), 5) : pref.styleAlternative2 ? TintColor(HEXtoRGB(customColor.ui_col_bg), 5) : HEXtoRGB(customColor.ui_col_bg); - ui.col.rowStripes = pref.styleBlend ? HEXtoRGBA(customColor.ui_col_rowStripes, 130) : HEXtoRGB(customColor.ui_col_rowStripes); - - // * ROW COLORS * // - ui.col.nowPlayingBg = pref.styleBlend ? HEXtoRGBA(customColor.ui_col_nowPlayingBg, 130) : HEXtoRGB(customColor.ui_col_nowPlayingBg); - ui.col.sideMarker = HEXtoRGB(customColor.ui_col_sideMarker); - ui.col.selectionFrame = HEXtoRGB(customColor.ui_col_selectionFrame); - ui.col.selectionFrame2 = HEXtoRGB(customColor.ui_col_selectionFrame2); - ui.col.hoverFrame = HEXtoRGB(customColor.ui_col_hoverFrame); - - // * NODE COLORS * // - ui.col.iconPlus = pref.styleBlend ? ShadeColor(HEXtoRGB(customColor.ui_col_iconPlus), 10) : HEXtoRGB(customColor.ui_col_iconPlus); - ui.col.iconPlus_h = HEXtoRGB(customColor.ui_col_iconPlus_h); - ui.col.iconPlus_sel = HEXtoRGB(customColor.ui_col_iconPlus_sel); - ui.col.iconPlusBg = HEXtoRGB(customColor.ui_col_iconPlusBg); - ui.col.iconMinus_e = pref.styleBlend ? ShadeColor(HEXtoRGB(customColor.ui_col_iconMinus_e), 10) : HEXtoRGB(customColor.ui_col_iconMinus_e); - ui.col.iconMinus_c = HEXtoRGB(customColor.ui_col_iconMinus_c); - ui.col.iconMinus_h = HEXtoRGB(customColor.ui_col_iconMinus_h); - - // * TEXT COLORS * // - ui.col.text = ppt.albumArtShow && img.labels.overlayDark ? TintColor(HEXtoRGB(customColor.ui_col_text), 40) : pref.styleBlend ? TintColor(HEXtoRGB(customColor.ui_col_text), 10) : HEXtoRGB(customColor.ui_col_text); - ui.col.text_h = ppt.albumArtShow && img.labels.overlayDark ? TintColor(HEXtoRGB(customColor.ui_col_text), 40) : HEXtoRGB(customColor.ui_col_text_h); - ui.col.text_nowp = HEXtoRGB(customColor.ui_col_text_nowp); - ui.col.textSel = ppt.albumArtShow && ppt.albumArtLabelType === 1 ? ui.col.text_nowp : HEXtoRGB(customColor.ui_col_textSel); - ui.col.txt = HEXtoRGB(customColor.ui_col_txt); - ui.col.txt_h = HEXtoRGB(customColor.ui_col_txt_h); - ui.col.txt_box = pref.styleBlend ? TintColor(HEXtoRGB(customColor.ui_col_txt_box), 10) : HEXtoRGB(customColor.ui_col_txt_box); - ui.col.search = pref.styleBlend ? TintColor(HEXtoRGB(customColor.ui_col_search), 10) : HEXtoRGB(customColor.ui_col_search); - - // * BUTTON COLORS * // - ui.col.searchBtn = HEXtoRGB(customColor.ui_col_searchBtn); - ui.col.crossBtn = pref.styleBlend ? TintColor(HEXtoRGB(customColor.ui_col_crossBtn), 10) : HEXtoRGB(customColor.ui_col_crossBtn); - ui.col.filterBtn = pref.styleBlend ? TintColor(HEXtoRGB(customColor.ui_col_filterBtn), 10) : HEXtoRGB(customColor.ui_col_filterBtn); - ui.col.settingsBtn = pref.styleBlend ? TintColor(HEXtoRGB(customColor.ui_col_settingsBtn), 10) : HEXtoRGB(customColor.ui_col_settingsBtn); - ui.col.line = HEXtoRGB(customColor.ui_col_line); - ui.col.s_line = HEXtoRGB(customColor.ui_col_s_line); - - // * SCROLLBAR COLORS * // - ui.col.sbarBtns = HEXtoRGB(customColor.ui_col_sbarBtns); - ui.col.sbarNormal = HEXtoRGB(customColor.ui_col_sbarNormal); - ui.col.sbarHovered = HEXtoRGB(customColor.ui_col_sbarHovered); - ui.col.sbarDrag = HEXtoRGB(customColor.ui_col_sbarDrag); - } - catch (e) { - const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; - fb.ShowPopupMessage(`Error when initializing library custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${configPathCustom}\n`, 'Library custom theme color error'); - } -} - - -/** - * The Biography colors for Custom theme used in Options > Theme > Custom. - */ -function biographyColorsCustomTheme() { - try { - // * MAIN COLORS * // - uiBio.col.bg = pref.styleAlternative ? ShadeColor(HEXtoRGB(customColor.uiBio_col_bg), 5) : pref.styleAlternative2 ? TintColor(HEXtoRGB(customColor.uiBio_col_bg), 5) : HEXtoRGB(customColor.uiBio_col_bg); - uiBio.col.rowStripes = pref.styleBlend ? HEXtoRGBA(customColor.uiBio_col_rowStripes, 130) : HEXtoRGB(customColor.uiBio_col_rowStripes); - - // * HEADER COLORS * // - uiBio.col.headingText = pref.styleBlend ? TintColor(HEXtoRGB(customColor.uiBio_col_headingText), 10) : HEXtoRGB(customColor.uiBio_col_headingText); - uiBio.col.bottomLine = (uiBio.blur.blend || uiBio.blur.light) ? ShadeColor(HEXtoRGB(customColor.uiBio_col_bottomLine), 25) : HEXtoRGB(customColor.uiBio_col_bottomLine); - uiBio.col.centerLine = HEXtoRGB(customColor.uiBio_col_centerLine); - uiBio.col.sectionLine = HEXtoRGB(customColor.uiBio_col_sectionLine); - - // * TEXT COLORS * // - uiBio.col.text = pref.styleBlend ? TintColor(HEXtoRGB(customColor.uiBio_col_text), 10) : HEXtoRGB(customColor.uiBio_col_text); - uiBio.col.source = pref.styleBlend ? TintColor(HEXtoRGB(customColor.uiBio_col_source), 10) : HEXtoRGB(customColor.uiBio_col_source); - uiBio.col.accent = pref.styleBlend ? TintColor(HEXtoRGB(customColor.uiBio_col_accent), 10) : HEXtoRGB(customColor.uiBio_col_accent); - uiBio.col.summary = pref.styleBlend ? TintColor(HEXtoRGB(customColor.uiBio_col_summary), 10) : HEXtoRGB(customColor.uiBio_col_summary); - - // * POPUP COLORS * // - uiBio.col.popupBg = HEXtoRGB(customColor.col_popupBg); - uiBio.col.popupText = HEXtoRGB(customColor.col_popupText); - - // * MISC COLORS * // - uiBio.col.lyricsNormal = HEXtoRGB(customColor.uiBio_col_lyricsNormal); - uiBio.col.lyricsHighlight = HEXtoRGB(customColor.uiBio_col_lyricsHighlight); - uiBio.col.noPhotoStubBg = HEXtoRGB(customColor.uiBio_col_noPhotoStubBg); - uiBio.col.noPhotoStubText = HEXtoRGB(customColor.uiBio_col_noPhotoStubText); - - // * SCROLLBAR COLORS * // - uiBio.col.sbarBtns = HEXtoRGB(customColor.uiBio_col_sbarBtns); - uiBio.col.sbarNormal = HEXtoRGB(customColor.uiBio_col_sbarNormal); - uiBio.col.sbarHovered = HEXtoRGB(customColor.uiBio_col_sbarHovered); - uiBio.col.sbarDrag = HEXtoRGB(customColor.uiBio_col_sbarDrag); - } - catch (e) { - const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; - fb.ShowPopupMessage(`Error when initializing biography custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${configPathCustom}\n`, 'Biography custom theme color error'); - } -} - - -/** - * The Main colors for Custom theme used in Options > Theme > Custom. - */ -function mainColorsCustomTheme() { - try { - const lightImg = imgBrightness > 180; - const lightBg = new Color(HEXtoRGB(customColor.col_bg)).brightness > 200; - const darkBg = new Color(HEXtoRGB(customColor.col_bg)).brightness < 50; - const nighttime = (pref.styleNighttime || pref.themeDayNightMode && pref.themeDayNightTime === 'night') && !pref.styleRebornWhite; - - // * MAIN COLORS * // - col.bg = pref.styleBevel ? TintColor(HEXtoRGB(customColor.col_bg), lightBgMain ? 80 : 0) : HEXtoRGB(customColor.col_bg); - col.loadingThemeBg = nighttime && customColor.preloaderBg === '' ? RGB(25, 25, 25) : customColor.preloaderBg !== '' ? HEXtoRGB(customColor.preloaderBg) : RGB(245, 245, 245); - col.uiHacksFrame = nighttime ? RGB(25, 25, 25) : col.bg; - col.shadow = HEXtoRGBA(customColor.col_shadow, lightBgMain ? 50 : 75); - col.discArtShadow = HEXtoRGBA(customColor.col_discArtShadow, lightBgMain ? 50 : 75); - col.noAlbumArtStub = HEXtoRGB(customColor.col_noAlbumArtStub); - col.lowerBarArtist = pref.styleBlend || pref.styleBlend2 ? TintColor(HEXtoRGB(customColor.col_lowerBarArtist), 10) : HEXtoRGB(customColor.col_lowerBarArtist); - col.lowerBarTitle = pref.styleBlend || pref.styleBlend2 ? TintColor(HEXtoRGB(customColor.col_lowerBarTitle), 10) : HEXtoRGB(customColor.col_lowerBarTitle); - col.lowerBarTime = HEXtoRGB(customColor.col_lowerBarTime); - col.lowerBarLength = HEXtoRGB(customColor.col_lowerBarLength); - col.lyricsNormal = HEXtoRGB(customColor.col_lyricsNormal); - col.lyricsHighlight = HEXtoRGB(customColor.col_lyricsHighlight); - col.lyricsShadow = HEXtoRGB(customColor.col_lyricsShadow); - - // * DETAILS COLORS * // - col.detailsBg = HEXtoRGB(customColor.col_detailsBg); - col.detailsText = HEXtoRGB(customColor.col_detailsText); - col.detailsRating = HEXtoRGB(customColor.col_detailsRating); - col.detailsHotness = col.detailsRating; - - col.timelineAdded = - isStreaming ? RGB(207, 0, 5) : - lightBgDetails ? ShadeColor(HEXtoRGB(customColor.col_timelinePlayed), pref.styleBlend ? 35 : 0) : - HEXtoRGB(customColor.col_timelineAdded); - - col.timelinePlayed = - isStreaming ? RGB(207, 0, 5) : - lightBgDetails ? ShadeColor(HEXtoRGB(customColor.col_timelinePlayed), pref.styleBlend ? 35 : 0) : - HEXtoRGB(customColor.col_timelinePlayed); - - col.timelineUnplayed = - isStreaming ? RGB(207, 0, 5) : - lightBgDetails ? ShadeColor(HEXtoRGB(customColor.col_timelineUnplayed), pref.styleBlend ? 20 : 0) : - HEXtoRGB(customColor.col_timelineUnplayed); - - col.timelineFrame = HEXtoRGB(customColor.col_timelineFrame); - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = HEXtoRGB(customColor.col_popupBg); - col.popupText = HEXtoRGB(customColor.col_popupText); - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleBlend || pref.styleBlend2 ? - lightBgMain ? ShadeColor(HEXtoRGB(customColor.col_menuBgColor), 10) : TintColor(HEXtoRGB(customColor.col_menuBgColor), 10) : - HEXtoRGB(customColor.col_menuBgColor); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? lightBgMain ? ShadeColor(HEXtoRGB(customColor.col_menuStyleBg), 10) : TintColor(HEXtoRGB(customColor.col_menuStyleBg), 10) : HEXtoRGB(customColor.col_menuStyleBg) : - pref.styleTopMenuButtons === 'emboss' ? - lightBgMain ? ShadeColor(HEXtoRGB(customColor.col_menuStyleBg), 10) : TintColor(HEXtoRGB(customColor.col_menuStyleBg), 10) : ''; - - col.menuRectStyleEmbossTop = HEXtoRGB(customColor.col_menuRectStyleEmbossTop); - col.menuRectStyleEmbossBottom = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_menuRectStyleEmbossBottom), 10) : HEXtoRGB(customColor.col_menuRectStyleEmbossBottom); - - col.menuRectNormal = - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? TintColor(HEXtoRGB(customColor.col_menuRectNormal), 10) : HEXtoRGB(customColor.col_menuRectNormal) : - pref.styleAlternative2 ? TintColor(HEXtoRGB(customColor.col_menuRectNormal), 10) : - HEXtoRGB(customColor.col_menuRectNormal); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_menuRectHovered), 15) : ShadeColor(HEXtoRGB(customColor.col_menuRectHovered), 5) : - pref.styleBlend || pref.styleBlend2 ? - pref.styleBevel ? TintColor(HEXtoRGB(customColor.col_menuRectHovered), 10) : TintColor(HEXtoRGB(customColor.col_menuRectHovered), 5) : - pref.styleAlternative2 ? TintColor(HEXtoRGB(customColor.col_menuRectHovered), 10) : - HEXtoRGB(customColor.col_menuRectHovered); - - col.menuRectDown = HEXtoRGB(customColor.col_menuRectDown); - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? TintColor(HEXtoRGB(customColor.col_menuTextNormal), 10) : HEXtoRGB(customColor.col_menuTextNormal); - col.menuTextHovered = HEXtoRGB(customColor.col_menuTextHovered); - col.menuTextDown = HEXtoRGB(customColor.col_menuTextDown); - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = - pref.styleBlend || pref.styleBlend2 ? - lightImg || lightBgMain ? ShadeColor(HEXtoRGB(customColor.col_transportEllipseBg), 5) : TintColor(HEXtoRGB(customColor.col_transportEllipseBg), 10) : - darkBg && pref.styleTransportButtons === 'emboss' ? TintColor(HEXtoRGB(customColor.col_transportEllipseBg), 15) : HEXtoRGB(customColor.col_transportEllipseBg); - - col.transportEllipseNormal = - pref.styleBlend || pref.styleBlend2 ? - pref.styleBevel ? TintColor(HEXtoRGB(customColor.col_transportEllipseNormal), 5) : TintColor(HEXtoRGB(customColor.col_transportEllipseNormal), 10) : - HEXtoRGB(customColor.col_transportEllipseNormal); - - col.transportEllipseHovered = - pref.styleBlend || pref.styleBlend2 ? - pref.styleBevel ? TintColor(HEXtoRGB(customColor.col_transportEllipseHovered), 5) : TintColor(HEXtoRGB(customColor.col_transportEllipseHovered), 10) : - HEXtoRGB(customColor.col_transportEllipseHovered); - - col.transportEllipseDown = HEXtoRGB(customColor.col_transportEllipseDown); - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? - ShadeColor(HEXtoRGB(customColor.col_transportStyleBg), 0) : - pref.styleTransportButtons === 'emboss' ? - pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_transportStyleBottom), 5) : TintColor(HEXtoRGB(customColor.col_transportStyleBg), 0) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? - TintColor(HEXtoRGB(customColor.col_transportStyleTop), 0) : - pref.styleTransportButtons === 'emboss' ? - lightImg || lightBgMain ? TintColor(HEXtoRGB(customColor.col_transportStyleTop), 0) : - TintColor(HEXtoRGB(customColor.col_transportStyleTop), 15) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? - ShadeColor(HEXtoRGB(customColor.col_transportStyleBottom), 0) : - pref.styleTransportButtons === 'emboss' ? - darkBg ? ShadeColor(HEXtoRGB(customColor.col_transportStyleBottom), 20) : - TintColor(HEXtoRGB(customColor.col_transportStyleBottom), 10) : ''; - - col.transportIconNormal = - pref.styleBlend || pref.styleBlend2 ? TintColor(HEXtoRGB(customColor.col_transportIconNormal), 10) : HEXtoRGB(customColor.col_transportIconNormal); - - col.transportIconHovered = - pref.styleBlend || pref.styleBlend2 ? TintColor(HEXtoRGB(customColor.col_transportIconHovered), 10) : HEXtoRGB(customColor.col_transportIconHovered); - - col.transportIconDown = HEXtoRGB(customColor.col_transportIconDown); - - // * PROGRESS BAR COLORS * // - col.progressBar = - pref.styleProgressBar === 'bevel' ? - pref.styleBlend || pref.styleBlend2 ? lightImg || lightBgMain ? ShadeColor(HEXtoRGB(customColor.col_progressBar), 5) : TintColor(HEXtoRGB(customColor.col_progressBar), 10) : - pref.styleBevel ? HEXtoRGB(customColor.col_progressBar) : TintColor(HEXtoRGB(customColor.col_progressBar), 10) : - pref.styleProgressBar === 'inner' ? - pref.styleBlend || pref.styleBlend2 ? lightImg || lightBgMain ? TintColor(HEXtoRGB(customColor.col_progressBar), 10) : TintColor(HEXtoRGB(customColor.col_progressBar), 5) : - pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_progressBar), 5) : HEXtoRGB(customColor.col_progressBar) : - pref.styleBlend || pref.styleBlend2 ? lightBgMain ? TintColor(HEXtoRGB(customColor.col_progressBar), 10) : TintColor(HEXtoRGB(customColor.col_progressBar), 5) : - pref.styleBevel ? lightBgMain ? ShadeColor(HEXtoRGB(customColor.col_progressBar), 5) : TintColor(HEXtoRGB(customColor.col_progressBar), 5) : - HEXtoRGB(customColor.col_progressBar); - - col.progressBarStreaming = HEXtoRGB(customColor.col_progressBarStreaming); - col.progressBarFrame = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_progressBarFrame), 5) : HEXtoRGB(customColor.col_progressBarFrame); - col.progressBarFill = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_progressBarFill), 5) : HEXtoRGB(customColor.col_progressBarFill); - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarProg), 5) : HEXtoRGB(customColor.col_peakmeterBarProg); - col.peakmeterBarProgFill = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarProgFill), 5) : HEXtoRGB(customColor.col_peakmeterBarProgFill); - col.peakmeterBarFillTop = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarFillTop), 5) : HEXtoRGB(customColor.col_peakmeterBarFillTop); - col.peakmeterBarFillMiddle = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarFillMiddle), 5) : HEXtoRGB(customColor.col_peakmeterBarFillMiddle); - col.peakmeterBarFillBack = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarFillBack), 5) : HEXtoRGB(customColor.col_peakmeterBarFillBack); - col.peakmeterBarVertProgFill = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarVertProgFill), 5) : HEXtoRGB(customColor.col_peakmeterBarVertProgFill); - col.peakmeterBarVertFill = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarVertFill), 5) : HEXtoRGB(customColor.col_peakmeterBarVertFill); - col.peakmeterBarVertFillPeaks = pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_peakmeterBarVertFillPeaks), 5) : HEXtoRGB(customColor.col_peakmeterBarVertFillPeaks); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = lightBg && (pref.styleBevel || pref.styleBlend) ? ShadeColor(HEXtoRGB(customColor.col_waveformBarFillFront), 5) : HEXtoRGB(customColor.col_waveformBarFillFront); - col.waveformBarFillBack = lightBg && (pref.styleBevel || pref.styleBlend) ? ShadeColor(HEXtoRGB(customColor.col_waveformBarFillBack), 5) : HEXtoRGB(customColor.col_waveformBarFillBack); - col.waveformBarFillPreFront = lightBg && (pref.styleBevel || pref.styleBlend) ? ShadeColor(HEXtoRGB(customColor.col_waveformBarFillPreFront), 10) : HEXtoRGB(customColor.col_waveformBarFillPreFront); - col.waveformBarFillPreBack = lightBg && (pref.styleBevel || pref.styleBlend) ? ShadeColor(HEXtoRGB(customColor.col_waveformBarFillPreBack), 5) : HEXtoRGB(customColor.col_waveformBarFillPreBack); - col.waveformBarIndicator = HEXtoRGB(customColor.col_waveformBarIndicator); - - // * VOLUME BAR COLORS * // - col.volumeBar = HEXtoRGB(customColor.col_volumeBar); - - col.volumeBarFrame = - pref.styleVolumeBar === 'bevel' || pref.styleVolumeBar === 'inner' ? TintColor(HEXtoRGB(customColor.col_volumeBarFrame), 5) : - HEXtoRGB(customColor.col_volumeBarFrame); - - col.volumeBarFill = HEXtoRGB(customColor.col_volumeBarFill); - - // * STYLE COLORS * // - col.styleBevel = HEXtoRGB(customColor.col_styleBevel); - col.styleGradient = HEXtoRGB(customColor.col_styleGradient); - col.styleGradient2 = HEXtoRGB(customColor.col_styleGradient2); - - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBar), lightBg ? 5 : 10) : HEXtoRGB(customColor.col_styleProgressBar) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBar), lightBg ? 5 : 10) : HEXtoRGB(customColor.col_styleProgressBar) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBarLineTop), darkBg ? 40 : lightBg ? 0 : 10) : HEXtoRGB(customColor.col_styleProgressBarLineTop) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBarLineTop), darkBg ? 20 : lightBg ? 5 : 10) : HEXtoRGB(customColor.col_styleProgressBarLineTop) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBarLineBottom), darkBg ? 30 : lightBg ? 30 : 20) : HEXtoRGB(customColor.col_styleProgressBarLineBottom) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBarLineBottom), darkBg ? 25 : lightBg ? 15 : 20) : HEXtoRGB(customColor.col_styleProgressBarLineBottom) : ''; - - col.styleProgressBarFill = - pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? ShadeColor(HEXtoRGB(customColor.col_styleProgressBarFill), 15) : - HEXtoRGB(customColor.col_styleProgressBarFill); - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleVolumeBar), 10) : HEXtoRGB(customColor.col_styleVolumeBar) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? ShadeColor(HEXtoRGB(customColor.col_styleVolumeBar), 10) : HEXtoRGB(customColor.col_styleVolumeBar) : ''; - - col.styleVolumeBarFill = - pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? ShadeColor(HEXtoRGB(customColor.col_styleVolumeBarFill), 15) : - HEXtoRGB(customColor.col_styleVolumeBarFill); - } - catch (e) { - const configPathCustom = `${fb.ProfilePath}georgia-reborn\\configs\\georgia-reborn-custom.jsonc`; - fb.ShowPopupMessage(`Error when initializing main custom theme colors:\n\nOne or more variable color names do not exist or have wrong values in your custom config file:\n\n${configPathCustom}\n`, 'Main custom theme color error'); - } -} - - -///////////////////////////////////// -// * WHITE PANEL AND MAIN COLORS * // -///////////////////////////////////// -/** - * Mainly used for style Black and white 2 or theme color adjustments for style Reborn fusion 1 and 2 when panel bg is too light. - * @param {boolean} lighterBg If true, lightens the panel background color. - * @param {Object} [accentColor] If provided, the RGB color object to be used as the accent color. - */ -function panelWhiteColors(lighterBg, accentColor) { - // * PLAYLIST COLORS * // - g_pl_colors.bg = lighterBg ? RGB(255, 255, 255) : RGB(245, 245, 245); - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(180, 180, 180); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(100, 100, 100) : RGB(240, 240, 240); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(100, 100, 100) : RGB(180, 180, 180); - g_pl_colors.header_nowplaying_bg = pref.styleBlend ? lighterBg ? RGBA(245, 245, 245, 130) : RGBA(255, 255, 255, 130) : lighterBg ? RGB(245, 245, 245) : RGB(255, 255, 255); - g_pl_colors.header_sideMarker = isStreaming ? RGB(207, 0, 5) : accentColor || RGB(40, 40, 40); - g_pl_colors.header_artist_normal = RGB(80, 80, 80); - g_pl_colors.header_artist_playing = RGB(60, 60, 60); - g_pl_colors.header_album_normal = RGB(80, 80, 80); - g_pl_colors.header_album_playing = RGB(60, 60, 60); - g_pl_colors.header_info_normal = RGB(60, 60, 60); - g_pl_colors.header_info_playing = RGB(60, 60, 60); - g_pl_colors.header_date_normal = RGB(60, 60, 60); - g_pl_colors.header_date_playing = RGB(60, 60, 60); - g_pl_colors.header_line_normal = pref.styleBlend ? RGB(190, 190, 190) : RGB(215, 215, 215); - g_pl_colors.header_line_playing = pref.styleBlend ? RGB(200, 200, 200) : RGB(215, 215, 215); - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(25, 25, 25, 130) : RGB(25, 25, 25); - g_pl_colors.row_selection_bg = pref.styleBlend ? RGB(190, 190, 190) : RGB(215, 215, 215); - g_pl_colors.row_selection_frame = pref.styleBlend ? RGB(190, 190, 190) : g_pl_colors.row_selection_bg; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(80, 80, 80); - g_pl_colors.row_title_playing = RGB(60, 60, 60); - g_pl_colors.row_title_selected = RGB(0, 0, 0); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = pref.styleBlend ? RGB(190, 190, 190) : RGB(215, 215, 215); - g_pl_colors.row_drag_line = ShadeColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - g_pl_colors.sbar_btn_normal = RGB(100, 100, 100); - g_pl_colors.sbar_btn_hovered = RGB(0, 0, 0); - g_pl_colors.sbar_thumb_normal = RGB(100, 100, 100); - g_pl_colors.sbar_thumb_hovered = RGB(40, 40, 40); - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = isStreaming ? RGB(207, 0, 5) : accentColor || RGB(40, 40, 40); - ui.col.selectionFrame = pref.styleBlend ? RGB(190, 190, 190) : RGB(215, 215, 215); - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - ui.col.iconPlus = RGB(80, 80, 80); - ui.col.iconPlus_h = RGB(0, 0, 0); - ui.col.iconPlus_sel = RGB(0, 0, 0); - ui.col.iconPlusBg = pref.libraryDesign === 'traditional' ? RGB(255, 255, 255) : RGB(45, 45, 45); - ui.col.iconMinus_e = RGB(80, 80, 80); - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(0, 0, 0); - ui.col.text = ppt.albumArtShow && img.labels.overlayDark ? RGB(220, 220, 220) : RGB(80, 80, 80); - ui.col.text_h = ppt.albumArtShow && img.labels.overlayDark ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.text_nowp = RGB(0, 0, 0); - ui.col.textSel = RGB(0, 0, 0); - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(80, 80, 80); - ui.col.count = ui.col.text; - ui.col.search = RGB(80, 80, 80); - ui.col.searchBtn = RGB(0, 0, 0); - ui.col.crossBtn = RGB(0, 0, 0); - ui.col.filterBtn = RGB(80, 80, 80); - ui.col.settingsBtn = RGB(80, 80, 80); - ui.col.line = pref.styleBlend ? RGB(190, 190, 190) : RGB(215, 215, 215); - ui.col.s_line = ui.col.line; - ui.col.sbarBtns = RGB(60, 60, 60); - ui.col.sbarNormal = RGB(0, 0, 0); - ui.col.sbarHovered = RGB(40, 40, 40); - ui.col.sbarDrag = RGB(40, 40, 40); - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.headingText = RGB(60, 60, 60); - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - uiBio.col.sbarBtns = RGB(60, 60, 60); - uiBio.col.sbarNormal = RGB(0, 0, 0); - uiBio.col.sbarHovered = RGB(40, 40, 40); - uiBio.col.sbarDrag = RGB(40, 40, 40); - uiBio.col.noPhotoStubBg = RGB(25, 25, 25); - uiBio.col.noPhotoStubText = pref.theme === 'cream' ? RGB(120, 170, 130) : g_pl_colors.header_artist_playing; - - // * DETAILS COLORS * // - col.detailsBg = g_pl_colors.bg; - col.detailsText = RGB(60, 60, 60); - col.timelineAdded = isStreaming ? RGB(207, 0, 5) : RGB(40, 40, 40); - col.timelinePlayed = isStreaming ? RGB(207, 0, 5) : RGB(80, 80, 80); - col.timelineUnplayed = isStreaming ? RGB(207, 0, 5) : RGB(120, 120, 120); - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = g_pl_colors.row_nowplaying_bg; - col.popupText = g_pl_colors.row_title_playing; -} - - -/** - * Mainly used for style Black and white 1 or theme color adjustments for style Reborn fusion 1 and 2 when main bg is too light. - * @param {boolean} lighterBg If true, lightens the main background color. - */ -function mainWhiteColors(lighterBg) { - // * MAIN COLORS * // - col.bg = lighterBg || pref.styleBevel ? RGB(255, 255, 255) : RGB(230, 230, 230); - col.loadingThemeBg = col.bg; - col.uiHacksFrame = col.bg; - col.noAlbumArtStub = RGB(255, 255, 255); - col.lowerBarArtist = RGB(80, 80, 80); - col.lowerBarTitle = RGB(80, 80, 80); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = RGB(255, 255, 255); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(210, 210, 210) : - pref.styleTopMenuButtons === 'emboss' ? RGB(235, 235, 235) : - pref.styleBevel ? RGB(205, 205, 205) : RGB(220, 220, 220); - - col.menuRectStyleEmbossTop = RGB(255, 255, 255); - col.menuRectStyleEmbossBottom = pref.styleBevel ? RGB(200, 200, 200) : RGB(195, 195, 195); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGB(200, 200, 200) : - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(140, 140, 140) : RGB(150, 150, 150) : - pref.styleBevel ? RGB(170, 170, 170) : RGB(180, 180, 180); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGB(200, 200, 200) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(200, 200, 200) : RGB(205, 205, 205) : - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(140, 140, 140) : RGB(150, 150, 150) : - pref.styleBevel ? RGB(170, 170, 170) : RGB(180, 180, 180); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = RGB(80, 80, 80); - col.menuTextHovered = RGB(40, 40, 40); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = (pref.styleBlend || pref.styleBlend2) && fb.IsPlaying ? RGB(230, 230, 230) : RGB(255, 255, 255); - col.transportEllipseNormal = pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(200, 200, 200) : RGB(210, 210, 210) : RGB(220, 220, 220); - col.transportEllipseHovered = pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(160, 160, 160) : RGB(170, 170, 170) : RGB(180, 180, 180); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' ? pref.styleBevel ? RGB(200, 200, 200) : RGB(205, 205, 205) : - pref.styleTransportButtons === 'inner' ? pref.styleBevel ? RGB(215, 215, 215) : RGB(205, 205, 205) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGB(230, 230, 230) : RGB(215, 215, 215) : - RGB(225, 225, 225); - - col.transportStyleTop = RGB(255, 255, 255); - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? - pref.styleBlend || pref.styleBlend2 ? RGB(180, 180, 180) : pref.styleBevel ? RGB(200, 200, 200) : RGB(220, 220, 220) : - pref.styleTransportButtons === 'emboss' ? - pref.styleBlend || pref.styleBlend2 ? RGB(180, 180, 180) : pref.styleBevel ? RGB(215, 215, 215) : RGB(210, 210, 210) : - RGB(230, 230, 230); - - // * PROGRESS BAR COLORS * // - col.progressBar = - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(205, 205, 205) : RGB(215, 215, 215) : - pref.styleBevel ? RGB(195, 195, 195) : RGB(210, 210, 210); - - col.progressBarFill = RGB(255, 255, 255); - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = RGB(20, 20, 20); - col.peakmeterBarFillTop = RGB(140, 140, 140); - col.peakmeterBarFillMiddle = RGB(20, 20, 20); - col.peakmeterBarFillBack = RGB(80, 80, 80); - col.peakmeterBarVertProgFill = RGB(20, 20, 20); - col.peakmeterBarVertFill = RGB(20, 20, 20); - col.peakmeterBarVertFillPeaks = RGB(120, 120, 120); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = RGB(120, 120, 120); - col.waveformBarFillBack = RGB(20, 20, 20); - col.waveformBarFillPreFront = RGB(160, 160, 160); - col.waveformBarFillPreBack = RGB(120, 120, 120); - col.waveformBarIndicator = RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(255, 255, 255); - col.volumeBarFill = RGB(120, 120, 120); - col.volumeBarFrame = RGB(210, 210, 210); - - // * STYLE COLORS * // - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 40) : RGBA(0, 0, 0, 30) : - pref.styleProgressBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 35) : RGBA(0, 0, 0, 40) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(0, 0, 0, 40) : - pref.styleBevel ? RGBA(255, 255, 255, 20) : RGBA(0, 0, 0, 0) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(0, 0, 0, 50) : - RGBA(0, 0, 0, 20) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 180) : RGBA(255, 255, 255, 255) : - pref.styleBevel ? RGBA(255, 255, 255, 160) : RGBA(255, 255, 255, 220) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 150) : RGBA(255, 255, 255, 255) : - pref.styleBevel ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 170) : ''; - - col.styleProgressBarFill = - pref.styleProgressBarFill === 'bevel' ? RGBA(0, 0, 0, pref.styleBevel ? 40 : 30) : - pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, pref.styleBevel ? 50 : 40) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 25) : RGBA(0, 0, 0, 30) : - pref.styleVolumeBar === 'inner' ? RGBA(0, 0, 0, 30) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(255, 255, 255, 90) : ''; -} - - -///////////////////////////////////// -// * BLACK PANEL AND MAIN COLORS * // -///////////////////////////////////// -/** - * Mainly used for style Black and white 1 or theme color adjustments for style Reborn fusion 1 and 2 when panel bg is too dark. - * @param {boolean} darkerBg If true, darkens the panel background color. - * @param {Object} [accentColor] If provided, the RGB color object to be used as the accent color. - */ -function panelBlackColors(darkerBg, accentColor) { - // * PLAYLIST COLORS * // - g_pl_colors.bg = darkerBg ? RGB(0, 0, 0) : RGB(20, 20, 20); - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = pref.autoHidePlman ? g_pl_colors.bg : RGB(180, 180, 180); - g_pl_colors.plman_text_hovered = pref.autoHidePlman ? RGB(200, 200, 200) : RGB(240, 240, 240); - g_pl_colors.plman_text_pressed = pref.autoHidePlman ? RGB(240, 240, 240) : RGB(180, 180, 180); - g_pl_colors.header_nowplaying_bg = pref.styleBlend ? RGBA(230, 230, 230, 200) : RGB(230, 230, 230); - g_pl_colors.header_sideMarker = isStreaming ? RGB(207, 0, 5) : accentColor || RGB(255, 255, 255); - g_pl_colors.header_artist_normal = RGB(220, 220, 220); - g_pl_colors.header_artist_playing = RGB(25, 25, 25); - g_pl_colors.header_album_normal = RGB(200, 200, 200); - g_pl_colors.header_album_playing = RGB(25, 25, 25); - g_pl_colors.header_info_normal = RGB(200, 200, 200); - g_pl_colors.header_info_playing = RGB(25, 25, 25); - g_pl_colors.header_date_normal = RGB(220, 220, 220); - g_pl_colors.header_date_playing = RGB(25, 25, 25); - g_pl_colors.header_line_normal = pref.styleBlend ? RGB(80, 80, 80) : RGB(45, 45, 45); - g_pl_colors.header_line_playing = RGB(25, 25, 25); - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_stripes_bg = pref.styleBlend ? RGBA(25, 25, 25, 130) : RGB(25, 25, 25); - g_pl_colors.row_selection_bg = pref.styleBlend ? RGB(80, 80, 80) : RGB(45, 45, 45); - g_pl_colors.row_selection_frame = g_pl_colors.row_selection_bg; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_title_normal = RGB(200, 200, 200); - g_pl_colors.row_title_playing = RGB(25, 25, 25); - g_pl_colors.row_title_selected = RGB(255, 255, 255); - g_pl_colors.row_title_hovered = g_pl_colors.row_title_selected; - g_pl_colors.row_rating_color = RGB(255, 190, 0); - g_pl_colors.row_disc_subheader_line = pref.styleBlend ? RGB(80, 80, 80) : RGB(45, 45, 45); - g_pl_colors.row_drag_line = TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - g_pl_colors.sbar_btn_normal = RGB(200, 200, 200); - g_pl_colors.sbar_btn_hovered = RGB(255, 255, 255); - g_pl_colors.sbar_thumb_normal = RGB(180, 180, 180); - g_pl_colors.sbar_thumb_hovered = RGB(255, 255, 255); - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = isStreaming ? RGB(207, 0, 5) : accentColor || RGB(255, 255, 255); - ui.col.selectionFrame = pref.styleBlend ? RGB(80, 80, 80) : RGB(45, 45, 45); - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - ui.col.iconPlus = RGB(220, 220, 220); - ui.col.iconPlus_h = RGB(255, 255, 255); - ui.col.iconPlus_sel = RGB(255, 255, 255); - ui.col.iconPlusBg = RGB(45, 45, 45); - ui.col.iconMinus_e = RGB(220, 220, 220); - ui.col.iconMinus_c = ui.col.iconMinus_e; - ui.col.iconMinus_h = RGB(255, 255, 255); - ui.col.text = RGB(200, 200, 200); - ui.col.text_h = !ppt.albumArtShow || ppt.albumArtShow && ppt.highLightRow !== 2 ? RGB(255, 255, 255) : RGB(0, 0, 0); - ui.col.text_nowp = RGB(0, 0, 0); - ui.col.textSel = - ppt.albumArtShow && !['coversLabelsRight', 'coversLabelsBottom', 'coversLabelsBlend'].includes(pref.libraryDesign) || - pref.libraryDesign === 'traditional' ? RGB(0, 0, 0) : RGB(255, 255, 255); - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = RGB(200, 200, 200); - ui.col.count = ui.col.text; - ui.col.search = RGB(200, 200, 200); - ui.col.searchBtn = RGB(255, 255, 255); - ui.col.crossBtn = RGB(255, 255, 255); - ui.col.filterBtn = RGB(220, 220, 220); - ui.col.settingsBtn = RGB(220, 220, 220); - ui.col.line = pref.styleBlend ? RGB(80, 80, 80) : RGB(45, 45, 45); - ui.col.s_line = ui.col.line; - ui.col.sbarBtns = RGB(200, 200, 200); - ui.col.sbarNormal = RGB(255, 255, 255); - ui.col.sbarHovered = RGB(255, 255, 255); - ui.col.sbarDrag = RGB(255, 255, 255); - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.headingText = RGB(230, 230, 230); - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.text = g_pl_colors.row_title_normal; - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; - uiBio.col.sbarBtns = RGB(200, 200, 200); - uiBio.col.sbarNormal = RGB(255, 255, 255); - uiBio.col.sbarHovered = RGB(255, 255, 255); - uiBio.col.sbarDrag = RGB(255, 255, 255); - uiBio.col.noPhotoStubBg = RGB(25, 25, 25); - uiBio.col.noPhotoStubText = pref.theme === 'cream' ? RGB(120, 170, 130) : g_pl_colors.header_artist_playing; - - // * DETAILS COLORS * // - col.detailsBg = g_pl_colors.bg; - col.detailsText = RGB(220, 220, 220); - col.timelineAdded = isStreaming ? RGB(207, 0, 5) : RGB(230, 230, 230); - col.timelinePlayed = isStreaming ? RGB(207, 0, 5) : RGB(180, 180, 180); - col.timelineUnplayed = isStreaming ? RGB(207, 0, 5) : RGB(160, 160, 160); - col.timelineFrame = col.detailsBg; - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * POPUP COLORS * // - col.popupBg = g_pl_colors.row_nowplaying_bg; - col.popupText = g_pl_colors.row_title_playing; -} - - -/** - * Mainly used for style Black and white 2 or theme color adjustments for style Reborn fusion 1 and 2 when main bg is too dark. - * @param {boolean} darkerBg If true, darkens the main background color. - */ -function mainBlackColors(darkerBg) { - const nighttime = (pref.styleNighttime || pref.themeDayNightMode && pref.themeDayNightTime === 'night') && !pref.styleRebornWhite; - - // * MAIN COLORS * // - col.bg = pref.styleBevel ? darkerBg ? RGB(25, 25, 25) : RGB(50, 50, 50) : darkerBg ? RGB(0, 0, 0) : RGB(25, 25, 25); - col.loadingThemeBg = nighttime ? RGB(25, 25, 25) : col.bg; - col.uiHacksFrame = col.bg; - col.shadow = isPlayingCD ? RGBA(0, 0, 0, 30) : col.shadow; - col.noAlbumArtStub = RGB(40, 40, 40); - col.lowerBarArtist = RGB(240, 240, 240); - col.lowerBarTitle = RGB(220, 220, 220); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTONS COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? pref.styleBevel ? RGB(40, 40, 40) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(55, 55, 55) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'emboss' ? RGB(45, 45, 45) : - RGB(35, 35, 35); - - col.menuStyleBg = - pref.styleTopMenuButtons === 'inner' ? RGB(20, 20, 20) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(45, 45, 45) : RGB(50, 50, 50) : - pref.styleBevel ? RGB(30, 30, 30) : RGB(20, 20, 20); - - col.menuRectStyleEmbossTop = pref.styleBevel ? RGB(60, 60, 60) : RGB(70, 70, 70); - col.menuRectStyleEmbossBottom = RGB(0, 0, 0); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGBA(60, 60, 60, 100) : - pref.styleTopMenuButtons === 'bevel' ? RGB(0, 0, 0) : - RGB(60, 60, 60); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(120, 120, 120, 100) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(0, 0, 0) : - RGB(100, 100, 100); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? RGB(200, 200, 200) : RGB(180, 180, 180); - col.menuTextHovered = RGB(255, 255, 255); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = pref.styleBlend || pref.styleBlend2 ? RGB(60, 60, 60) : RGB(40, 40, 40); - col.transportEllipseNormal = RGB(50, 50, 50); - col.transportEllipseHovered = RGB(100, 100, 100); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(20, 20, 20) : - pref.styleTransportButtons === 'emboss' ? RGB(50, 50, 50) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(50, 50, 50) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(10, 10, 10) : - pref.styleTransportButtons === 'emboss' ? RGB(20, 20, 20) : ''; - - col.transportIconNormal = RGB(200, 200, 200); - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = RGB(50, 50, 50); - col.progressBarFill = RGB(210, 210, 210); - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = RGB(200, 200, 200); - col.peakmeterBarFillTop = RGB(120, 120, 120); - col.peakmeterBarFillMiddle = RGB(160, 160, 160); - col.peakmeterBarFillBack = RGB(80, 80, 80); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = RGB(245, 245, 245); - col.peakmeterBarVertFillPeaks = RGB(200, 200, 200); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = RGB(245, 245, 245); - col.waveformBarFillBack = RGB(200, 200, 200); - col.waveformBarFillPreFront = RGB(160, 160, 160); - col.waveformBarFillPreBack = RGB(120, 120, 120); - col.waveformBarIndicator = RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = col.progressBar; - col.volumeBarFill = col.progressBarFill; - col.volumeBarFrame = RGB(50, 50, 50); - - // * STYLE COLORS * // - col.styleProgressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 100) : RGBA(0, 0, 0, 80) : - pref.styleProgressBar === 'inner' ? RGBA(0, 0, 0, 100) : ''; - - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 60) : RGBA(0, 0, 0, 255) : - RGBA(0, 0, 0, 255) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 150) : - pref.styleBevel ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 100) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 40) : RGBA(255, 255, 255, 30) : - pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 25) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 35) : RGBA(255, 255, 255, 30) : - pref.styleBevel ? RGBA(255, 255, 255, 45) : RGBA(255, 255, 255, 40) : ''; - - col.styleProgressBarFill = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner' ? RGBA(0, 0, 0, 70) : ''; - - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 80) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 70) : RGBA(0, 0, 0, 80) : ''; -} - - -////////////////////////////// -// * LIBRARY THEME COLORS * // -////////////////////////////// -/** - * The Library theme colors used in Options > Library > Theme. - */ -function libraryThemeColors() { - // * SETUP COLORS * // - const colBrightness = new Color(ui.col.bg).brightness; - lightBgLib = - ppt.theme === 1 && imgBrightness > 200 - || - ppt.theme === 2 && (colBrightness > 150 || colBrightness > 75 && imgBrightness > 200) - || - ppt.theme === 3 - || - ppt.theme === 5 && (colBrightness > 150); - - // * GET BLENDED BG IMAGE * // - ui.get = true; - - // * ROW COLORS * // - ui.col.selectionFrame = lightBgLib ? RGBA(0, 0, 0, 100) : RGBA(255, 255, 255, 100); - - // * NODE COLORS * // - ui.col.iconPlus = lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); - ui.col.iconPlus_h = lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); - ui.col.iconPlus_sel = lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * TEXT COLORS * // - ui.col.text = lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); - ui.col.text_h = lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); - ui.col.textSel = ppt.albumArtShow ? ui.col.text_nowp : ui.col.text; - ui.col.txt = ui.col.text; - ui.col.txt_h = ui.col.text_h; - ui.col.txt_box = lightBgLib ? RGB(40, 40, 40) : RGB(220, 220, 220); - ui.col.count = ui.col.text; - - // * BUTTON COLORS * // - ui.col.search = lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); - ui.col.searchBtn = lightBgLib ? RGB(0, 0, 0) : RGB(255, 255, 255); - ui.col.crossBtn = lightBgLib ? RGB(40, 40, 40) : RGB(255, 255, 255); - ui.col.filterBtn = lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); - ui.col.settingsBtn = lightBgLib ? RGB(60, 60, 60) : RGB(220, 220, 220); - ui.col.line = lightBgLib ? RGBA(0, 0, 0, 100) : RGBA(255, 255, 255, 100); - ui.col.s_line = ui.col.line; -} - - -//////////////////////////////// -// * BIOGRAPHY THEME COLORS * // -//////////////////////////////// -/** - * The Biography theme colors used in Options > Biography > Theme. - */ -function biographyThemeColors() { - // * SETUP COLORS * // - const colBrightness = new Color(uiBio.col.bg).brightness; - lightBgBio = - pptBio.theme === 1 && imgBrightness > 200 - || - pptBio.theme === 2 && (colBrightness > 150 || imgBrightness > 200) - || - pptBio.theme === 3 - || - pptBio.theme === 4 && imgBrightness > 150; - - // * MAIN COLORS * // - uiBio.col.rowStripes = RGBtoRGBA(g_pl_colors.row_stripes_bg, 100); - - // * HEADER COLORS * // - uiBio.col.headingText = lightBgBio ? RGB(65, 65, 65) : RGB(230, 230, 230); - uiBio.col.bottomLine = lightBgBio ? RGB(120, 120, 120) : g_pl_colors.header_line_normal; - uiBio.col.centerLine = uiBio.col.bottomLine; - uiBio.col.sectionLine = uiBio.col.bottomLine; - - // * TEXT COLORS * // - uiBio.col.text = lightBgBio ? RGB(60, 60, 60) : RGB(220, 220, 220); - uiBio.col.source = uiBio.col.headingText; - uiBio.col.accent = uiBio.col.headingText; - uiBio.col.summary = uiBio.col.text; -} - - -///////////////////////////////// -// * THEME COLOR ADJUSTMENTS * // -///////////////////////////////// -/** - * Post init color adjustments, used for White, Black, Reborn and Random theme. - */ -function themeColorAdjustments() { - const cBRT = colBrightness; - const cBRT2 = colBrightness2; - const iBRT = imgBrightness; - const bevel = pref.styleBevel; - const blend = pref.styleBlend; - const blend2 = pref.styleBlend2; - const alt = pref.styleAlternative; - const transpBtns = pref.styleTransportButtons; - const progBar = pref.styleProgressBar; - const progBarFillBevelInner = pref.styleProgressBarFill === 'bevel' || pref.styleProgressBarFill === 'inner'; - - // * WHITE THEME/REBORN WHITE WITH STYLE BLEND - dynamically adjust transport buttons styles - if (((pref.theme === 'white' && !pref.styleBlackAndWhite2 || pref.styleRebornWhite) && (blend || blend2)) && fb.IsPlaying && !isStreaming && !isPlayingCD) { - switch (true) { - case ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) > 500: col.transportStyleBottom = RGB(175, 175, 175); break; - case ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) > 300: col.transportStyleBottom = RGB(180, 180, 180); break; - case ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) > 150: col.transportStyleBottom = RGB(185, 185, 185); break; - case ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) > 75: col.transportStyleBottom = RGB(190, 190, 190); break; - case ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) > 50: col.transportStyleBottom = RGB(195, 195, 195); break; - case ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) >= 0: col.transportStyleBottom = RGB(200, 200, 200); break; - } - } - - // * WHITE THEME/REBORN WHITE WITH STYLE BLEND - dynamically adjust progress bar background color - if ((pref.theme === 'white' || pref.styleRebornWhite) && (blend || blend2) && fb.IsPlaying) { - if (ColorDistance(RGB(iBRT, iBRT, iBRT), col.bg, true) < 180) { - if (settings.showDebugThemeLog) console.log('>>> Blended album art image is too close to col.bg. Adjusting progress bar'); - col.progressBar = bevel ? TintColor(col.progressBar, 10) : ShadeColor(col.progressBar, 10); - } - if (!pref.styleBlackAndWhiteReborn && ColorDistance(col.progressBarFill, col.progressBar, true) < 150) { - if (settings.showDebugThemeLog) console.log('>>> Progress bar fill color is too close to progress bar background. Adjusting progress bar fill'); - col.progressBarFill = bevel ? ShadeColor(col.progressBarFill, 20) : ShadeColor(col.progressBarFill, 10); - } - } - - //////////////////////////// - // * STYLE BLACK REBORN * // - //////////////////////////// - if (pref.styleBlackReborn && fb.IsPlaying && !isStreaming && !isPlayingCD && !noAlbumArtStub) { - // * PLAYLIST COLORS * // - g_pl_colors.header_nowplaying_bg = - cBRT > 200 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 175 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 150 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 125 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 100 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 75 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 50 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT > 25 ? blend ? RGBtoRGBA(col.primary, bevel ? 180 : 200) : col.primary : - cBRT >= 0 ? cBRT < 10 ? TintColor(col.primary, blend || blend2 ? 15 : 10) : - TintColor(col.primary, blend || blend2 ? 15 : 5) : ''; - - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - - // * MAIN COLORS * // - col.transportEllipseBg = - cBRT > 150 ? transpBtns === 'emboss' ? TintColor(col.transportEllipseBg, 10) : col.transportEllipseBg : - col.transportEllipseBg; - - col.transportStyleTop = - cBRT > 200 ? transpBtns === 'emboss' ? TintColor(col.transportStyleTop, 30) : TintColor(col.transportStyleTop, 10) : - cBRT > 175 ? transpBtns === 'emboss' ? TintColor(col.transportStyleTop, 30) : TintColor(col.transportStyleTop, 10) : - cBRT > 150 ? transpBtns === 'emboss' ? TintColor(col.transportStyleTop, 30) : TintColor(col.transportStyleTop, 10) : - cBRT > 125 ? transpBtns === 'emboss' ? TintColor(col.transportStyleTop, 30) : TintColor(col.transportStyleTop, 15) : - cBRT > 100 ? transpBtns === 'emboss' ? TintColor(col.transportStyleTop, 40) : ShadeColor(col.transportStyleTop, 10) : - cBRT > 75 ? transpBtns === 'emboss' ? TintColor(col.transportStyleTop, 40) : ShadeColor(col.transportStyleTop, 8) : - cBRT > 50 ? transpBtns === 'emboss' ? ShadeColor(col.transportStyleTop, 10) : TintColor(col.transportStyleTop, 6) : - cBRT > 25 ? transpBtns === 'emboss' ? ShadeColor(col.transportStyleTop, 20) : TintColor(col.transportStyleTop, 4) : - cBRT >= 0 ? transpBtns === 'emboss' ? ShadeColor(col.transportStyleTop, 20) : TintColor(col.transportStyleTop, 4) : ''; - - col.transportStyleBottom = - cBRT > 200 ? TintColor(col.transportStyleBottom, 6) : - cBRT > 175 ? TintColor(col.transportStyleBottom, 6) : - cBRT > 150 ? TintColor(col.transportStyleBottom, 6) : - cBRT > 125 ? TintColor(col.transportStyleBottom, 6) : - cBRT > 100 ? ShadeColor(col.transportStyleBottom, 6) : - cBRT > 75 ? ShadeColor(col.transportStyleBottom, 12) : - cBRT > 50 ? TintColor(col.transportStyleBottom, 6) : - cBRT > 25 ? TintColor(col.transportStyleBottom, 4) : - cBRT >= 0 ? TintColor(col.transportStyleBottom, 4) : ''; - - col.progressBar = - cBRT < 25 ? bevel ? TintColor(col.primary, cBRT < 10 ? blend2 ? 15 : 10 : 5) : ShadeColor(col.primary, 30) : - cBRT < 50 ? RGB(0, 0, 0) : g_pl_colors.bg; - - col.styleProgressBar = - cBRT > 200 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 45 : 65) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 40 : 60) : '' : - cBRT > 175 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 45 : 65) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 40 : 60) : '' : - cBRT > 150 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 45 : 65) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 40 : 60) : '' : - cBRT > 125 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 35 : 55) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 30 : 50) : '' : - cBRT > 100 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 25 : 40) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 20 : 40) : '' : - cBRT > 75 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 15 : 25) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 15 : 20) : '' : - cBRT > 50 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 10 : 15) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 10 : 15) : '' : - cBRT < 50 ? progBar === 'bevel' ? RGBA(255, 255, 255, bevel ? 10 : 10) : progBar === 'inner' ? RGBA(255, 255, 255, bevel ? 10 : 10) : '' : - col.styleProgressBar; - - col.progressBarFill = - cBRT > 200 ? bevel ? TintColor(col.progressBarFill, 10) : ShadeColor(col.progressBarFill, 15) : - cBRT > 175 ? bevel ? TintColor(col.progressBarFill, 5) : ShadeColor(col.progressBarFill, blend ? 5 : 15) : - cBRT > 150 ? bevel ? TintColor(col.progressBarFill, 10) : ShadeColor(col.progressBarFill, 20) : - cBRT > 125 ? bevel ? TintColor(col.progressBarFill, 10) : ShadeColor(col.progressBarFill, 20) : - cBRT > 100 ? bevel ? TintColor(col.progressBarFill, 15) : ShadeColor(col.progressBarFill, 30) : - cBRT > 75 ? TintColor(col.progressBarFill, 30) : - cBRT > 50 ? TintColor(col.progressBarFill, 30) : - cBRT > 25 ? TintColor(col.primary, 25) : - cBRT >= 0 ? TintColor(col.primary, 25) : ''; - - col.styleProgressBarFill = - cBRT > 200 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 80 : 60) : '' : - cBRT > 175 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 80 : 60) : '' : - cBRT > 150 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 80 : 60) : '' : - cBRT > 125 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 90 : 70) : '' : - cBRT > 100 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 100 : 85) : '' : - cBRT > 75 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 100 : 90) : '' : - cBRT > 50 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 110 : 100) : '' : - cBRT > 25 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 130 : 120) : '' : - cBRT >= 0 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 130 : 120) : '' : ''; - - col.shadow = - cBRT > 200 ? RGBA(0, 0, 0, alt ? 45 : 35) : - cBRT > 175 ? RGBA(0, 0, 0, alt ? 45 : 35) : - cBRT > 150 ? RGBA(0, 0, 0, alt ? 45 : 35) : - cBRT > 125 ? RGBA(0, 0, 0, alt ? 50 : 40) : - cBRT > 100 ? RGBA(0, 0, 0, alt ? 60 : 45) : - cBRT > 75 ? RGBA(0, 0, 0, alt ? 75 : 50) : - cBRT > 50 ? RGBA(0, 0, 0, alt ? 100 : 55) : - cBRT > 25 ? RGBA(0, 0, 0, alt ? 120 : 70) : - cBRT >= 0 ? RGBA(0, 0, 0, alt ? 140 : 90) : ''; - } - - /////////////////////////////////////////////////////// - // * REBORN/RANDOM/STYLE REBORN WHITE/BLACK/FUSION * // - /////////////////////////////////////////////////////// - // * Dynamically adjust background colors, lines, transport buttons, progress/volume bar, gradient and shadow - if ((pref.theme === 'reborn' || pref.theme === 'random') && fb.IsPlaying && !isStreaming && !isPlayingCD && !noAlbumArtStub) { - const primary = pref.styleRebornFusion2 ? col.primary_alt : col.primary; - const primary_alt = pref.styleRebornFusion ? col.primary_alt : col.primary; - - // * PLAYLIST COLORS * // - g_pl_colors.header_nowplaying_bg = - cBRT > 200 ? blend ? RGBtoRGBA(TintColor(primary, 20), 130) : TintColor(primary, 20) : - cBRT > 175 ? blend ? RGBtoRGBA(TintColor(primary, 12), 130) : TintColor(primary, 12) : - cBRT > 150 ? blend ? RGBtoRGBA(TintColor(primary, 12), 130) : TintColor(primary, 12) : - cBRT > 125 ? blend ? RGBtoRGBA(TintColor(primary, 10), 130) : TintColor(primary, 10) : - cBRT > 100 ? blend ? RGBtoRGBA(TintColor(primary, 10), 130) : TintColor(primary, 10) : - cBRT > 75 ? blend ? RGBtoRGBA(TintColor(primary, 8), 130) : TintColor(primary, 8) : - cBRT > 50 ? blend ? RGBtoRGBA(TintColor(primary, 6), 130) : TintColor(primary, 6) : - cBRT > 25 ? blend ? RGBtoRGBA(TintColor(primary, 6), 130) : TintColor(primary, 6) : - cBRT >= 0 ? cBRT < 10 ? TintColor(primary, blend || blend2 ? 15 : 10) : - TintColor(primary, blend || blend2 ? 15 : 5) : ''; - - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - - g_pl_colors.header_line_normal = - cBRT > 200 ? ShadeColor(primary, 16) : - cBRT > 175 ? ShadeColor(primary, 18) : - cBRT > 150 ? ShadeColor(primary, 20) : - cBRT > 125 ? ShadeColor(primary, 22) : - cBRT > 100 ? ShadeColor(primary, 24) : - cBRT > 75 ? ShadeColor(primary, 26) : - cBRT > 50 ? ShadeColor(primary, 28) : - cBRT > 25 ? ShadeColor(primary, 30) : - cBRT >= 0 ? TintColor(primary, 15) : ''; - - g_pl_colors.header_line_playing = - cBRT > 200 ? ShadeColor(primary, 26) : - cBRT > 175 ? ShadeColor(primary, 28) : - cBRT > 150 ? ShadeColor(primary, 30) : - cBRT > 125 ? ShadeColor(primary, 32) : - cBRT > 100 ? ShadeColor(primary, 34) : - cBRT > 75 ? ShadeColor(primary, 36) : - cBRT > 50 ? ShadeColor(primary, 38) : - cBRT > 25 ? ShadeColor(primary, 40) : - cBRT >= 0 ? TintColor(primary, 20) : ''; - - g_pl_colors.row_selection_frame = g_pl_colors.header_line_normal; - g_pl_colors.row_disc_subheader_line = g_pl_colors.header_line_normal; - g_pl_colors.row_drag_line = g_pl_colors.row_sideMarker; - g_pl_colors.row_drag_line_reached = cBRT > 210 ? ShadeColor(g_pl_colors.row_sideMarker, 25) : TintColor(g_pl_colors.row_sideMarker, 50); - - // * LIBRARY COLORS * // - ui.col.selectionFrame = g_pl_colors.header_line_normal; - ui.col.line = g_pl_colors.header_line_playing; - - // * MAIN COLORS * // - col.styleGradient = - cBRT > 200 ? ShadeColor(primary, pref.styleRebornBlack ? 65 : 25) : - cBRT > 175 ? ShadeColor(primary, pref.styleRebornBlack ? 60 : 30) : - cBRT > 150 ? ShadeColor(primary, pref.styleRebornBlack ? 55 : 35) : - cBRT > 125 ? ShadeColor(primary, pref.styleRebornBlack ? 50 : 40) : - cBRT > 100 ? ShadeColor(primary, pref.styleRebornBlack ? 45 : 45) : - cBRT > 75 ? ShadeColor(primary, pref.styleRebornBlack ? 40 : 50) : - cBRT > 50 ? ShadeColor(primary, pref.styleRebornBlack ? 35 : 55) : - cBRT > 25 ? ShadeColor(primary, pref.styleRebornBlack ? 30 : 60) : - cBRT >= 0 ? pref.styleRebornBlack ? TintColor(primary, 10) : ShadeColor(primary, 25) : ''; - - col.styleGradient2 = col.styleGradient; - - if (!pref.styleRebornWhite && !pref.styleRebornBlack && !pref.styleRebornFusion) { - col.bg = - cBRT < 10 ? TintColor(primary_alt, blend || blend2 ? 15 : 10) : - cBRT < 25 ? TintColor(primary_alt, blend || blend2 ? 15 : 5) : - col.bg; - - col.progressBar = - cBRT > 200 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 20 : 10) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 175 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 25 : 10) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 150 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 25 : 10) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 125 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 30 : 15) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 100 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 30 : 15) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 75 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 35 : 20) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 50 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 45 : 25) : ShadeColor(primary_alt, bevel ? 30 : 15) : - cBRT > 25 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 0 : 0) : ShadeColor(primary_alt, bevel ? 40 : 15) : - cBRT >= 0 ? blend || blend2 ? ShadeColor(primary_alt, bevel ? 0 : 0) : ShadeColor(primary_alt, bevel ? 40 : 10) : ''; - } - - col.progressBarFill = - cBRT < 175 > 125 ? progBarFillBevelInner ? TintColor(col.progressBarFill, 10) : col.progressBarFill : - col.progressBarFill; - - col.styleProgressBarFill = - cBRT > 200 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 25 : 20) : '' : - cBRT > 175 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 30 : 25) : '' : - cBRT > 150 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 35 : 30) : '' : - cBRT > 125 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 45 : 40) : '' : - cBRT > 100 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 50 : 45) : '' : - cBRT > 75 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 55 : 50) : '' : - cBRT > 50 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 65 : 60) : '' : - cBRT > 25 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 75 : 70) : '' : - cBRT >= 0 ? progBarFillBevelInner ? RGBA(0, 0, 0, bevel ? 85 : 80) : '' : ''; - - if (!pref.styleRebornWhite && !pref.styleRebornBlack) { - col.styleProgressBarLineTop = - cBRT > 200 ? RGBA(0, 0, 0, bevel ? 15 : 10) : - cBRT > 175 ? RGBA(0, 0, 0, bevel ? 20 : 15) : - cBRT > 150 ? RGBA(0, 0, 0, bevel ? 30 : 25) : - cBRT > 125 ? RGBA(0, 0, 0, bevel ? 35 : 30) : - cBRT > 100 ? RGBA(0, 0, 0, bevel ? 40 : 35) : - cBRT > 75 ? RGBA(0, 0, 0, bevel ? 45 : 40) : - cBRT > 50 ? RGBA(0, 0, 0, bevel ? 50 : 45) : - cBRT > 25 ? RGBA(0, 0, 0, bevel ? 55 : 50) : - cBRT >= 0 ? RGBA(0, 0, 0, bevel ? 65 : 60) : ''; - - col.styleProgressBarLineBottom = - cBRT > 200 ? RGBA(255, 255, 255, bevel ? 45 : 50) : - cBRT > 175 ? RGBA(255, 255, 255, bevel ? 40 : 45) : - cBRT > 150 ? RGBA(255, 255, 255, bevel ? 35 : 40) : - cBRT > 125 ? RGBA(255, 255, 255, bevel ? 30 : 35) : - cBRT > 100 ? RGBA(255, 255, 255, bevel ? 25 : 30) : - cBRT > 75 ? RGBA(255, 255, 255, bevel ? 20 : 25) : - cBRT > 50 ? RGBA(255, 255, 255, bevel ? 15 : 20) : - cBRT > 25 ? RGBA(255, 255, 255, bevel ? 10 : 15) : - cBRT >= 0 ? RGBA(255, 255, 255, bevel ? 5 : 10) : ''; - } - - col.shadow = - cBRT > 200 ? RGBA(0, 0, 0, alt ? 45 : 35) : - cBRT > 175 ? RGBA(0, 0, 0, alt ? 45 : 35) : - cBRT > 150 ? RGBA(0, 0, 0, alt ? 45 : 35) : - cBRT > 125 ? RGBA(0, 0, 0, alt ? 50 : 40) : - cBRT > 100 ? RGBA(0, 0, 0, alt ? 60 : 45) : - cBRT > 75 ? RGBA(0, 0, 0, alt ? 75 : 50) : - cBRT > 50 ? RGBA(0, 0, 0, alt ? 100 : 55) : - cBRT > 25 ? RGBA(0, 0, 0, alt ? 120 : 70) : - cBRT >= 0 ? RGBA(0, 0, 0, alt ? 140 : 90) : ''; - - - // * REBORN/RANDOM THEME/STYLE REBORN WHITE/REBORN BLACK - Adjust colors primary color is almost pure white - const defaultRebornRandom = cBRT > 210 && (!blend && !blend2) && !pref.styleRebornWhite && !pref.styleRebornBlack && !pref.styleRebornFusion && !pref.styleRebornFusion2 && !pref.styleRandomDark; - const rebornWhiteBlack = cBRT > 210 && (pref.styleRebornWhite || pref.styleRebornBlack); - - if (defaultRebornRandom || rebornWhiteBlack) { - // * PLAYLIST COLORS * // - g_pl_colors.bg = RGB(255, 255, 255); - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = g_pl_colors.bg; - - g_pl_colors.header_nowplaying_bg = - defaultRebornRandom ? blend && iBRT > 240 ? col.lightAccent_7 : blend2 && iBRT > 240 ? col.lightAccent_35 : RGB(245, 245, 245) : - rebornWhiteBlack ? blend ? RGBtoRGBA(col.lightAccent, 130) : RGB(245, 245, 245) : - g_pl_colors.header_nowplaying_bg; - - g_pl_colors.header_sideMarker = RGB(90, 90, 90); - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - g_pl_colors.row_selection_frame = RGB(220, 220, 220); - g_pl_colors.sbar_btn_normal = RGB(90, 90, 90); - g_pl_colors.sbar_btn_hovered = RGB(0, 0, 0); - g_pl_colors.sbar_thumb_normal = RGB(235, 235, 235); - g_pl_colors.sbar_thumb_hovered = RGB(90, 90, 90); - g_pl_colors.sbar_thumb_drag = g_pl_colors.sbar_thumb_hovered; - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.bgSel = g_pl_colors.header_nowplaying_bg; - ui.col.nowPlayingBg = g_pl_colors.header_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.header_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.sbarBtns = RGB(90, 90, 90); - ui.col.sbarNormal = RGB(210, 210, 210); - ui.col.sbarHovered = RGB(90, 90, 90); - ui.col.sbarDrag = RGB(90, 90, 90); - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.sbarBtns = RGB(90, 90, 90); - uiBio.col.sbarNormal = RGB(210, 210, 210); - uiBio.col.sbarHovered = RGB(90, 90, 90); - uiBio.col.sbarDrag = RGB(90, 90, 90); - - // * MAIN COLORS * // - col.bg = pref.styleRebornBlack ? RGB(0, 0, 0) : bevel ? RGB(255, 255, 255) : RGB(245, 245, 245); - col.detailsBg = g_pl_colors.bg; - - if (pref.styleBlackReborn || pref.styleRebornWhite || pref.styleRebornBlack) { - col.transportEllipseBg = col.lightAccent_100; - col.transportEllipseNormal = ShadeColor(col.lightAccent_7, 10); - } - - if (!pref.styleRebornBlack) { - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = - (pref.styleBlend || pref.styleBlend2) && fb.IsPlaying ? RGB(230, 230, 230) : - pref.styleBevel ? RGB(240, 240, 240) : RGB(255, 255, 255); - - // * PROGRESS BAR COLORS * // - col.progressBar = - pref.styleProgressBar === 'bevel' ? pref.styleBevel ? RGB(245, 245, 245) : RGB(220, 220, 220) : - pref.styleBevel ? pref.styleBlend || pref.styleBlend2 ? RGB(235, 235, 235) : RGB(225, 225, 225) : - (pref.styleBlend || pref.styleBlend2) && fb.IsPlaying && !noAlbumArtStub ? RGB(240, 240, 240) : - RGB(220, 220, 220); - - col.progressBarStreaming = RGB(207, 0, 5); - col.progressBarFrame = pref.styleBevel ? RGB(180, 180, 180) : col.bg; - col.progressBarFill = RGB(90, 90, 90); - - // * VOLUME BAR COLORS * // - col.volumeBar = g_pl_colors.bg; - col.volumeBarFill = RGB(90, 90, 90); - } - - if (pref.styleRebornBlack) { - // * STYLE COLORS * // - col.styleGradient = col.darkAccent_75; - col.styleGradient2 = col.darkAccent_75; - } - } - - // * REBORN/RANDOM THEME - Adjust colors when using style blend and album art is almost pure white - if (iBRT > 210 && (blend || blend2) && !pref.styleRebornWhite && !pref.styleRebornBlack && !pref.styleRandomDark) { - col.primary = TintColor(col.primary, 15); - - // * PLAYLIST COLORS * // - g_pl_colors.header_nowplaying_bg = - blend && iBRT > 240 ? col.lightAccent_7 : - blend2 && iBRT > 240 ? col.lightAccent_35 : - RGBtoRGBA(col.lightAccent_50, 130); - - g_pl_colors.header_sideMarker = cBRT < 150 ? col.lightAccent_80 : col.lightAccent_100; - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - - // * LIBRARY COLORS * // - ui.col.sideMarker = cBRT < 150 ? col.lightAccent_80 : col.lightAccent_100; - - // * MAIN COLORS * // - col.transportEllipseNormal = cBRT < 150 ? col.transportEllipseNormal : ShadeColor(col.lightAccent_7, 10); - col.transportEllipseBg = cBRT < 150 ? col.lightAccent_80 : col.lightAccent_100; - col.progressBar = cBRT < 150 ? col.lightAccent_7 : blend2 && iBRT > 240 ? col.lightAccent_35 : ShadeColor(col.lightAccent_7, 5); - col.progressBarFill = cBRT < 150 ? col.lightAccent_80 : col.lightAccent_100; - } - - ///////////////////////////////////// - // * STYLE REBORN FUSION 1 AND 2 * // - ///////////////////////////////////// - // * ADJUST COLORS WHEN PANEL BG IS TOO LIGHT * // - if (cBRT > 210 && pref.styleRebornFusion || cBRT2 > 210 && pref.styleRebornFusion2) { - panelWhiteColors(true); - } - - // * ADJUST COLORS WHEN MAIN BG IS TOO LIGHT * // - if (cBRT2 > 210 && pref.styleRebornFusion || cBRT > 210 && pref.styleRebornFusion2) { - mainWhiteColors(); - } - - // * ADJUST COLORS WHEN PANEL BG IS TOO DARK * // - if (cBRT < 25 && pref.styleRebornFusion || cBRT2 < 25 && pref.styleRebornFusion2) { - panelBlackColors(true); - } - - // * ADJUST COLORS WHEN MAIN BG IS TOO DARK * // - if (cBRT2 < 25 && pref.styleRebornFusion || cBRT < 25 && pref.styleRebornFusion2) { - mainBlackColors(); - } - } -} - - -////////////////////////// -// * THEME BRIGHTNESS * // -////////////////////////// -/** - * Lightens or darkens the theme based on pref.themeBrightness value, used in Options > Brightness - * @param {number} percent The percentage number for lightening or darkening all colors in the theme. - */ -function adjustThemeBrightness(percent) { - if (percent < 0) percent = Math.abs(percent); // Negative passed values need to be converted to positives - - if (pref.themeBrightness < 0) { // * Darken - // * PLAYLIST COLORS * // - g_pl_colors.bg = ShadeColor(g_pl_colors.bg, percent); - g_pl_colors.plman_bg = ShadeColor(g_pl_colors.plman_bg, percent); - g_pl_colors.plman_text_normal = ShadeColor(g_pl_colors.plman_text_normal, percent); - g_pl_colors.header_nowplaying_bg = ShadeColor(g_pl_colors.header_nowplaying_bg, percent); - g_pl_colors.header_sideMarker = ShadeColor(g_pl_colors.header_sideMarker, percent); - g_pl_colors.header_line_normal = ShadeColor(g_pl_colors.header_line_normal, percent); - g_pl_colors.header_line_playing = ShadeColor(g_pl_colors.header_line_playing, percent); - g_pl_colors.row_nowplaying_bg = ShadeColor(g_pl_colors.row_nowplaying_bg, percent); - g_pl_colors.row_stripes_bg = ShadeColor(g_pl_colors.row_stripes_bg, percent); - g_pl_colors.row_selection_bg = ShadeColor(g_pl_colors.row_selection_bg, percent); - g_pl_colors.row_selection_frame = ShadeColor(g_pl_colors.row_selection_frame, percent); - g_pl_colors.row_sideMarker = ShadeColor(g_pl_colors.row_sideMarker, percent); - g_pl_colors.row_disc_subheader_line = ShadeColor(g_pl_colors.row_disc_subheader_line, percent); - g_pl_colors.sbar_btn_normal = ShadeColor(g_pl_colors.sbar_btn_normal, percent); - g_pl_colors.sbar_btn_hovered = ShadeColor(g_pl_colors.sbar_btn_hovered, percent); - g_pl_colors.sbar_thumb_normal = ShadeColor(g_pl_colors.sbar_thumb_normal, percent); - g_pl_colors.sbar_thumb_hovered = ShadeColor(g_pl_colors.sbar_thumb_hovered, percent); - g_pl_colors.sbar_thumb_drag = ShadeColor(g_pl_colors.sbar_thumb_drag, percent); - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.line = ShadeColor(ui.col.line, percent); - ui.col.s_line = ShadeColor(ui.col.s_line, percent); - ui.col.nowPlayingBg = ShadeColor(ui.col.nowPlayingBg, percent); - ui.col.sideMarker = ShadeColor(ui.col.sideMarker, percent); - ui.col.sideMarker_nobw = ShadeColor(ui.col.sideMarker_nobw, percent); - ui.col.selectionFrame = ShadeColor(ui.col.selectionFrame, percent); - ui.col.sbarBtns = ShadeColor(ui.col.sbarBtns, percent); - ui.col.sbarNormal = ShadeColor(ui.col.sbarNormal, percent); - ui.col.sbarHovered = ShadeColor(ui.col.sbarHovered, percent); - ui.col.sbarDrag = ShadeColor(ui.col.sbarDrag, percent); - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = g_pl_colors.header_line_normal; - uiBio.col.sbarBtns = ShadeColor(uiBio.col.sbarBtns, percent); - uiBio.col.sbarNormal = ShadeColor(uiBio.col.sbarNormal, percent); - uiBio.col.sbarHovered = ShadeColor(uiBio.col.sbarHovered, percent); - uiBio.col.sbarDrag = ShadeColor(uiBio.col.sbarDrag, percent); - - // * MAIN COLORS * // - col.bg = ShadeColor(col.bg, percent); - col.uiHacksFrame = ShadeColor(col.uiHacksFrame, percent); - col.shadow = ShadeColor(col.shadow, percent); - col.detailsBg = ShadeColor(col.detailsBg, percent); - col.timelineAdded = ShadeColor(col.timelineAdded, percent); - col.timelinePlayed = ShadeColor(col.timelinePlayed, percent); - col.timelineUnplayed = ShadeColor(col.timelineUnplayed, percent); - col.timelineFrame = ShadeColor(col.timelineFrame, percent); - col.popupBg = ShadeColor(col.popupBg, percent); - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = ShadeColor(col.menuBgColor, percent); - col.menuStyleBg = ShadeColor(col.menuStyleBg, percent); - col.menuRectStyleEmbossTop = ShadeColor(col.menuRectStyleEmbossTop, percent); - col.menuRectStyleEmbossBottom = ShadeColor(col.menuRectStyleEmbossBottom, percent); - col.menuRectNormal = ShadeColor(col.menuRectNormal, percent); - col.menuRectHovered = ShadeColor(col.menuRectHovered, percent); - col.menuRectDown = col.menuRectHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = ShadeColor(col.transportEllipseBg, percent); - col.transportEllipseNormal = ShadeColor(col.transportEllipseNormal, percent); - col.transportEllipseHovered = ShadeColor(col.transportEllipseHovered, percent); - col.transportEllipseDown = col.transportEllipseHovered; - col.transportStyleBg = ShadeColor(col.transportStyleBg, percent); - col.transportStyleTop = ShadeColor(col.transportStyleTop, percent); - col.transportStyleBottom = ShadeColor(col.transportStyleBottom, percent); - - // * PROGRESS BAR COLORS * // - col.progressBar = ShadeColor(col.progressBar, percent); - col.progressBarStreaming = ShadeColor(col.progressBarStreaming, percent); - col.progressBarFrame = ShadeColor(col.progressBarFrame, percent); - col.progressBarFill = ShadeColor(col.progressBarFill, percent); - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = ShadeColor(col.peakmeterBarProg, percent); - col.peakmeterBarProgFill = ShadeColor(col.peakmeterBarProgFill, percent); - col.peakmeterBarFillTop = ShadeColor(col.peakmeterBarFillTop, percent); - col.peakmeterBarFillMiddle = ShadeColor(col.peakmeterBarFillMiddle, percent); - col.peakmeterBarFillBack = ShadeColor(col.peakmeterBarFillBack, percent); - col.peakmeterBarVertProgFill = ShadeColor(col.peakmeterBarVertProgFill, percent); - col.peakmeterBarVertFill = ShadeColor(col.peakmeterBarVertFill, percent); - col.peakmeterBarVertFillPeaks = ShadeColor(col.peakmeterBarVertFillPeaks, percent); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = ShadeColor(col.waveformBarFillFront, percent); - col.waveformBarFillBack = ShadeColor(col.waveformBarFillBack, percent); - col.waveformBarFillPreFront = ShadeColor(col.waveformBarFillPreFront, percent); - col.waveformBarFillPreBack = ShadeColor(col.waveformBarFillPreBack, percent); - col.waveformBarIndicator = ShadeColor(col.waveformBarIndicator, percent); - - // * VOLUME BAR COLORS * // - col.volumeBar = ShadeColor(col.volumeBar, percent); - col.volumeBarFrame = ShadeColor(col.volumeBarFrame, percent); - col.volumeBarFill = ShadeColor(col.volumeBarFill, percent); - - // * STYLE COLORS * // - col.styleProgressBar = ShadeColor(col.styleProgressBar, percent); - col.styleProgressBarLineTop = ShadeColor(col.styleProgressBarLineTop, percent); - col.styleProgressBarLineBottom = ShadeColor(col.styleProgressBarLineBottom, percent); - col.styleVolumeBar = ShadeColor(col.styleVolumeBar, percent); - - // * ONLY DARKEN BLACK TEXT AND BUTTON COLORS BUT NOT WHITE TEXT COLORS * // - const bgColBrightness = new Color(col.bg).brightness; - const txtColBrightness = new Color(g_pl_colors.row_title_normal).brightness; - if (bgColBrightness < 200 && txtColBrightness < 150) { - adjustTextButtonColors(true, false, false, false); - } - } - else if (pref.themeBrightness > 0) { // * Lighten - // * PLAYLIST COLORS * // - g_pl_colors.bg = TintColor(g_pl_colors.bg, percent); - g_pl_colors.plman_bg = TintColor(g_pl_colors.plman_bg, percent); - g_pl_colors.plman_text_normal = TintColor(g_pl_colors.plman_text_normal, percent); - g_pl_colors.header_nowplaying_bg = TintColor(g_pl_colors.header_nowplaying_bg, percent); - g_pl_colors.header_sideMarker = TintColor(g_pl_colors.header_sideMarker, percent); - g_pl_colors.header_line_normal = TintColor(g_pl_colors.header_line_normal, percent); - g_pl_colors.header_line_playing = TintColor(g_pl_colors.header_line_playing, percent); - g_pl_colors.row_nowplaying_bg = TintColor(g_pl_colors.row_nowplaying_bg, percent); - g_pl_colors.row_stripes_bg = TintColor(g_pl_colors.row_stripes_bg, percent); - g_pl_colors.row_selection_bg = TintColor(g_pl_colors.row_selection_bg, percent); - g_pl_colors.row_selection_frame = TintColor(g_pl_colors.row_selection_frame, percent); - g_pl_colors.row_sideMarker = TintColor(g_pl_colors.row_sideMarker, percent); - g_pl_colors.row_disc_subheader_line = TintColor(g_pl_colors.row_disc_subheader_line, percent); - g_pl_colors.sbar_btn_normal = TintColor(g_pl_colors.sbar_btn_normal, percent); - g_pl_colors.sbar_btn_hovered = TintColor(g_pl_colors.sbar_btn_hovered, percent); - g_pl_colors.sbar_thumb_normal = TintColor(g_pl_colors.sbar_thumb_normal, percent); - g_pl_colors.sbar_thumb_hovered = TintColor(g_pl_colors.sbar_thumb_hovered, percent); - g_pl_colors.sbar_thumb_drag = TintColor(g_pl_colors.sbar_thumb_drag, percent); - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.line = TintColor(ui.col.line, percent); - ui.col.s_line = TintColor(ui.col.s_line, percent); - ui.col.nowPlayingBg = TintColor(ui.col.nowPlayingBg, percent); - ui.col.sideMarker = TintColor(ui.col.sideMarker, percent); - ui.col.sideMarker_nobw = TintColor(ui.col.sideMarker_nobw, percent); - ui.col.selectionFrame = TintColor(ui.col.selectionFrame, percent); - ui.col.sbarBtns = TintColor(ui.col.sbarBtns, percent); - ui.col.sbarNormal = TintColor(ui.col.sbarNormal, percent); - ui.col.sbarHovered = TintColor(ui.col.sbarHovered, percent); - ui.col.sbarDrag = TintColor(ui.col.sbarDrag, percent); - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.bottomLine = g_pl_colors.header_line_normal; - uiBio.col.centerLine = g_pl_colors.header_line_normal; - uiBio.col.sbarBtns = TintColor(uiBio.col.sbarBtns, percent); - uiBio.col.sbarNormal = TintColor(uiBio.col.sbarNormal, percent); - uiBio.col.sbarHovered = TintColor(uiBio.col.sbarHovered, percent); - uiBio.col.sbarDrag = TintColor(uiBio.col.sbarDrag, percent); - - // * MAIN COLORS * // - col.bg = TintColor(col.bg, percent); - col.uiHacksFrame = TintColor(col.uiHacksFrame, percent); - col.shadow = TintColor(col.shadow, percent); - col.detailsBg = TintColor(col.detailsBg, percent); - col.timelineAdded = TintColor(col.timelineAdded, percent); - col.timelinePlayed = TintColor(col.timelinePlayed, percent); - col.timelineUnplayed = TintColor(col.timelineUnplayed, percent); - col.timelineFrame = TintColor(col.timelineFrame, percent); - col.popupBg = TintColor(col.popupBg, percent); - if (str.timeline) str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = TintColor(col.menuBgColor, percent); - col.menuStyleBg = TintColor(col.menuStyleBg, percent); - col.menuRectStyleEmbossTop = TintColor(col.menuRectStyleEmbossTop, percent); - col.menuRectStyleEmbossBottom = TintColor(col.menuRectStyleEmbossBottom, percent); - col.menuRectNormal = TintColor(col.menuRectNormal, percent); - col.menuRectHovered = TintColor(col.menuRectHovered, percent); - col.menuRectDown = col.menuRectHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = TintColor(col.transportEllipseBg, percent); - col.transportEllipseNormal = TintColor(col.transportEllipseNormal, percent); - col.transportEllipseHovered = TintColor(col.transportEllipseHovered, percent); - col.transportEllipseDown = col.transportEllipseHovered; - col.transportStyleBg = TintColor(col.transportStyleBg, percent); - col.transportStyleTop = TintColor(col.transportStyleTop, percent); - col.transportStyleBottom = TintColor(col.transportStyleBottom, percent); - - // * PROGRESS BAR COLORS * // - col.progressBar = TintColor(col.progressBar, percent); - col.progressBarStreaming = TintColor(col.progressBarStreaming, percent); - col.progressBarFrame = TintColor(col.progressBarFrame, percent); - col.progressBarFill = TintColor(col.progressBarFill, percent); - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = TintColor(col.peakmeterBarProg, percent); - col.peakmeterBarProgFill = TintColor(col.peakmeterBarProgFill, percent); - col.peakmeterBarFillTop = TintColor(col.peakmeterBarFillTop, percent); - col.peakmeterBarFillMiddle = TintColor(col.peakmeterBarFillMiddle, percent); - col.peakmeterBarFillBack = TintColor(col.peakmeterBarFillBack, percent); - col.peakmeterBarVertProgFill = TintColor(col.peakmeterBarVertProgFill, percent); - col.peakmeterBarVertFill = TintColor(col.peakmeterBarVertFill, percent); - col.peakmeterBarVertFillPeaks = TintColor(col.peakmeterBarVertFillPeaks, percent); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = TintColor(col.waveformBarFillFront, percent); - col.waveformBarFillBack = TintColor(col.waveformBarFillBack, percent); - col.waveformBarFillPreFront = TintColor(col.waveformBarFillPreFront, percent); - col.waveformBarFillPreBack = TintColor(col.waveformBarFillPreBack, percent); - col.waveformBarIndicator = TintColor(col.waveformBarIndicator, percent); - - // * VOLUME BAR COLORS * // - col.volumeBar = TintColor(col.volumeBar, percent); - col.volumeBarFrame = TintColor(col.volumeBarFrame, percent); - col.volumeBarFill = TintColor(col.volumeBarFill, percent); - - // * STYLE COLORS * // - col.styleProgressBar = TintColor(col.styleProgressBar, percent); - col.styleProgressBarLineTop = TintColor(col.styleProgressBarLineTop, percent); - col.styleProgressBarLineBottom = TintColor(col.styleProgressBarLineBottom, percent); - col.styleVolumeBar = TintColor(col.styleVolumeBar, percent); - - // * LIGHTEN TEXT AND BUTTON COLORS * // - const bgColBrightness = new Color(col.bg).brightness; - if (bgColBrightness < 150 && bgColBrightness > 50) { - adjustTextButtonColors(false, false, true, false); - } - } - - // * ADJUST TEXT AND BUTTONS WHEN BRIGHTNESS IS TOO BRIGHT OR TOO DARK * // - function adjustTextButtonColors(darken, darkenMax, lighten, lightenMax) { - const SetColor = (color, boost, soften) => { - switch (true) { - case darken: - return ShadeColor(color, percent * (boost ? 1.75 : soften ? 1.25 : 1.5)); - case darkenMax: - return ShadeColor(color, boost ? 100 : soften ? 60 : 85); - case lighten: - return TintColor(color, percent * (boost ? 1.75 : soften ? 1.25 : 1.5)); - case lightenMax: - return TintColor(color, boost ? 100 : soften ? 60 : 85); - } - }; - - const playlistColors = { - plman_text_normal: pref.autoHidePlman ? g_pl_colors.bg : SetColor(g_pl_colors.plman_text_normal), - plman_text_hovered: SetColor(g_pl_colors.plman_text_hovered, true), - plman_text_pressed: SetColor(g_pl_colors.plman_text_pressed, true), - header_artist_normal: SetColor(g_pl_colors.header_artist_normal), - header_artist_playing: SetColor(g_pl_colors.header_artist_playing, true), - header_album_normal: SetColor(g_pl_colors.header_album_normal), - header_album_playing: SetColor(g_pl_colors.header_album_playing, true), - header_info_normal: SetColor(g_pl_colors.header_info_normal, true), - header_info_playing: SetColor(g_pl_colors.header_info_playing, true), - header_date_normal: SetColor(g_pl_colors.header_date_normal), - header_date_playing: SetColor(g_pl_colors.header_date_playing, true), - row_title_normal: SetColor(g_pl_colors.row_title_normal), - row_title_playing: SetColor(g_pl_colors.row_title_playing, true), - row_title_selected: SetColor(g_pl_colors.row_title_selected, true), - row_title_hovered: SetColor(g_pl_colors.row_title_hovered, true), - sbar_btn_normal: SetColor(g_pl_colors.sbar_btn_normal), - sbar_btn_hovered: SetColor(g_pl_colors.sbar_btn_hovered, true), - sbar_thumb_normal: SetColor(g_pl_colors.sbar_thumb_normal, false, true), - sbar_thumb_hovered: SetColor(g_pl_colors.sbar_thumb_hovered, true), - sbar_thumb_drag: SetColor(g_pl_colors.sbar_thumb_drag, true) - }; - Object.assign(g_pl_colors, playlistColors); - - const libraryColors = { - iconPlus: SetColor(ui.col.iconPlus), - iconPlus_h: SetColor(ui.col.iconPlus_h, true), - iconPlus_sel: SetColor(ui.col.iconPlus_sel, true), - iconPlusBg: SetColor(ui.col.iconPlusBg), - iconMinus_e: SetColor(ui.col.iconMinus_e), - iconMinus_h: SetColor(ui.col.iconMinus_h, true), - text: SetColor(ui.col.text), - text_h: SetColor(ui.col.text_h, true), - text_nowp: SetColor(ui.col.text_nowp, true), - textSel: SetColor(ui.col.textSel, true), - txt_box: SetColor(ui.col.txt_box), - search: SetColor(ui.colsearch), - searchBtn: SetColor(ui.col.searchBtn), - crossBtn: SetColor(ui.col.crossBtn), - filterBtn: SetColor(ui.col.filterBtn), - settingsBtn: SetColor(ui.col.settingsBtn), - line: SetColor(ui.col.line), - sbarBtns: SetColor(ui.col.sbarBtns), - sbarNormal: SetColor(ui.col.sbarNormal), - sbarHovered: SetColor(ui.col.sbarHovered, true), - sbarDrag: SetColor(ui.col.sbarDrag, true) - }; - Object.assign(ui.col, libraryColors); - - const biographyColors = { - headingText: SetColor(uiBio.col.headingText), - iconMinus_e: SetColor(uiBio.col.iconMinus_e), - iconMinus_h: SetColor(uiBio.col.iconMinus_h), - text: SetColor(uiBio.col.text), - source: SetColor(uiBio.col.source), - accent: SetColor(uiBio.col.accent), - summary: SetColor(uiBio.col.summary), - sbarBtns: SetColor(uiBio.col.sbarBtns), - sbarNormal: SetColor(uiBio.sbarNormal), - sbarHovered: SetColor(uiBio.col.sbarHovered, true), - sbarDrag: SetColor(uiBio.col.sbarDrag, true) - }; - Object.assign(uiBio.col, biographyColors); - - const mainColors = { - detailsText: SetColor(col.detailsText), - popupText: SetColor(col.popupText), - noAlbumArtStub: SetColor(col.noAlbumArtStub), - lowerBarArtist: SetColor(col.lowerBarArtist), - lowerBarTitle: SetColor(col.lowerBarTitle), - lowerBarTime: SetColor(col.lowerBarTime), - lowerBarLength: SetColor(col.lowerBarLength), - menuTextNormal: SetColor(col.menuTextNormal), - menuTextHovered: SetColor(col.menuTextHovered, true), - menuTextDown: SetColor(col.menuTextDown, true), - transportIconNormal: !['reborn', 'random'].includes(pref.theme) ? SetColor(col.transportIconNormal) : col.transportIconNormal, - transportIconHovered: !['reborn', 'random'].includes(pref.theme) ? SetColor(col.transportIconHovered, true) : col.transportIconHovered, - transportIconDown: !['reborn', 'random'].includes(pref.theme) ? SetColor(col.transportIconDown, true) : col.transportIconDown - }; - Object.assign(col, mainColors); - - window.Repaint(); - } - - const bgColBrightness = new Color(col.bg).brightness; - if (pref.themeBrightness > 20 && bgColBrightness < 200 && bgColBrightness > 125) { - lightBg = false; - adjustTextButtonColors(false, true, false, false); - } - else if (pref.themeBrightness < -20 && bgColBrightness < 150 && bgColBrightness > 50) { - lightBg = false; - adjustTextButtonColors(false, false, false, true); - } -} - - -//////////////////////////////// -// * STYLE NIGHTTIME COLORS * // -//////////////////////////////// -/** - * Active Reborn theme used in Options > Style > Nighttime. - */ -function styleNighttimeColors() { - if (pref.theme.startsWith('custom')) return; - - const rebornNightAccentColor = pref.theme === 'reborn' ? RGB(210, 235, 240) : false; - - if (!fb.IsPlaying) { - panelBlackColors(false, rebornNightAccentColor); - mainBlackColors(); - } else { - playlistColorsRebornRandomTheme(); - libraryColorsRebornRandomTheme(); - biographyColorsRebornRandomTheme(); - mainColorsRebornRandomTheme(); - } -} - - -/////////////////////////// -// * STYLE ALTERNATIVE * // -/////////////////////////// -/** - * Any active theme used in Options > Style > Alternative. - */ -function styleAlternativeColors() { - // * PLAYLIST * // - g_pl_colors.bg = - pref.theme === 'white' ? RGB(245, 245, 245) : - pref.theme === 'black' ? TintColor(g_pl_colors.bg, 6) : - pref.theme === 'reborn' || pref.theme === 'random' ? ShadeColor(g_pl_colors.bg, 5) : - pref.theme === 'blue' ? RGB(8, 110, 190) : - pref.theme === 'darkblue' ? RGB(17, 35, 57) : - pref.theme === 'red' ? RGB(106, 18, 18) : - pref.theme === 'cream' ? RGB(255, 247, 240) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? TintColor(g_pl_colors.bg, 8) : - pref.theme.startsWith('custom') ? ShadeColor(g_pl_colors.bg, 5) : ''; - - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = g_pl_colors.bg; - - g_pl_colors.header_nowplaying_bg = - pref.theme === 'blue' ? RGB(20, 120, 205) : - pref.theme === 'darkblue' ? RGB(18, 42, 70) : - pref.theme === 'red' ? RGB(130, 25, 25) : - g_pl_colors.header_nowplaying_bg; - - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_selection_bg = g_pl_colors.row_nowplaying_bg; - - // * LIBRARY * // - ui.col.bg = g_pl_colors.bg; - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - - // * BIOGRAPHY * // - uiBio.col.bg = g_pl_colors.bg; - - // * MAIN * // - col.bg = - pref.theme === 'white' ? RGB(255, 255, 255) : - pref.theme === 'black' ? TintColor(col.bg, 4) : - pref.theme === 'reborn' || pref.theme === 'random' ? TintColor(col.bg, 8) : - pref.theme === 'blue' ? RGB(20, 120, 205) : - pref.theme === 'darkblue' ? RGB(18, 42, 70) : - pref.theme === 'red' ? RGB(130, 25, 25) : - pref.theme === 'cream' ? RGB(255, 255, 255) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? RGB(30, 30, 30) : - pref.theme.startsWith('custom') ? TintColor(col.bg, 8) : ''; - - col.uiHacksFrame = col.bg; - - col.shadow = - pref.theme === 'reborn' || pref.theme === 'random' ? RGBA(0, 0, 0, 25) : - pref.theme === 'blue' ? col.shadow + RGBA(0, 0, 0, 25) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? RGBA(0, 0, 0, 255) : - col.shadow + RGBA(0, 0, 0, 10); - - col.detailsBg = g_pl_colors.bg; - col.detailsText = g_pl_colors.row_title_normal; - - // * LOWER BAR TRANSPORT BUTTONS * // - col.transportEllipseBg = - pref.theme === 'white' ? TintColor(col.transportEllipseBg, 100) : - pref.theme === 'black' ? TintColor(col.transportEllipseBg, 4) : - pref.theme === 'reborn' || pref.theme === 'random' ? TintColor(col.transportEllipseBg, 0) : - pref.theme === 'blue' ? TintColor(col.transportEllipseBg, 6) : - pref.theme === 'darkblue' ? TintColor(col.transportEllipseBg, 0) : - pref.theme === 'red' ? RGB(158, 30, 30) : - pref.theme === 'cream' ? g_pl_colors.bg : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? ShadeColor(col.transportEllipseBg, 20) : - ShadeColor(col.transportEllipseBg, 10); - - col.transportEllipseNormal = - pref.theme === 'black' ? ShadeColor(col.transportEllipseNormal, 6) : - pref.theme === 'red' ? RGB(106, 18, 18) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? ShadeColor(col.transportEllipseNormal, 90) : - TintColor(col.transportEllipseNormal, 6); - - col.transportEllipseHovered = TintColor(col.transportEllipseHovered, 6); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.theme === 'black' ? TintColor(col.transportStyleBg, 4) : - pref.theme === 'blue' ? TintColor(col.transportStyleBg, 6) : - pref.theme === 'darkblue' ? TintColor(col.transportStyleBg, 0) : - pref.theme === 'red' ? TintColor(col.transportStyleBg, 2) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? ShadeColor(col.transportStyleBg, 6) : - TintColor(col.transportStyleBg, 6); - - col.transportStyleTop = - pref.theme === 'black' ? ShadeColor(col.transportStyleTop, 6) : - pref.theme === 'blue' ? ShadeColor(col.transportStyleTop, 6) : - pref.theme === 'darkblue' ? ShadeColor(col.transportStyleTop, 0) : - pref.theme === 'red' ? TintColor(col.transportStyleTop, pref.styleTransportButtons === 'emboss' ? 6 : 2) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? ShadeColor(col.transportStyleTop, 6) : - TintColor(col.transportStyleTop, 6); - - col.transportStyleBottom = - pref.theme === 'black' ? ShadeColor(col.transportStyleBottom, 0) : - pref.theme === 'blue' ? ShadeColor(col.transportStyleBottom, 6) : - pref.theme === 'darkblue' ? ShadeColor(col.transportStyleBottom, 0) : - pref.theme === 'red' ? TintColor(col.transportStyleBottom, pref.styleTransportButtons === 'emboss' ? 6 : 2) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? ShadeColor(col.transportStyleBottom, 6) : - TintColor(col.transportStyleBottom, 6); - - // * PROGRESS BAR * // - col.progressBar = - pref.theme === 'white' ? pref.styleBevel ? TintColor(col.progressBar, 60) : TintColor(col.progressBar, 40) : - pref.theme === 'black' ? TintColor(col.progressBar, 2) : - pref.theme === 'reborn' || pref.theme === 'random' ? colBrightness < 25 ? TintColor(col.primary, 12) : fb.IsPlaying && !noAlbumArtStub ? g_pl_colors.bg : col.progressBar : - pref.theme === 'blue' ? TintColor(col.progressBar, 2) : - pref.theme === 'darkblue' ? TintColor(col.progressBar, 0) : - pref.theme === 'red' ? RGB(158, 30, 30) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? pref.styleBevel ? ShadeColor(col.progressBar, 60) : ShadeColor(col.progressBar, 35) : - pref.theme.startsWith('custom') ? ShadeColor(col.progressBar, 5) : - g_pl_colors.bg; - - // * VOLUME BAR * // - col.volumeBar = col.progressBar; - col.volumeBarFill = col.progressBarFill; -} - - -///////////////////////////// -// * STYLE ALTERNATIVE 2 * // -///////////////////////////// -/** - * Any active theme used in Options > Style > Alternative 2. - */ -function styleAlternative2Colors() { - // * PLAYLIST * // - g_pl_colors.bg = - pref.theme === 'white' ? TintColor(g_pl_colors.bg, 4) : - pref.theme === 'black' ? TintColor(g_pl_colors.bg, 3) : - pref.theme === 'reborn' || pref.theme === 'random' ? TintColor(g_pl_colors.bg, 5) : - pref.theme === 'blue' ? RGB(20, 120, 205) : - pref.theme === 'darkblue' ? RGB(18, 42, 70) : - pref.theme === 'red' ? RGB(120, 22, 22) : - pref.theme === 'cream' ? RGB(255, 255, 255) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? TintColor(g_pl_colors.bg, 6) : - pref.theme.startsWith('custom') ? TintColor(g_pl_colors.bg, 5) : ''; - - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = g_pl_colors.bg; - - g_pl_colors.header_nowplaying_bg = - pref.theme === 'black' && colBrightness < 25 ? TintColor(col.primary, 15) : - pref.theme === 'reborn' || pref.theme === 'random' ? pref.styleBlend ? RGBtoRGBA(col.darkAccent, 60) : RGBtoRGBA(col.darkAccent, 40) : - pref.theme === 'red' ? RGB(140, 25, 25) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? TintColor(g_pl_colors.header_nowplaying_bg, 6) : - g_pl_colors.header_nowplaying_bg; - - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_selection_bg = g_pl_colors.row_nowplaying_bg; - - // * LIBRARY * // - ui.col.bg = g_pl_colors.bg; - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - - // * BIOGRAPHY * // - uiBio.col.bg = g_pl_colors.bg; - - // * MAIN * // - col.bg = - pref.theme === 'white' ? ShadeColor(col.bg, 6) : - pref.theme === 'black' ? TintColor(col.bg, 10) : - pref.theme === 'reborn' || pref.theme === 'random' ? ShadeColor(col.bg, 8) : - pref.theme === 'blue' ? RGB(8, 102, 180) : - pref.theme === 'darkblue' ? RGB(17, 35, 57) : - pref.theme === 'red' ? RGB(95, 15, 15) : - pref.theme === 'cream' ? RGB(255, 247, 240) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? RGB(25, 25, 25) : - pref.theme.startsWith('custom') ? ShadeColor(col.bg, 8) : ''; - - col.uiHacksFrame = col.bg; - - col.shadow = - pref.theme === 'black' ? col.shadow - RGBA(0, 0, 0, 80) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? col.shadow : - col.shadow + RGBA(0, 0, 0, 5); - - col.detailsBg = g_pl_colors.bg; - col.detailsText = g_pl_colors.row_title_normal; - - // * LOWER BAR TRANSPORT BUTTONS * // - col.transportEllipseBg = - pref.theme === 'white' ? TintColor(col.transportEllipseBg, 100) : - pref.theme === 'black' ? ShadeColor(col.transportEllipseBg, 6) : - pref.theme === 'darkblue' ? TintColor(col.transportEllipseBg, 0) : - pref.theme === 'red' ? RGB(140, 25, 25) : - TintColor(col.transportEllipseBg, 6); - - col.transportEllipseNormal = - pref.theme === 'black' ? ShadeColor(col.transportEllipseNormal, 60) : - TintColor(col.transportEllipseNormal, 6); - - col.transportEllipseHovered = TintColor(col.transportEllipseHovered, 6); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.theme === 'white' ? pref.styleBevel ? ShadeColor(col.transportStyleBg, 10) : ShadeColor(col.transportStyleBg, 7) : - pref.theme === 'darkblue' ? TintColor(col.transportStyleBg, 0) : - pref.theme === 'red' ? TintColor(col.transportStyleBg, 2) : - pref.theme === 'cream' ? RGB(230, 230, 230) : - TintColor(col.transportStyleBg, 6); - - col.transportStyleTop = - pref.theme === 'white' ? ShadeColor(col.transportStyleTop, 6) : - pref.theme === 'blue' ? TintColor(col.transportStyleTop, 12) : - pref.theme === 'darkblue' ? TintColor(col.transportStyleTop, 0) : - pref.theme === 'red' ? TintColor(col.transportStyleTop, 2) : - TintColor(col.transportStyleTop, 6); - - col.transportStyleBottom = - pref.theme === 'white' ? ShadeColor(col.transportStyleBottom, 6) : - pref.theme === 'blue' ? ShadeColor(col.transportStyleBottom, 10) : - pref.theme === 'darkblue' ? TintColor(col.transportStyleBottom, 0) : - pref.theme === 'red' ? TintColor(col.transportStyleBottom, 2) : - TintColor(col.transportStyleBottom, 6); - - // * PROGRESS BAR * // - col.progressBar = - pref.theme === 'white' ? pref.styleBevel ? TintColor(col.progressBar, 60) : TintColor(col.progressBar, 100) : - pref.theme === 'black' ? ShadeColor(col.progressBar, 16) : - pref.theme === 'reborn' || pref.theme === 'random' ? colBrightness < 25 ? TintColor(col.primary, 12) : g_pl_colors.bg : - pref.theme === 'blue' ? g_pl_colors.bg : - pref.theme === 'darkblue' ? g_pl_colors.row_nowplaying_bg : - pref.theme === 'red' ? TintColor(col.progressBar, 0) : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? TintColor(col.progressBar, 3) : - pref.theme.startsWith('custom') ? ShadeColor(col.progressBar, 2) : - g_pl_colors.bg; - - // * VOLUME BAR * // - col.volumeBar = col.progressBar; - col.volumeBarFill = col.progressBarFill; -} - - -/////////////////////////////// -// * STYLE BLACK AND WHITE * // -/////////////////////////////// -/** - * Active White theme used in Options > Style > Black and white. - */ -function styleBlackAndWhiteColors() { - col.primary = RGB(255, 255, 255); - panelBlackColors(); - mainWhiteColors(); -} - - -///////////////////////////////// -// * STYLE BLACK AND WHITE 2 * // -///////////////////////////////// -/** - * Active White theme used in Options > Style > Black and white 2. - */ -function styleBlackAndWhite2Colors() { - col.primary = RGB(255, 255, 255); - panelWhiteColors(); - mainBlackColors(); -} - - -//////////////////////////// -// * STYLE BLACK REBORN * // -//////////////////////////// -/** - * Active Black theme used in Options > Style > Black reborn. - */ -function styleBlackRebornColors() { - if (!fb.IsPlaying || !albumArt && !noAlbumArtStub) col.primary = RGB(25, 25, 25); - if (isStreaming || isPlayingCD) setNoAlbumArtColors(); - - // * PLAYLIST COLORS * // - g_pl_colors.bg = RGB(20, 20, 20); - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = g_pl_colors.bg; - g_pl_colors.header_nowplaying_bg = colBrightness < 25 ? col.lightAccent : pref.styleBevel ? ShadeColor(col.primary, 10) : col.primary; - g_pl_colors.header_line_normal = pref.styleBlend ? RGB(65, 65, 65) : RGB(45, 45, 45); - g_pl_colors.header_line_playing = RGB(25, 25, 25); - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_disc_subheader_line = pref.styleBlend ? RGB(65, 65, 65) : RGB(45, 45, 45); - g_pl_colors.row_drag_line = lightBg ? ShadeColor(g_pl_colors.row_selection_frame, 20) : TintColor(g_pl_colors.row_selection_frame, 20); - g_pl_colors.row_drag_line_reached = g_pl_colors.row_sideMarker; - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.nowPlayingBg = g_pl_colors.row_nowplaying_bg; - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - - // * MAIN COLORS * // - col.bg = colBrightness < 25 || isStreaming || isPlayingCD ? pref.styleBevel ? RGB(40, 40, 40) : RGB(25, 25, 25) : col.primary; - col.detailsBg = g_pl_colors.bg; - col.detailsText = RGB(220, 220, 220); - col.styleBevel = isStreaming || isPlayingCD || !fb.IsPlaying ? RGB(0, 0, 0) : col.primary === RGB(175, 205, 225) ? RGB(70, 90, 105) : col.darkAccent_50; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = g_pl_colors.bg; - col.transportEllipseNormal = col.transportEllipseBg; - col.transportEllipseHovered = RGB(120, 120, 120); - col.transportEllipseDown = col.transportEllipseHovered; - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * WHEN PLAYING * // - if (fb.IsPlaying) { - if (lightBg) { - // * PLAYLIST COLORS * // - g_pl_colors.row_title_playing = col.darkAccent_100; - - // * MAIN COLORS * // - col.noAlbumArtStub = RGB(175, 205, 225); - col.lowerBarArtist = col.darkAccent_75; - col.lowerBarTitle = col.darkAccent_75; - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 40) : RGBtoRGBA(col.lightAccent_80, 50) : - pref.styleTopMenuButtons !== 'default' ? col.lightAccent_10 : col.lightAccent_50; - - col.menuRectNormal = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 40) : RGBtoRGBA(col.lightAccent_80, 30) : - col.darkAccent; - - col.menuRectHovered = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 40) : RGBtoRGBA(col.darkAccent_75, 30) : - col.darkAccent; - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = col.darkAccent_75; - col.menuTextHovered = col.darkAccent_100; - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportIconNormal = pref.styleTransportButtons === 'minimal' ? RGB(20, 20, 20) : RGB(180, 180, 180); - col.menuStyleBg = col.primary === RGB(175, 205, 225) ? RGB(130, 153, 168) : col.accent; - col.menuRectStyleEmbossTop = col.lightAccent; - col.menuRectStyleEmbossBottom = col.darkAccent; - } - else { - // * PLAYLIST COLORS * // - g_pl_colors.row_title_playing = col.lightAccent_100; - - // * MAIN COLORS * // - col.noAlbumArtStub = RGB(175, 205, 225); - col.lowerBarArtist = col.lightAccent_100; - col.lowerBarTitle = col.lightAccent_100; - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.lightAccent_80, 40) : RGBtoRGBA(col.lightAccent_80, 50) : - pref.styleTopMenuButtons !== 'default' ? col.lightAccent_10 : col.darkAccent_50; - - col.menuRectNormal = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 60) : RGBtoRGBA(col.darkAccent_75, 50) : - col.lightAccent_50; - - col.menuRectHovered = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? - pref.styleBevel ? RGBtoRGBA(col.darkAccent_75, 60) : RGBtoRGBA(col.darkAccent_75, 50) : - col.lightAccent_50; - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = col.lightAccent_80; - col.menuTextHovered = col.lightAccent_100; - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportIconNormal = pref.styleTransportButtons === 'minimal' ? RGB(220, 220, 220) : RGB(180, 180, 180); - col.menuStyleBg = col.primary === RGB(175, 205, 225) ? RGB(130, 153, 168) : col.accent; - col.menuRectStyleEmbossTop = col.lightAccent; - col.menuRectStyleEmbossBottom = col.darkAccent; - } - } - - // * PROGRESS BAR COLORS * // - col.progressBar = - colBrightness < 50 ? - pref.styleProgressBar === 'bevel' ? RGB(30, 30, 30) : - pref.styleProgressBar === 'inner' ? RGB(30, 30, 30) : - pref.styleBevel ? colBrightness < 25 ? RGB(30, 30, 30) : RGB(0, 0, 0) : RGB(0, 0, 0) : - g_pl_colors.bg; - - if (col.primary === RGB(175, 205, 225)) { - col.progressBarFill = pref.styleBlend || pref.styleBlend2 ? RGB(155, 185, 205) : RGB(145, 170, 190); - } - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness > 200 || colBrightness < 75 ? TintColor(col.primary, 100) : ShadeColor(col.primary, 100); - col.peakmeterBarFillTop = colBrightness > 200 ? ShadeColor(col.primary, 10) : TintColor(col.primary, 40); - col.peakmeterBarFillMiddle = colBrightness > 200 ? ShadeColor(col.primary, 20) : TintColor(col.primary, 60); - col.peakmeterBarFillBack = colBrightness > 200 ? ShadeColor(col.primary, 40) : TintColor(col.primary, 80); - col.peakmeterBarVertProgFill = colBrightness > 200 ? ShadeColor(col.primary, 25) : col.progressBarFill; - col.peakmeterBarVertFill = colBrightness > 200 ? ShadeColor(col.primary, 15) : TintColor(col.primary, 40); - col.peakmeterBarVertFillPeaks = colBrightness > 200 ? ShadeColor(col.primary, 25) : TintColor(col.primary, 60); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = colBrightness > 200 ? ShadeColor(col.primary, 80) : TintColor(col.primary, 90); - col.waveformBarFillBack = colBrightness > 200 ? ShadeColor(col.primary, 40) : TintColor(col.primary, 45); - col.waveformBarFillPreFront = colBrightness > 150 ? ShadeColor(col.primary, 40) : TintColor(col.primary, 50); - col.waveformBarFillPreBack = colBrightness > 150 ? ShadeColor(col.primary, 20) : TintColor(col.primary, 25); - col.waveformBarIndicator = colBrightness > 200 ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = g_pl_colors.bg; - col.volumeBarFill = col.progressBarFill; - - // * STYLE COLORS * // - col.styleVolumeBar = - pref.styleVolumeBar === 'bevel' ? pref.styleBevel ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : - pref.styleVolumeBar === 'inner' ? pref.styleBevel ? RGBA(0, 0, 0, 120) : RGBA(0, 0, 0, 100) : ''; - - col.styleVolumeBarFill = pref.styleVolumeBarFill === 'bevel' || pref.styleVolumeBarFill === 'inner' ? RGBA(0, 0, 0, 100) : ''; -} - - -/////////////////////////////////// -// * STYLE REBORN WHITE COLORS * // -/////////////////////////////////// -/** - * Active Reborn theme used in Options > Style > Reborn white. - */ -function styleRebornWhiteColors() { - // * PLAYLIST COLORS * // - g_pl_colors.bg = !fb.IsPlaying ? RGB(255, 255, 255) : g_pl_colors.bg; - - // * MAIN COLORS * // - col.bg = pref.styleBlend || pref.styleBlend2 ? RGB(255, 255, 255) : RGB(245, 245, 245); - col.noAlbumArtStub = RGB(90, 90, 90); - col.lowerBarArtist = pref.styleBlend || pref.styleBlend2 ? RGB(40, 40, 40) : RGB(80, 80, 80); - col.lowerBarTitle = pref.styleBlend || pref.styleBlend2 ? RGB(50, 50, 50) : RGB(100, 100, 100); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(250, 250, 250) : RGB(255, 255, 255) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(250, 250, 250) : RGB(235, 235, 235) : - RGB(255, 255, 255); - - col.menuRectNormal = - pref.styleTopMenuButtons === 'filled' ? RGB(200, 200, 200) : - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(140, 140, 140) : RGB(150, 150, 150) : - pref.styleBevel ? RGB(170, 170, 170) : RGB(180, 180, 180); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGB(200, 200, 200) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(200, 200, 200) : RGB(220, 220, 220) : - pref.styleBlend || pref.styleBlend2 ? pref.styleBevel ? RGB(140, 140, 140) : RGB(150, 150, 150) : - pref.styleBevel ? RGB(170, 170, 170) : RGB(180, 180, 180); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? RGB(50, 50, 50) : RGB(100, 100, 100); - col.menuTextHovered = pref.styleBlend || pref.styleBlend2 ? RGB(0, 0, 0) : RGB(80, 80, 80); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = pref.styleBlend || pref.styleBlend2 ? RGB(225, 225, 225) : RGB(255, 255, 255); - col.transportEllipseNormal = RGB(220, 220, 220); - col.transportEllipseHovered = RGB(200, 200, 200); - col.transportEllipseDown = col.transportEllipseHovered; - col.transportIconNormal = pref.styleBlend || pref.styleBlend2 ? RGB(80, 80, 80) : RGB(100, 100, 100); - col.transportIconHovered = RGB(80, 80, 80); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = pref.styleBevel ? RGB(225, 225, 225) : RGB(220, 220, 220); - col.progressBarFill = ShadeColor(col.primary, 5); - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness < 75 ? TintColor(col.primary, 40) : ShadeColor(col.primary, 40); - col.peakmeterBarFillTop = TintColor(col.primary, 10); - col.peakmeterBarFillMiddle = TintColor(col.primary, 30); - col.peakmeterBarFillBack = TintColor(col.primary, 50); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = ShadeColor(col.primary, 10); - col.peakmeterBarVertFillPeaks = TintColor(col.primary, 20); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = col.primary; - col.waveformBarFillBack = ShadeColor(col.primary, 20); - col.waveformBarFillPreFront = RGB(180, 180, 180); - col.waveformBarFillPreBack = RGB(160, 160, 160); - col.waveformBarIndicator = colBrightness > 200 ? RGB(0, 0, 0) : TintColor(col.primary, 30); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(255, 255, 255); - col.volumeBarFill = col.primary; - col.volumeBarFrame = RGB(220, 220, 220); - - // * STYLE COLORS * // - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 50) : - pref.styleBevel ? RGBA(0, 0, 0, 10) : RGBA(0, 0, 0, 0) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 30) : RGBA(0, 0, 0, 50) : - pref.styleBevel ? RGBA(0, 0, 0, 10) : RGBA(0, 0, 0, 20) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(255, 255, 255, 120) : RGBA(255, 255, 255, 255) : - pref.styleBevel ? RGBA(255, 255, 255, 100) : - pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 255) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 20) : RGBA(0, 0, 0, 25) : - pref.styleBevel ? RGBA(0, 0, 0, 5) : - pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 80) : RGBA(255, 255, 255, 255) : ''; -} - - -/////////////////////////////////// -// * STYLE REBORN BLACK COLORS * // -/////////////////////////////////// -/** - * Active Reborn theme used in Options > Style > Reborn black. - */ -function styleRebornBlackColors() { - // * MAIN COLORS * // - col.bg = pref.styleBevel ? RGB(40, 40, 40) : RGB(20, 20, 20); - col.noAlbumArtStub = RGB(90, 90, 90); - col.lowerBarArtist = RGB(240, 240, 240); - col.lowerBarTitle = pref.styleBlend || pref.styleBlend2 ? RGB(220, 220, 220) : RGB(200, 200, 200); - col.lowerBarTime = col.lowerBarTitle; - col.lowerBarLength = col.lowerBarTitle; - - // * TOP MENU BUTTON COLORS * // - col.menuBgColor = - pref.styleTopMenuButtons === 'bevel' ? pref.styleBevel ? RGB(40, 40, 40) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'inner' ? pref.styleBevel ? RGB(55, 55, 55) : RGB(50, 50, 50) : - pref.styleTopMenuButtons === 'emboss' ? RGB(45, 45, 45) : - col.darkAccent_50; - - col.menuStyleBg = - pref.styleTopMenuButtons === 'inner' ? RGB(20, 20, 20) : - pref.styleTopMenuButtons === 'emboss' ? pref.styleBevel ? RGB(45, 45, 45) : RGB(50, 50, 50) : - pref.styleBevel ? RGB(30, 30, 30) : RGB(20, 20, 20); - - col.menuRectStyleEmbossTop = pref.styleBevel ? RGB(60, 60, 60) : RGB(70, 70, 70); - col.menuRectStyleEmbossBottom = RGB(0, 0, 0); - - col.menuRectNormal = pref.styleTopMenuButtons === 'filled' ? RGBA(60, 60, 60, 100) : RGB(60, 60, 60); - - col.menuRectHovered = - pref.styleTopMenuButtons === 'filled' ? RGBA(120, 120, 120, 100) : - pref.styleTopMenuButtons === 'bevel' || pref.styleTopMenuButtons === 'inner' ? RGB(0, 0, 0) : - RGB(120, 120, 120); - - col.menuRectDown = col.menuRectHovered; - col.menuTextNormal = pref.styleBlend || pref.styleBlend2 ? RGB(220, 220, 220) : RGB(180, 180, 180); - col.menuTextHovered = RGB(255, 255, 255); - col.menuTextDown = col.menuTextHovered; - - // * LOWER BAR TRANSPORT BUTTON COLORS * // - col.transportEllipseBg = pref.styleBlend || pref.styleBlend2 ? RGB(50, 50, 50) : RGB(35, 35, 35); - col.transportEllipseNormal = RGB(60, 60, 60); - col.transportEllipseHovered = RGB(120, 120, 120); - col.transportEllipseDown = col.transportEllipseHovered; - - col.transportStyleBg = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(20, 20, 20) : - pref.styleTransportButtons === 'emboss' ? RGB(50, 50, 50) : ''; - - col.transportStyleTop = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(50, 50, 50) : - pref.styleTransportButtons === 'emboss' ? pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 40) : ''; - - col.transportStyleBottom = - pref.styleTransportButtons === 'bevel' || pref.styleTransportButtons === 'inner' ? RGB(10, 10, 10) : - pref.styleTransportButtons === 'emboss' ? RGB(20, 20, 20) : ''; - - col.transportIconNormal = pref.styleBlend || pref.styleBlend2 ? RGB(180, 180, 180) : RGB(160, 160, 160); - col.transportIconHovered = RGB(255, 255, 255); - col.transportIconDown = col.transportIconHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = RGB(50, 50, 50); - col.progressBarFill = colBrightness < 50 ? col.lightAccent : col.primary; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness > 200 ? ShadeColor(col.primary, 40) : colBrightness < 50 ? TintColor(col.primary, 50) : TintColor(col.primary, 40); - col.peakmeterBarFillTop = colBrightness < 50 ? TintColor(col.primary, 20) : TintColor(col.primary, 10); - col.peakmeterBarFillMiddle = colBrightness < 50 ? TintColor(col.primary, 40) : TintColor(col.primary, 30); - col.peakmeterBarFillBack = colBrightness < 50 ? TintColor(col.primary, 30) : ShadeColor(col.primary, 15); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = colBrightness < 50 ? TintColor(col.primary, 20) : ShadeColor(col.primary, 10); - col.peakmeterBarVertFillPeaks = colBrightness < 50 ? TintColor(col.primary, 30) : TintColor(col.primary, 20); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = colBrightness < 50 ? TintColor(col.primary, 40) : colBrightness < 100 ? TintColor(col.primary, 20) : col.primary; - col.waveformBarFillBack = colBrightness < 50 ? TintColor(col.primary, 20) : colBrightness < 100 ? col.primary : ShadeColor(col.primary, 20); - col.waveformBarFillPreFront = RGB(100, 100, 100); - col.waveformBarFillPreBack = RGB(80, 80, 80); - col.waveformBarIndicator = colBrightness > 200 ? RGB(255, 255, 255) : RGB(220, 220, 220); - - // * VOLUME BAR COLORS * // - col.volumeBar = RGB(35, 35, 35); - col.volumeBarFill = colBrightness < 50 ? col.lightAccent : col.primary; - col.volumeBarFrame = RGB(60, 60, 60); - - // * STYLE COLORS * // - col.styleProgressBarLineTop = - pref.styleProgressBar === 'bevel' ? RGBA(0, 0, 0, 255) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? pref.styleBevel ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 150) : - pref.styleBevel ? RGBA(0, 0, 0, 255) : RGBA(0, 0, 0, 100) : ''; - - col.styleProgressBarLineBottom = - pref.styleProgressBar === 'bevel' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(255, 255, 255, 30) : - pref.styleBevel ? RGBA(255, 255, 255, 30) : RGBA(255, 255, 255, 25) : - pref.styleProgressBar === 'inner' ? pref.styleProgressBarDesign === 'rounded' ? RGBA(255, 255, 255, 30) : - pref.styleBevel ? RGBA(255, 255, 255, 45) : - pref.styleBlend || pref.styleBlend2 ? RGBA(255, 255, 255, 25) : - colBrightness < 50 ? RGBA(255, 255, 255, 15) : RGBA(255, 255, 255, 40) : ''; -} - - -//////////////////////////////////// -// * STYLE REBORN FUSION COLORS * // -//////////////////////////////////// -/** - * Active Reborn theme used in Options > Style > Reborn fusion. - */ -function styleRebornFusionColors() { - if (!(fb.IsPlaying && isColored)) return; - const smallColDiff = ColorDistance(col.primary, col.primary_alt) < 100; - - // * PLAYLIST COLORS * // - g_pl_colors.header_nowplaying_bg = pref.styleBlend ? RGBtoRGBA(col.lightAccent_7_alt, 130) : col.lightAccent_7_alt; - g_pl_colors.header_sideMarker = smallColDiff ? col.lightAccent_35_alt : col.primary_alt; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - - // * MAIN COLORS * // - col.bg = col.primary_alt; - col.uiHacksFrame = col.bg; - col.transportEllipseBg = col.lightAccent_50_alt; - col.transportEllipseNormal = col.lightAccent_alt; - col.transportEllipseHovered = col.lightAccent_50_alt; - col.transportEllipseDown = col.transportEllipseHovered; - - // * PROGRESS BAR COLORS * // - col.progressBar = col.accent_alt; - col.progressBarFill = smallColDiff ? col.lightAccent_50 : col.primary; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness2 > 200 ? ShadeColor(col.primary_alt, 100) : colBrightness2 < 75 ? TintColor(col.primary_alt, 100) : TintColor(col.primary_alt, 40); - col.peakmeterBarFillTop = colBrightness2 > 200 ? ShadeColor(col.primary_alt, 10) : TintColor(col.primary_alt, 40); - col.peakmeterBarFillMiddle = colBrightness2 > 200 ? ShadeColor(col.primary_alt, 20) : TintColor(col.primary_alt, 60); - col.peakmeterBarFillBack = colBrightness2 > 200 ? ShadeColor(col.primary_alt, 40) : TintColor(col.primary_alt, 80); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = smallColDiff ? col.lightAccent_50 : col.primary; - col.peakmeterBarVertFillPeaks = TintColor(col.primary, 60); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = colBrightness2 > 200 || noAlbumArtStub ? ShadeColor(col.primary_alt, 80) : TintColor(col.primary_alt, 90); - col.waveformBarFillBack = colBrightness2 > 200 || noAlbumArtStub ? ShadeColor(col.primary_alt, 40) : TintColor(col.primary_alt, 45); - col.waveformBarFillPreFront = colBrightness2 > 150 ? ShadeColor(col.primary_alt, 40) : TintColor(col.primary_alt, 50); - col.waveformBarFillPreBack = colBrightness2 > 150 ? ShadeColor(col.primary_alt, 20) : TintColor(col.primary_alt, 25); - col.waveformBarIndicator = colBrightness2 > 200 || noAlbumArtStub ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = col.transportEllipseBg; - col.volumeBarFrame = col.volumeBar; - col.volumeBarFill = col.progressBarFill; -} - - -////////////////////////////////////// -// * STYLE REBORN FUSION 2 COLORS * // -////////////////////////////////////// -/** - * Active Reborn theme used in Options > Style > Reborn fusion 2. - */ -function styleRebornFusion2Colors() { - if (!(fb.IsPlaying && isColored)) return; - const smallColDiff = ColorDistance(col.primary, col.primary_alt) < 100; - - // * PLAYLIST COLORS * // - g_pl_colors.bg = col.primary_alt; - g_pl_colors.plman_bg = g_pl_colors.bg; - g_pl_colors.plman_text_normal = g_pl_colors.bg; - g_pl_colors.header_nowplaying_bg = pref.styleBlend ? RGBtoRGBA(col.lightAccent_7, 130) : TintColor(col.primary_alt, 10); - g_pl_colors.header_sideMarker = smallColDiff ? col.lightAccent_35 : col.primary; - g_pl_colors.row_nowplaying_bg = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_sideMarker = smallColDiff ? col.lightAccent_35 : col.primary; - - // * LIBRARY COLORS * // - ui.col.bg = g_pl_colors.bg; - ui.col.rowStripes = g_pl_colors.row_stripes_bg; - ui.col.nowPlayingBg = ppt.albumArtShow ? TintColor(g_pl_colors.row_nowplaying_bg, 7) : g_pl_colors.row_nowplaying_bg; - ui.col.sideMarker = g_pl_colors.row_sideMarker; - ui.col.selectionFrame = g_pl_colors.row_selection_frame; - ui.col.selectionFrame2 = ui.col.sideMarker; - ui.col.hoverFrame = ui.col.sideMarker; - - // * BIOGRAPHY COLORS * // - uiBio.col.bg = g_pl_colors.bg; - uiBio.col.rowStripes = g_pl_colors.row_stripes_bg; - - // * MAIN COLORS * // - col.bg = col.primary; - col.detailsBg = col.primary_alt !== RGB(25, 160, 240) && albumArt && !isStreaming ? col.primary_alt : RGB(255, 255, 255); - col.detailsText = - isStreaming || isPlayingCD || !albumArt ? RGB(120, 120, 120) : - lightBgDetails ? RGB(55, 55, 55) : - RGB(255, 255, 255); - - col.transportEllipseBg = col.lightAccent_65; - col.transportEllipseNormal = col.lightAccent; - col.transportEllipseHovered = col.lightAccent_50; - col.transportEllipseDown = col.transportEllipseHovered; - - col.progressBar = pref.styleBevel ? ShadeColor(col.primary_alt, 5) : col.lightAccent; - col.progressBarFill = smallColDiff ? col.lightAccent_35 : col.primary_alt; - col.volumeBar = col.transportEllipseBg; - col.volumeBarFrame = col.volumeBar; - col.volumeBarFill = smallColDiff ? col.lightAccent_35 : col.primary; -} - - -/////////////////////////////////////////// -// * STYLE REBORN FUSION ACCENT COLORS * // -/////////////////////////////////////////// -/** - * Active Reborn theme used in Options > Style > Reborn fusion accent. - */ -function styleRebornFusionAccentColors() { - if (!(fb.IsPlaying && isColored)) return; - const smallColDiff = ColorDistance(col.primary, col.primary_alt) < 100; - - g_pl_colors.header_nowplaying_bg = smallColDiff ? colBrightness > 150 ? col.darkAccent_50_alt : col.lightAccent_50_alt : col.primary_alt; - g_pl_colors.header_sideMarker = g_pl_colors.header_nowplaying_bg; - g_pl_colors.row_nowplaying_bg = col.primary_alt; - g_pl_colors.row_sideMarker = g_pl_colors.header_sideMarker; - ui.col.sideMarker = g_pl_colors.header_sideMarker; - - col.progressBarFill = smallColDiff ? colBrightness > 150 ? col.darkAccent_50_alt : col.lightAccent_50_alt : col.primary_alt; - - // * PEAKMETER BAR COLORS * // - col.peakmeterBarProg = col.progressBar; - col.peakmeterBarProgFill = colBrightness > 150 ? ShadeColor(col.primary_alt, smallColDiff ? 80 : 50) : TintColor(col.primary_alt, smallColDiff ? 80 : 50); - col.peakmeterBarFillTop = colBrightness > 150 ? ShadeColor(col.primary_alt, smallColDiff ? 40 : 10) : TintColor(col.primary_alt, smallColDiff ? 40 : 10); - col.peakmeterBarFillMiddle = colBrightness > 150 ? ShadeColor(col.primary_alt, smallColDiff ? 30 : 0) : TintColor(col.primary_alt, smallColDiff ? 30 : 0); - col.peakmeterBarFillBack = colBrightness > 150 ? ShadeColor(col.primary_alt, smallColDiff ? 60 : 30) : TintColor(col.primary_alt, smallColDiff ? 60 : 30); - col.peakmeterBarVertProgFill = col.progressBarFill; - col.peakmeterBarVertFill = col.progressBarFill; - col.peakmeterBarVertFillPeaks = TintColor(col.primary_alt, 60); - if (peakmeterBar) peakmeterBar.setColors(fb.GetNowPlaying()); - - // * WAVEFORM BAR COLORS * // - col.waveformBarFillFront = colBrightness > 150 ? ShadeColor(col.primary_alt, smallColDiff || colBrightness > 200 || noAlbumArtStub ? 60 : 10) : TintColor(col.primary_alt, smallColDiff ? 60 : 10); - col.waveformBarFillBack = colBrightness > 150 ? ShadeColor(col.primary_alt, smallColDiff || colBrightness > 200 || noAlbumArtStub ? 80 : 20) : TintColor(col.primary_alt, smallColDiff ? 80 : 20); - col.waveformBarFillPreFront = colBrightness > 150 ? ShadeColor(col.primary, 20) : TintColor(col.primary, 30); - col.waveformBarFillPreBack = colBrightness > 150 ? ShadeColor(col.primary, 30) : TintColor(col.primary, 40); - col.waveformBarIndicator = colBrightness > 200 || noAlbumArtStub ? RGB(0, 0, 0) : RGB(255, 255, 255); - - // * VOLUME BAR COLORS * // - col.volumeBar = col.transportEllipseBg; - col.volumeBarFrame = col.volumeBar; - col.volumeBarFill = col.progressBarFill; -} - - -/////////////////////////// -// * INITIALIZE COLORS * // -/////////////////////////// -/** - * Init all colors that are used in the Playlist, mostly called from initTheme(). - */ -function initPlaylistColors() { - switch (pref.theme) { - case 'white': playlistColorsWhiteTheme(); break; - case 'black': playlistColorsBlackTheme(); break; - case 'reborn': case 'random': playlistColorsRebornRandomTheme(); break; - case 'blue': playlistColorsBlueTheme(); break; - case 'darkblue': playlistColorsDarkblueTheme(); break; - case 'red': playlistColorsRedTheme(); break; - case 'cream': playlistColorsCreamTheme(); break; - case 'nblue': case 'ngreen': case 'nred': case 'ngold': playlistColorsNeonThemes(); break; - default: if (pref.theme.startsWith('custom')) playlistColorsCustomTheme(); break; - } -} - - -/** - * Init all colors that are used in the Library, mostly called from initTheme(). - */ -function initLibraryColors() { - switch (pref.theme) { - case 'white': libraryColorsWhiteTheme(); break; - case 'black': libraryColorsBlackTheme(); break; - case 'reborn': case 'random': libraryColorsRebornRandomTheme(); break; - case 'blue': libraryColorsBlueTheme(); break; - case 'darkblue': libraryColorsDarkblueTheme(); break; - case 'red': libraryColorsRedTheme(); break; - case 'cream': libraryColorsCreamTheme(); break; - case 'nblue': case 'ngreen': case 'nred': case 'ngold': libraryColorsNeonThemes(); break; - default: if (pref.theme.startsWith('custom')) libraryColorsCustomTheme(); break; - } - if (ppt.theme !== 0) libraryThemeColors(); -} - - -/** - * Init all colors that are used in the Biography, mostly called from initTheme(). - */ -function initBiographyColors() { - switch (pref.theme) { - case 'white': biographyColorsWhiteTheme(); break; - case 'black': biographyColorsBlackTheme(); break; - case 'reborn': case 'random': biographyColorsRebornRandomTheme(); break; - case 'blue': biographyColorsBlueTheme(); break; - case 'darkblue': biographyColorsDarkblueTheme(); break; - case 'red': biographyColorsRedTheme(); break; - case 'cream': biographyColorsCreamTheme(); break; - case 'nblue': case 'ngreen': case 'nred': case 'ngold': biographyColorsNeonThemes(); break; - default: if (pref.theme.startsWith('custom')) biographyColorsCustomTheme(); break; - } - if (pptBio.theme !== 0) biographyThemeColors(); -} - - -/** - * Init all colors that are used in Georgia-ReBORN main, mostly called from initTheme(). - */ -function initMainColors() { - switch (pref.theme) { - case 'white': mainColorsWhiteTheme(); break; - case 'black': mainColorsBlackTheme(); break; - case 'reborn': case 'random': mainColorsRebornRandomTheme(); break; - case 'blue': mainColorsBlueTheme(); break; - case 'darkblue': mainColorsDarkblueTheme(); break; - case 'red': mainColorsRedTheme(); break; - case 'cream': mainColorsCreamTheme(); break; - case 'nblue': case 'ngreen': case 'nred': case 'ngold': mainColorsNeonThemes(); break; - default: if (pref.theme.startsWith('custom')) mainColorsCustomTheme(); break; - } -} - - -/** - * Init all colors that are used in styles, mostly called from initTheme(). - */ -function initStyleColors() { - if ((['reborn', 'random'].includes(pref.theme) || pref.theme.startsWith('custom')) && - (pref.styleNighttime || pref.themeDayNightMode && pref.themeDayNightTime === 'night') && !pref.styleRebornWhite) { - styleNighttimeColors(); - } - - switch (true) { - case pref.styleAlternative: styleAlternativeColors(); break; - case pref.styleAlternative2: styleAlternative2Colors(); break; - case pref.styleBlackAndWhite: styleBlackAndWhiteColors(); break; - case pref.styleBlackAndWhite2: styleBlackAndWhite2Colors(); break; - case pref.styleBlackReborn: styleBlackRebornColors(); break; - case pref.styleRebornWhite: styleRebornWhiteColors(); break; - case pref.styleRebornBlack: styleRebornBlackColors(); break; - case pref.styleRebornFusion: styleRebornFusionColors(); break; - case pref.styleRebornFusion2: styleRebornFusion2Colors(); break; - case pref.styleRebornFusionAccent: styleRebornFusionAccentColors(); break; - } -} - - -/** - * Init style Black And White Reborn, dynamically change between style Black and white 1 and 2. - */ -function initBlackAndWhiteReborn() { - setImageBrightness(); - - if (imgBrightness > 150) { - pref.styleBlackAndWhite2 = true; // White background - pref.styleBlackAndWhite = false; - } - else { - pref.styleBlackAndWhite = true; // Black background - pref.styleBlackAndWhite2 = false; - } -} - - -/** - * Init all colors that are used in the chronflow user-component, mostly called from initTheme(). - */ -function initChronflowColors() { - if (!componentChronFlow) return; - try { - const chron = new ActiveXObject('chron.IChronControl'); - if (chron) { - let r_bg = 255; - let g_bg = 255; - let b_bg = 255; - - if (g_pl_colors.bg !== RGB(255, 255, 255)) { - const bg_rgb = Math.abs(g_pl_colors.bg); - - r_bg = GetRed(bg_rgb); - g_bg = GetGreen(bg_rgb); - b_bg = GetBlue(bg_rgb); - - r_bg = 255 - r_bg; - g_bg = 255 - g_bg; - b_bg = 255 - b_bg; - } - else { - r_bg = 255; - g_bg = 255; - b_bg = 255; - } - - // * SetPanelColor - const bg = (b_bg << 16) | (g_bg << 8) | r_bg; - chron.SetPanelColor(bg, /*skip refresh*/ [0]); - - // * SetTextColor - r_bg = GetRed(col.lowerBarArtist); - g_bg = GetGreen(col.lowerBarArtist); - b_bg = GetBlue(col.lowerBarArtist); - - let strhex = '0x'; - const rgbtohex = RGBtoHEX(b_bg, g_bg, r_bg); - strhex = strhex.concat(rgbtohex); - chron.SetTextColor(strhex, /*refresh*/ [1]); - } - } - catch (e) { - // DebugLog('Unable to create ActiveX chron.IChronControl object'); - } -} - - -////////////////////////// -// * SET THEME COLORS * // -////////////////////////// -/** - * Sets Main, Playlist, Details, Library and Biography background color brightness rules. - * Based on background color and image brightness, text colors in theme will change accordingly to black or white. - * Used in White, Black, Reborn, Random and Custom themes. - */ -function setBackgroundColorDefinition() { - colBrightness = new Color(col.primary).brightness; - colBrightness2 = new Color(col.primary_alt).brightness; - const colBrightnessGrad = (pref.styleRebornFusion ? colBrightness2 : colBrightness) - (new Color(RGBAtoRGB(col.styleGradient)).brightness * 0.5); - const colBrightnessGrad2 = (pref.styleRebornFusion ? colBrightness2 : colBrightness) - (new Color(RGBAtoRGB(col.styleGradient2)).brightness * 0.5); - const customThemes = pref.theme.startsWith('custom'); - - // * STANDARD THEMES * // - if (['white', 'black', 'reborn', 'random', 'cream'].includes(pref.theme) && !pref.styleRebornFusion && !pref.styleRebornFusion2) { - lightBg = - noAlbumArtStub && (pref.theme === 'white' && !pref.styleBlackAndWhite || pref.theme === 'reborn' || pref.theme === 'random') - || - colBrightness + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) && - ((pref.theme === 'white' || pref.theme === 'black') && colBrightness > 150 || pref.theme === 'reborn' || pref.theme === 'random' && !pref.styleRandomDark) - || - colBrightness > 150 && !pref.styleBlend && !pref.styleBlend2 && !pref.styleRandomDark - || - pref.theme === 'cream'; - } - - // * GRADIENT STYLES, REBORN FUSION STYLES, CUSTOM THEMES * // - if (!(pref.styleGradient || pref.styleGradient2 || pref.styleRebornFusion || pref.styleRebornFusion2 || pref.styleRebornFusionAccent || customThemes)) { - return; - } - - const mainBgColor = pref.styleGradient ? colBrightnessGrad : pref.styleGradient2 ? colBrightnessGrad2 : - new Color(pref.styleRebornFusion2 ? col.primary : customThemes ? HEXtoRGB(customColor.col_bg) : col.primary_alt).brightness; - const playlistBgColor = new Color(pref.styleRebornFusion2 ? col.primary_alt : customThemes ? HEXtoRGB(customColor.g_pl_colors_bg) : col.primary).brightness; - const detailsBgColor = new Color(pref.styleRebornFusion2 ? col.primary_alt : customThemes ? HEXtoRGB(customColor.col_detailsBg) : col.primary).brightness; - const libraryBgColor = new Color(pref.styleRebornFusion2 ? col.primary_alt : customThemes ? HEXtoRGB(customColor.ui_col_bg) : col.primary).brightness; - const biographyBgColor = new Color(pref.styleRebornFusion2 ? col.primary_alt : customThemes ? HEXtoRGB(customColor.uiBio_col_bg) : col.primary).brightness; - - const isLightBg = (color, brightness) => - color + imgBrightness > 285 && (pref.styleBlend || pref.styleBlend2) || - color > 150 && (!pref.styleBlend && !pref.styleBlend2 || pref.styleBlend2 && pref.styleRebornFusion2); - - lightBgMain = isLightBg(mainBgColor, imgBrightness); - lightBgPlaylist = isLightBg(playlistBgColor, imgBrightness); - lightBgDetails = isLightBg(detailsBgColor, imgBrightness); - lightBgLibrary = isLightBg(libraryBgColor, imgBrightness); - lightBgBiography = isLightBg(biographyBgColor, imgBrightness); -} - - -/** - * Sets style Blend 1 and 2, blurs and blends album art image in the background. - */ -function setStyleBlend() { - const setStyleBlendProfiler = timings.showDebugTiming && fb.CreateProfiler('setStyleBlend'); - - const blurImage = (image, blurLevel) => { - switch (pref.theme) { - case 'white': blurLevel = 100; break; - case 'black': blurLevel = 150; break; - - case 'reborn': case 'random': - switch (true) { - case imgBrightness > 125: blurLevel = 250; break; - case imgBrightness > 100: blurLevel = 220; break; - case imgBrightness > 75: blurLevel = 200; break; - case imgBrightness > 50: blurLevel = 220; break; - case imgBrightness > 0: blurLevel = 200; break; - } - break; - - default: blurLevel = 250; break; - } - - image.StackBlur(blurLevel); - - if (settings.showDebugThemeLog) console.log(`Blended image blur: ${blurLevel}`); - if (settings.showDebugThemeOverlay) blendedImgBlur = blurLevel; - - return image; - } - - const formatBlendedImg = (image, imgW, imgH, angle, alpha) => { - if (!image || !imgW || !imgH) return image; - - let tempImg = gdi.CreateImage(imgW, imgH); - const g = tempImg.GetGraphics(); - - angle = 0; - - switch (pref.theme) { - case 'white': alpha = 70; break; - case 'black': alpha = 50; break; - - case 'reborn': case 'random': - switch (true) { - case pref.styleRebornWhite && imgBrightness < 100: alpha = 70; break; - case pref.styleRebornWhite && imgBrightness < 75: alpha = 50; break; - case pref.styleRebornWhite && imgBrightness < 50: alpha = 30; break; - case pref.styleRebornWhite && imgBrightness < 25: alpha = 15; break; - - case pref.styleRebornBlack && imgBrightness > 240: alpha = 30; break; - case pref.styleRebornBlack && imgBrightness > 175: alpha = 100; break; - - case imgBrightness > 200: alpha = 150; break; - case imgBrightness > 150: alpha = 130; break; - case imgBrightness > 125: alpha = 120; break; - case imgBrightness > 100: alpha = 110; break; - case imgBrightness > 75: alpha = 100; break; - case imgBrightness > 50: alpha = 90; break; - case imgBrightness > 0: alpha = 80; break; - } - break; - - case 'blue': alpha = 80; break; - case 'darkblue': alpha = 70; break; - case 'red': alpha = 50; break; - case 'cream': alpha = 70; break; - - case 'nblue': case 'ngreen': case 'nred': case 'ngold': alpha = 50; break; - - default: alpha = 70; break; - } - - try { // * Prevent crash if album art is corrupt, file format is not supported or has a unusual ICC profile embedded - g.DrawImage(image, 0, 0, ww, wh, 0, 0, image.Width, image.Height, angle, alpha); - } catch (e) { - console.log('\n\n'); - } - tempImg.ReleaseGraphics(g); - tempImg = blurImage(tempImg); - - if (settings.showDebugThemeLog) console.log(`Blended image alpha: ${alpha}\nTheme brightness: ${pref.themeBrightness}`); - if (settings.showDebugThemeOverlay) blendedImgAlpha = alpha; - - return tempImg; - } - - blendedImg = formatBlendedImg(albumArt, ww, wh, 100, fb.GetNowPlaying()); - - if (setStyleBlendProfiler) setStyleBlendProfiler.Print(); -} - - -/** - * Sets calculated image brightness from album art, mainly used when using style Blend 1 and 2 or style Black and white reborn. - */ -function setImageBrightness() { - if (albumArt && (ppt.theme !== 0 || pref.styleBlend || pref.styleBlend2 || pref.styleBlackAndWhite || pref.styleBlackAndWhite2 || pref.styleBlackAndWhiteReborn)) { - imgBrightness = CalcImgBrightness(albumArt); - } -} - - -/** - * Sets noAlbumArtColors, change col.primary when streaming, reset to default when playing from CD or using noAlbumArtStub. - */ -function setNoAlbumArtColors() { - if (isStreaming && (['white', 'black', 'reborn', 'random'].includes(pref.theme))) { - col.primary = RGB(207, 0, 5); - } - if (isPlayingCD || noAlbumArtStub) { - if (!isStreaming) setThemeColors(); - uiBio.updateProp(1); // Needed to update color for NO PHOTO/COVER stub in Biography when changing themes - } -} - - -/** - * Sets primary and optional secondary theme color as well as accents. - * @param {number} color The primary color. - * @param {number} color2 The secondary color. - */ -function setTheme(color, color2) { - if (color2 === undefined) color2 = color; - let themeCol = new Color(color.primary); - const customThemes = pref.theme.startsWith('custom'); - - if (ColorDistance(color.primary, col.bg, true) < (themeCol.isCloseToGreyscale ? 60 : 45) && - (pref.theme !== 'reborn' && pref.theme !== 'random' && (pref.theme !== 'black' && !pref.styleBlackReborn) && !customThemes)) { - if (settings.showDebugThemeLog) console.log('>>> Theme primary color is too close to bg color. Tinting theme color.'); - color.primary = TintColor(color.primary, 15); - color.accent = TintColor(color.primary, 10); - themeCol = new Color(color.primary); - } - col.primary = color.primary; - col.primary_alt = color2.primary_alt; - - if (ColorDistance(color.primary, col.progressBar, true) < (themeCol.isCloseToGreyscale ? 60 : 45)) { - // Progress bar fill is too close in color to bg - if (settings.showDebugThemeLog) console.log('>>> Theme primary color is too close to progress bar. Adjusting progressBar'); - if (pref.theme === 'white' && themeCol.brightness < 125) { - col.progressBar = RGB(180, 180, 180); - } - } - - if (str.timeline) { - str.timeline.setColors(col.timelineAdded, col.timelinePlayed, col.timelineUnplayed); - } - - col.primary = color.primary; - col.primary_alt = color2.primary_alt; - - // * Reborn/Random theme main tone palette - col.darkAccent_100 = ShadeColor(color.primary, 100); - col.darkAccent_100_alt = ShadeColor(color2.primary_alt, 100); - col.darkAccent_75 = ShadeColor(color.primary, 75); - col.darkAccent_75_alt = ShadeColor(color2.primary_alt, 75); - col.darkAccent_65 = ShadeColor(color.primary, 65); - col.darkAccent_65_alt = ShadeColor(color2.primary_alt, 65); - col.darkAccent_50 = ShadeColor(color.primary, 50); - col.darkAccent_50_alt = ShadeColor(color2.primary_alt, 50); - col.darkAccent = color.darkAccent; - col.darkAccent_alt = color2.darkAccent_alt; - col.accent = color.accent; - col.accent_alt = color2.accent_alt; - - col.lightAccent_2 = TintColor(color.primary, 2); - col.lightAccent_2_alt = TintColor(color2.primary_alt, 2); - col.lightAccent_7 = TintColor(color.primary, 7); - col.lightAccent_7_alt = TintColor(color2.primary_alt, 7); - col.lightAccent_10 = TintColor(color.primary, 10); - col.lightAccent_10_alt = TintColor(color2.primary_alt, 10); - - col.lightAccent = color.lightAccent; - col.lightAccent_alt = color2.lightAccent_alt; - col.lightAccent_35 = TintColor(color.primary, 35); - col.lightAccent_35_alt = TintColor(color2.primary_alt, 35); - col.lightAccent_50 = TintColor(color.primary, 50); - col.lightAccent_50_alt = TintColor(color2.primary_alt, 50); - col.lightAccent_65 = TintColor(color.primary, 65); - col.lightAccent_65_alt = TintColor(color2.primary_alt, 65); - col.lightAccent_80 = TintColor(color.primary, 80); - col.lightAccent_80_alt = TintColor(color2.primary_alt, 80); - col.lightAccent_100 = TintColor(color.primary, 100); - col.lightAccent_100_alt = TintColor(color2.primary_alt, 100); - - // * Change col.primary if too bright or too dark - if (pref.theme === 'white' && (ColorDistance(col.primary, col.progressBar)) < 60) { - col.primary = col.darkAccent; - } - // else if (pref.theme === 'black' && !pref.styleBlackReborn && (ColorDistance(col.primary, col.bg)) < 50) { - // col.primary = RGB(175, 205, 225); - // } - // else if (ColorDistance(col.primary, col.bg) < 60) { - // col.primary = col.darkAccent; - // } -} - - -/** - * Sets default theme colors, used on startup when nothing has been played or using noAlbumArtStub. - */ -function setThemeColors() { - switch (pref.theme) { - case 'white': setTheme(whiteTheme.colors); break; - case 'black': setTheme(blackTheme.colors); break; - case 'reborn': setTheme(rebornTheme.colors); break; - case 'random': setTheme(randomTheme.colors); break; - case 'blue': setTheme(blueTheme.colors); break; - case 'darkblue': setTheme(darkblueTheme.colors); break; - case 'red': setTheme(redTheme.colors); break; - case 'cream': setTheme(creamTheme.colors); break; - case 'nblue': setTheme(nblueTheme.colors); break; - case 'ngreen': setTheme(ngreenTheme.colors); break; - case 'nred': setTheme(nredTheme.colors); break; - case 'ngold': setTheme(ngoldTheme.colors); break; - default: if (pref.theme.startsWith('custom')) setTheme(customTheme.colors); break; - } -} - - -/** - * Sets and saves currently used colors, used when transferring colors to a custom theme. - * @param {string} slot The custom theme slot in which to save. - */ -function setCurrentColorsToCustomTheme(slot) { - const currentColors = { - // * PLAYLIST COLORS * // - g_pl_colors_bg: RGBFtoHEX(g_pl_colors.bg), - g_pl_colors_plman_text_normal: RGBFtoHEX(g_pl_colors.plman_text_normal), - g_pl_colors_plman_text_hovered: RGBFtoHEX(g_pl_colors.plman_text_hovered), - g_pl_colors_plman_text_pressed: RGBFtoHEX(g_pl_colors.plman_text_pressed), - g_pl_colors_header_nowplaying_bg: RGBFtoHEX(g_pl_colors.header_nowplaying_bg), - g_pl_colors_header_sideMarker: RGBFtoHEX(g_pl_colors.header_sideMarker), - g_pl_colors_header_artist_normal: RGBFtoHEX(g_pl_colors.header_artist_normal), - g_pl_colors_header_artist_playing: RGBFtoHEX(g_pl_colors.header_artist_playing), - g_pl_colors_header_album_normal: RGBFtoHEX(g_pl_colors.header_album_normal), - g_pl_colors_header_album_playing: RGBFtoHEX(g_pl_colors.header_album_playing), - g_pl_colors_header_info_normal: RGBFtoHEX(g_pl_colors.header_info_normal), - g_pl_colors_header_info_playing: RGBFtoHEX(g_pl_colors.header_info_playing), - g_pl_colors_header_date_normal: RGBFtoHEX(g_pl_colors.header_date_normal), - g_pl_colors_header_date_playing: RGBFtoHEX(g_pl_colors.header_date_playing), - g_pl_colors_header_line_normal: RGBFtoHEX(g_pl_colors.header_line_normal), - g_pl_colors_header_line_playing: RGBFtoHEX(g_pl_colors.header_line_playing), - g_pl_colors_row_nowplaying_bg: RGBFtoHEX(g_pl_colors.row_nowplaying_bg), - g_pl_colors_row_stripes_bg: RGBFtoHEX(g_pl_colors.row_stripes_bg), - g_pl_colors_row_selection_frame: RGBFtoHEX(g_pl_colors.row_selection_frame), - g_pl_colors_row_sideMarker: RGBFtoHEX(g_pl_colors.row_sideMarker), - g_pl_colors_row_title_normal: RGBFtoHEX(g_pl_colors.row_title_normal), - g_pl_colors_row_title_playing: RGBFtoHEX(g_pl_colors.row_title_playing), - g_pl_colors_row_title_selected: RGBFtoHEX(g_pl_colors.row_title_selected), - g_pl_colors_row_title_hovered: RGBFtoHEX(g_pl_colors.row_title_hovered), - g_pl_colors_row_rating_color: RGBFtoHEX(g_pl_colors.row_rating_color), - g_pl_colors_row_disc_subheader_line: RGBFtoHEX(g_pl_colors.row_disc_subheader_line), - g_pl_colors_row_drag_line: RGBFtoHEX(g_pl_colors.row_drag_line), - g_pl_colors_row_drag_line_reached: RGBFtoHEX(g_pl_colors.row_drag_line_reached), - g_pl_colors_sbar_btn_normal: RGBFtoHEX(g_pl_colors.sbar_btn_normal), - g_pl_colors_sbar_btn_hovered: RGBFtoHEX(g_pl_colors.sbar_btn_hovered), - g_pl_colors_sbar_thumb_normal: RGBFtoHEX(g_pl_colors.sbar_thumb_normal), - g_pl_colors_sbar_thumb_hovered: RGBFtoHEX(g_pl_colors.sbar_thumb_hovered), - g_pl_colors_sbar_thumb_drag: RGBFtoHEX(g_pl_colors.sbar_thumb_drag), - - // * LIBRARY COLORS * // - ui_col_bg: RGBFtoHEX(ui.col.bg), - ui_col_rowStripes: RGBFtoHEX(ui.col.rowStripes), - ui_col_nowPlayingBg: RGBFtoHEX(ui.col.nowPlayingBg), - ui_col_sideMarker: RGBFtoHEX(ui.col.sideMarker), - ui_col_selectionFrame: RGBFtoHEX(ui.col.selectionFrame), - ui_col_selectionFrame2: RGBFtoHEX(ui.col.selectionFrame2), - ui_col_hoverFrame: RGBFtoHEX(ui.col.hoverFrame), - ui_col_iconPlus: RGBFtoHEX(ui.col.iconPlus), - ui_col_iconPlus_h: RGBFtoHEX(ui.col.iconPlus_h), - ui_col_iconPlus_sel: RGBFtoHEX(ui.col.iconPlus_sel), - ui_col_iconPlusBg: RGBFtoHEX(ui.col.iconPlusBg), - ui_col_iconMinus_e: RGBFtoHEX(ui.col.iconMinus_e), - ui_col_iconMinus_c: RGBFtoHEX(ui.col.iconMinus_c), - ui_col_iconMinus_h: RGBFtoHEX(ui.col.iconMinus_h), - ui_col_text: RGBFtoHEX(ui.col.text), - ui_col_text_h: RGBFtoHEX(ui.col.text_h), - ui_col_text_nowp: RGBFtoHEX(ui.col.text_nowp), - ui_col_textSel: RGBFtoHEX(ui.col.textSel), - ui_col_txt: RGBFtoHEX(ui.col.txt), - ui_col_txt_h: RGBFtoHEX(ui.col.txt_h), - ui_col_txt_box: RGBFtoHEX(ui.col.txt_box), - ui_col_search: RGBFtoHEX(ui.col.search), - ui_col_searchBtn: RGBFtoHEX(ui.col.searchBtn), - ui_col_crossBtn: RGBFtoHEX(ui.col.crossBtn), - ui_col_filterBtn: RGBFtoHEX(ui.col.filterBtn), - ui_col_settingsBtn: RGBFtoHEX(ui.col.settingsBtn), - ui_col_line: RGBFtoHEX(ui.col.line), - ui_col_s_line: RGBFtoHEX(ui.col.s_line), - ui_col_sbarBtns: RGBFtoHEX(ui.col.sbarBtns), - ui_col_sbarNormal: RGBFtoHEX(ui.col.sbarNormal), - ui_col_sbarHovered: RGBFtoHEX(ui.col.sbarHovered), - ui_col_sbarDrag: RGBFtoHEX(ui.col.sbarDrag), - - // * BIOGRAPHY COLORS * // - uiBio_col_bg: RGBFtoHEX(uiBio.col.bg), - uiBio_col_rowStripes: RGBFtoHEX(uiBio.col.rowStripes), - uiBio_col_headingText: RGBFtoHEX(uiBio.col.headingText), - uiBio_col_bottomLine: RGBFtoHEX(uiBio.col.bottomLine), - uiBio_col_centerLine: RGBFtoHEX(uiBio.col.centerLine), - uiBio_col_sectionLine: RGBFtoHEX(uiBio.col.sectionLine), - uiBio_col_accent: RGBFtoHEX(uiBio.col.accent), - uiBio_col_source: RGBFtoHEX(uiBio.col.source), - uiBio_col_summary: RGBFtoHEX(uiBio.col.summary), - uiBio_col_text: RGBFtoHEX(uiBio.col.text), - uiBio_col_lyricsNormal: RGBFtoHEX(uiBio.col.lyricsNormal), - uiBio_col_lyricsHighlight: RGBFtoHEX(uiBio.col.lyricsHighlight), - uiBio_col_noPhotoStubBg: RGBFtoHEX(uiBio.col.noPhotoStubBg), - uiBio_col_noPhotoStubText: RGBFtoHEX(uiBio.col.noPhotoStubText), - uiBio_col_sbarBtns: RGBFtoHEX(uiBio.col.sbarBtns), - uiBio_col_sbarNormal: RGBFtoHEX(uiBio.col.sbarNormal), - uiBio_col_sbarHovered: RGBFtoHEX(uiBio.col.sbarHovered), - uiBio_col_sbarDrag: RGBFtoHEX(uiBio.col.sbarDrag), - - // * MAIN COLORS * // - col_bg: RGBFtoHEX(col.bg), - col_shadow: RGBFtoHEX(col.shadow), - col_discArtShadow: RGBFtoHEX(col.discArtShadow), - col_noAlbumArtStub: RGBFtoHEX(col.noAlbumArtStub), - col_lowerBarArtist: RGBFtoHEX(col.lowerBarArtist), - col_lowerBarTitle: RGBFtoHEX(col.lowerBarTitle), - col_lowerBarTime: RGBFtoHEX(col.lowerBarTime), - col_lowerBarLength: RGBFtoHEX(col.lowerBarLength), - col_lyricsNormal: RGBFtoHEX(col.lyricsNormal), - col_lyricsHighlight: RGBFtoHEX(col.lyricsHighlight), - col_lyricsShadow: RGBFtoHEX(col.lyricsShadow), - col_detailsBg: RGBFtoHEX(col.detailsBg), - col_detailsText: RGBFtoHEX(col.detailsText), - col_detailsRating: RGBFtoHEX(col.detailsRating), - col_timelineAdded: RGBFtoHEX(col.timelineAdded), - col_timelinePlayed: RGBFtoHEX(col.timelinePlayed), - col_timelineUnplayed: RGBFtoHEX(col.timelineUnplayed), - col_timelineFrame: RGBFtoHEX(col.timelineFrame), - col_popupBg: RGBFtoHEX(col.popupBg), - col_popupText: RGBFtoHEX(col.popupText), - col_menuBgColor: RGBFtoHEX(col.menuBgColor), - col_menuStyleBg: RGBFtoHEX(col.menuStyleBg), - col_menuRectStyleEmbossTop: RGBFtoHEX(col.menuRectStyleEmbossTop), - col_menuRectStyleEmbossBottom: RGBFtoHEX(col.menuRectStyleEmbossBottom), - col_menuRectNormal: RGBFtoHEX(col.menuRectNormal), - col_menuRectHovered: RGBFtoHEX(col.menuRectHovered), - col_menuRectDown: RGBFtoHEX(col.menuRectDown), - col_menuTextNormal: RGBFtoHEX(col.menuTextNormal), - col_menuTextHovered: RGBFtoHEX(col.menuTextHovered), - col_menuTextDown: RGBFtoHEX(col.menuTextDown), - col_transportEllipseBg: RGBFtoHEX(col.transportEllipseBg), - col_transportEllipseNormal: RGBFtoHEX(col.transportEllipseNormal), - col_transportEllipseHovered: RGBFtoHEX(col.transportEllipseHovered), - col_transportEllipseDown: RGBFtoHEX(col.transportEllipseDown), - col_transportStyleBg: RGBFtoHEX(col.transportStyleBg), - col_transportStyleTop: RGBFtoHEX(col.transportStyleTop), - col_transportStyleBottom: RGBFtoHEX(col.transportStyleBottom), - col_transportIconNormal: RGBFtoHEX(col.transportIconNormal), - col_transportIconHovered: RGBFtoHEX(col.transportIconHovered), - col_transportIconDown: RGBFtoHEX(col.transportIconDown), - col_progressBar: RGBFtoHEX(col.progressBar), - col_progressBarStreaming: RGBFtoHEX(col.progressBarStreaming), - col_progressBarFrame: RGBFtoHEX(col.progressBarFrame), - col_progressBarFill: RGBFtoHEX(col.progressBarFill), - col_peakmeterBarProg: RGBFtoHEX(col.peakmeterBarProg), - col_peakmeterBarProgFill: RGBFtoHEX(col.peakmeterBarProgFill), - col_peakmeterBarFillTop: RGBFtoHEX(col.peakmeterBarFillTop), - col_peakmeterBarFillMiddle: RGBFtoHEX(col.peakmeterBarFillMiddle), - col_peakmeterBarFillBack: RGBFtoHEX(col.peakmeterBarFillBack), - col_peakmeterBarVertProgFill: RGBFtoHEX(col.peakmeterBarVertProgFill), - col_peakmeterBarVertFill: RGBFtoHEX(col.peakmeterBarVertFill), - col_peakmeterBarVertFillPeaks: RGBFtoHEX(col.peakmeterBarVertFillPeaks), - col_waveformBarFillFront: RGBFtoHEX(col.waveformBarFillFront), - col_waveformBarFillBack: RGBFtoHEX(col.waveformBarFillBack), - col_waveformBarFillPreFront: RGBFtoHEX(col.waveformBarFillPreFront), - col_waveformBarFillPreBack: RGBFtoHEX(col.waveformBarFillPreBack), - col_waveformBarIndicator: RGBFtoHEX(col.waveformBarIndicator), - col_volumeBar: RGBFtoHEX(col.volumeBar), - col_volumeBarFrame: RGBFtoHEX(col.volumeBarFrame), - col_volumeBarFill: RGBFtoHEX(col.volumeBarFill), - col_styleBevel: RGBFtoHEX(col.styleBevel), - col_styleGradient: RGBFtoHEX(col.styleGradient), - col_styleGradient2: RGBFtoHEX(col.styleGradient2), - col_styleProgressBar: RGBFtoHEX(col.styleProgressBar), - col_styleProgressBarLineTop: RGBFtoHEX(col.styleProgressBarLineTop), - col_styleProgressBarLineBottom: RGBFtoHEX(col.styleProgressBarLineBottom), - col_styleProgressBarFill: RGBFtoHEX(col.styleProgressBarFill), - col_styleVolumeBar: RGBFtoHEX(col.styleVolumeBar), - col_styleVolumeBarFill: RGBFtoHEX(col.styleVolumeBarFill) - } - - switch (slot) { - case 'custom01': - customTheme01 = configCustom.addConfigurationObject(customTheme01Schema, currentColors, customThemeComments); - customColor = customTheme01; - configCustom.updateConfigObjValues('customTheme01', currentColors, true); - break; - case 'custom02': - customTheme02 = configCustom.addConfigurationObject(customTheme02Schema, currentColors, customThemeComments); - customColor = customTheme02; - configCustom.updateConfigObjValues('customTheme02', currentColors, true); - break; - case 'custom03': - customTheme03 = configCustom.addConfigurationObject(customTheme03Schema, currentColors, customThemeComments); - customColor = customTheme03; - configCustom.updateConfigObjValues('customTheme03', currentColors, true); - break; - case 'custom04': - customTheme04 = configCustom.addConfigurationObject(customTheme04Schema, currentColors, customThemeComments); - customColor = customTheme04; - configCustom.updateConfigObjValues('customTheme04', currentColors, true); - break; - case 'custom05': - customTheme05 = configCustom.addConfigurationObject(customTheme05Schema, currentColors, customThemeComments); - customColor = customTheme05; - configCustom.updateConfigObjValues('customTheme05', currentColors, true); - break; - case 'custom06': - customTheme06 = configCustom.addConfigurationObject(customTheme06Schema, currentColors, customThemeComments); - customColor = customTheme06; - configCustom.updateConfigObjValues('customTheme06', currentColors, true); - break; - case 'custom07': - customTheme07 = configCustom.addConfigurationObject(customTheme07Schema, currentColors, customThemeComments); - customColor = customTheme07; - configCustom.updateConfigObjValues('customTheme07', currentColors, true); - break; - case 'custom08': - customTheme08 = configCustom.addConfigurationObject(customTheme08Schema, currentColors, customThemeComments); - customColor = customTheme08; - configCustom.updateConfigObjValues('customTheme08', currentColors, true); - break; - case 'custom09': - customTheme09 = configCustom.addConfigurationObject(customTheme09Schema, currentColors, customThemeComments); - customColor = customTheme09; - configCustom.updateConfigObjValues('customTheme09', currentColors, true); - break; - case 'custom10': - customTheme10 = configCustom.addConfigurationObject(customTheme10Schema, currentColors, customThemeComments); - customColor = customTheme10; - configCustom.updateConfigObjValues('customTheme10', currentColors, true); - break; - } -} - - -//////////////////////////////// -// * RANDOM COLOR GENERATOR * // -//////////////////////////////// -/** - * Generates a random theme color, used in Random theme. - */ -function getRandomThemeColor() { - if (!getRandomThemeColorContextMenu && ($('[%GR_THEMECOLOR%]') || $('[%GR_THEMECOLOR2%]'))) return; - - const generateRandomColor = () => { - const R = Math.floor((Math.random() * (pref.styleRandomPastel ? 127 : 27)) + (pref.styleRandomPastel ? 127 : 27)); - const G = Math.floor((Math.random() * (pref.styleRandomPastel ? 127 : 27)) + (pref.styleRandomPastel ? 127 : 27)); - const B = Math.floor((Math.random() * (pref.styleRandomPastel ? 127 : 27)) + (pref.styleRandomPastel ? 127 : 27)); - return pref.styleRandomPastel || pref.styleRandomDark ? (R << 16) + (G << 8) + B : ((1 << 24) * Math.random() | 0); - }; - - const color = new Color(generateRandomColor()); - const tObj = createThemeColorObject(color); - setTheme(tObj); - - if (settings.showDebugThemeLog) { - console.log('Random generated color:', color.getRGB(true)); - console.log('Random color brightness:', color.brightness); - } - if (settings.showDebugThemeOverlay) selectedPrimaryColor = color.getRGB(true); -} - - -/** - * Auto generates new colors depending on time interval, used in style Random theme auto color. - */ -function getRandomThemeAutoColor() { - clearInterval(randomThemeAutoColorTimer); - randomThemeAutoColorTimer = null; - - if (pref.styleRandomAutoColor !== 'off' && pref.styleRandomAutoColor !== 'track') { - randomThemeAutoColorTimer = setInterval(() => { - initTheme(); - }, pref.styleRandomAutoColor); - } - else if (pref.styleRandomAutoColor === 'track') { - initTheme(); - } - DebugLog('\n>>> initTheme -> getRandomThemeAutoColor <<<\n'); -} - - -/////////////////////////////////// -// * ALBUM ART COLOR GENERATOR * // -/////////////////////////////////// -/** - * Creates the color objects. - * @param {number} color The primary color. - * @param {number=} color2 The secondary color. - */ -function createThemeColorObject(color, color2) { - if (color2 === undefined) color2 = color; - const themeObj = { - primary: color.val, - primary_alt: color2.val, - darkAccent: ShadeColor(color.val, 30), - darkAccent_alt: ShadeColor(color2.val, 30), - accent: ShadeColor(color.val, 15), - accent_alt: ShadeColor(color2.val, 15), - lightAccent: TintColor(color.val, 20), - lightAccent_alt: TintColor(color2.val, 20) - }; - if (color.brightness < 18) { - // Hard code these values otherwise darkAccent and accent can be very hard to see on background - themeObj.darkAccent = RGB(32, 32, 32); - themeObj.darkAccent_alt = RGB(32, 32, 32); - themeObj.accent = RGB(56, 56, 56); - themeObj.accent_alt = RGB(56, 56, 56); - themeObj.lightAccent = RGB(78, 78, 78); - themeObj.lightAccent_alt = RGB(78, 78, 78); - } - else if (color.brightness < 40) { - themeObj.darkAccent = ShadeColor(color.val, 35); - themeObj.darkAccent_alt = ShadeColor(color2.val, 35); - themeObj.accent = TintColor(color.val, 10); - themeObj.accent_alt = TintColor(color2.val, 10); - themeObj.lightAccent = TintColor(color.val, 20); - themeObj.lightAccent_alt = TintColor(color2.val, 20); - } - else if (color.brightness > 210) { - themeObj.darkAccent = ShadeColor(color.val, 30); - themeObj.darkAccent_alt = ShadeColor(color2.val, 30); - themeObj.accent = ShadeColor(color.val, 20); - themeObj.accent_alt = ShadeColor(color2.val, 20); - themeObj.lightAccent = ShadeColor(color.val, 10); - themeObj.lightAccent_alt = ShadeColor(color2.val, 10); - } - return themeObj; -} - - -/** - * Extracts the primary and optional secondary color from an image. - * @param {GdiBitmap} image The image to extract the colors from. - * @param {number} maxColorsToPull The max number of colors in the palette. - * @param {number=} secondaryColor The secondary picked color, used in Reborn fusion. - */ -function getThemeColorsJson(image, maxColorsToPull, secondaryColor) { - const minFrequency = 0.015; - const maxBrightness = pref.theme === 'black' || pref.styleBlend || ['reborn', 'random'].includes(pref.theme) && pref.styleBlend2 ? 255 : 212; - let selectedColor; - let selectedColor2; - - try { - const colorsWeighted = JSON.parse(image.GetColourSchemeJSON(maxColorsToPull)); - colorsWeighted.map(c => { - c.col = new Color(c.col); - return c.col; - }); - - if (settings.showDebugThemeLog) console.log('idx color bright freq weight'); - - let maxWeight = 0; - let maxWeight2 = 0; - selectedColor = colorsWeighted[0].col; // Choose first color in case no color selected below - selectedColor2 = colorsWeighted[1].col; // Choose second color in case no color selected below - - for (const [i, c] of colorsWeighted.entries()) { - const col = c.col; - const midBrightness = 127 - Math.abs(127 - col.brightness); // Favors colors with a brightness around 127 - const midBrightness2 = RandomMinMax(60, 120) - Math.abs(RandomMinMax(60, 120) - col.brightness); // Favors colors with a random brightness from 60 - 120 - c.weight = c.freq * midBrightness * 10; // Multiply by 10 so numbers are easier to compare - c.weight2 = c.freq * midBrightness2 * 10; // Multiply by 10 so numbers are easier to compare - - if (c.freq >= minFrequency && !col.isCloseToGreyscale && col.brightness < maxBrightness) { - if (settings.showDebugThemeLog) { - console.log(LeftPad(i, 2), col.getRGB(true, true), LeftPad(col.brightness, 4), ' ', `${LeftPad((c.freq * 100).toFixed(2), 5)}%`, LeftPad(c.weight.toFixed(2), 7)); - } - if (c.weight > maxWeight) { - maxWeight = c.weight; - selectedColor = col; - } - if (c.weight2 < c.weight) { - maxWeight2 = c.weight2; - selectedColor2 = col; - } - } - else if (settings.showDebugThemeLog) { - console.log(' -', col.getRGB(true, true), LeftPad(col.brightness, 4), ' ', `${LeftPad((c.freq * 100).toFixed(2), 5)}%`, col.isCloseToGreyscale ? ' grey' : (c.freq < minFrequency) ? ' freq' : ' bright'); - } - } - - if (selectedColor.brightness < 37) { - if (settings.showDebugThemeLog) console.log(selectedColor.getRGB(true), 'brightness:', selectedColor.brightness, 'too dark -- searching for highlight color'); - let brightest = selectedColor; - maxWeight = 0; - for (const c of colorsWeighted) { - if (c.col.brightness > selectedColor.brightness && - c.col.brightness < 200 && - !c.col.isCloseToGreyscale && - c.weight > maxWeight && - c.freq > 0.01) { - maxWeight = c.weight; - brightest = c.col; - } - } - selectedColor = brightest; - } - if (selectedColor.brightness < selectedColor2.brightness) { - if (settings.showDebugThemeLog) console.log(selectedColor.getRGB(true), 'brightness:', selectedColor.brightness, 'too dark -- searching for highlight color'); - let brightest = selectedColor2; - maxWeight = 0; - for (const c of colorsWeighted) { - if (c.col.brightness > selectedColor2.brightness && - c.col.brightness < 200 && - !c.col.isCloseToGreyscale && - c.freq > 0.05) { - maxWeight2 = c.weight2; - brightest = c.col; - } - } - selectedColor2 = brightest; - } - - if (settings.showDebugThemeLog) { - console.log('Primary color:', selectedColor.getRGB(true)); - console.log('Primary color 2:', selectedColor2.getRGB(true)); - } - if (settings.showDebugThemeOverlay) { - selectedPrimaryColor = selectedColor.getRGB(true); - selectedPrimaryColor2 = selectedColor2.getRGB(true); - } - - return secondaryColor ? selectedColor2.val : selectedColor.val; - } - catch (e) { - console.log('\n\n'); - } -} - - -/** - * Sets the primary or secondary color from the value of getThemeColorsJson or from the custom GR-tag. - * @param {GdiBitmap} image The image from which the colors will be picked. - */ -function getThemeColors(image) { - let calculatedColor; - let calculatedColor2; - const val = $('[%GR_THEMECOLOR%]'); - const val2 = $('[%GR_THEMECOLOR2%]'); - const rebornFusion = pref.theme === 'reborn' && (pref.styleRebornFusion || pref.styleRebornFusion2 || pref.styleRebornFusionAccent); - - if (val.length) { // Color hardcoded in tags from music files - const themeRgb = val.match(/\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)/); - const themeRgb2 = val2.match(/\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)/); - calculatedColor = themeRgb ? RGB(parseInt(themeRgb[1]), parseInt(themeRgb[2]), parseInt(themeRgb[3])) : 0xff000000 | parseInt(val, 16); - calculatedColor2 = themeRgb2 ? RGB(parseInt(themeRgb2[1]), parseInt(themeRgb2[2]), parseInt(themeRgb2[3])) : 0xff000000 | parseInt(val2, 16); - } else { - calculatedColor = getThemeColorsJson(image, 14); - calculatedColor2 = rebornFusion ? getThemeColorsJson(image, 14, true) : undefined; - } - - if (isNaN(calculatedColor)) return; - - let color = new Color(calculatedColor); - let color2 = calculatedColor2 ? new Color(calculatedColor2) : undefined; - - while (pref.theme !== 'black' && color.brightness > 220) { - calculatedColor = ShadeColor(calculatedColor, pref.theme === 'white' ? 12 : 3); - color = new Color(calculatedColor); - if (rebornFusion) color2 = new Color(calculatedColor2); - if (settings.showDebugThemeLog) console.log(' >> Shading: ', ColToRgb(calculatedColor), ' - brightness: ', color.brightness); - } - while (!color.isGreyscale && color.brightness <= 17) { - calculatedColor = TintColor(calculatedColor, 3); - color = new Color(calculatedColor); - if (rebornFusion) color2 = new Color(calculatedColor2); - if (settings.showDebugThemeLog) console.log(' >> Tinting: ', ColToRgb(calculatedColor), ' - brightness: ', color.brightness); - } - - const tObj = createThemeColorObject(color); - if (rebornFusion) { - const tObj2 = createThemeColorObject(color, color2); - setTheme(tObj, tObj2); - } else { - setTheme(tObj); - } - - if (settings.showDebugThemeLog) { - console.log('Primary color brightness:', color.brightness); - if (color2) console.log('Primary color 2 brightness:', color2.brightness); - } -} diff --git a/profile/georgia-reborn/scripts/Biography/assets/html/config.html b/profile/georgia-reborn/scripts/Biography/assets/html/config.html index f4374f98..e839c9ce 100644 --- a/profile/georgia-reborn/scripts/Biography/assets/html/config.html +++ b/profile/georgia-reborn/scripts/Biography/assets/html/config.html @@ -1014,7 +1014,7 @@ function set_btn_apply_state() { getValues(); - document.getElementById("btn_apply").disabled = JSON.stringify(pptBio) != parsed_args[0] || JSON.stringify(cfg) != parsed_args[1] ? false : true; + document.getElementById("btn_apply").disabled = JSON.stringify(bioSet) != parsed_args[0] || JSON.stringify(cfg) != parsed_args[1] ? false : true; var el = document.activeElement; var id = el.id; @@ -1078,7 +1078,7 @@ var lang_ix = 0; var parsed_args; var tf_callback; - var pptBio = {}; + var bioSet = {}; var scale = 1; var sizes = []; var isTransparent = false; @@ -1090,7 +1090,7 @@ if (parsed_args) { cur = JSON.parse(parsed_args[2]); - if (!/PanelCfg|ServerCfg/.test(cur.tab)) cur.tab = 'PanelCfg'; // guard against bad user edits of pptBio + if (!/PanelCfg|ServerCfg/.test(cur.tab)) cur.tab = 'PanelCfg'; // guard against bad user edits of bioSet if (cur.tab == 'PanelCfg') { if (!/behaviour|display|headings|image|custom|textreader|advanced/.test(cur.panelPage)) cur.panelPage = 'display' } @@ -1098,10 +1098,10 @@ if (!/title_format|download|photo|cover|tagger|misc/.test(cur.serverPage)) cur.serverPage = 'download' } document.title = 'Biography ' + cur.version; - pptBio = JSON.parse(parsed_args[0]); + bioSet = JSON.parse(parsed_args[0]); cfg = JSON.parse(parsed_args[1]); - if (pptBio.multiServer_internal.value) { - var servName = pptBio.serverName_internal.value; + if (bioSet.multiServer_internal.value) { + var servName = bioSet.serverName_internal.value; servName = servName.charAt(0).toUpperCase() + servName.slice(1).toLowerCase() + ' '; var newName = ServerCfg.innerHTML.replace(' (All Panels)', '') ServerCfg.innerHTML = servName + newName; @@ -1168,7 +1168,7 @@ var textStyleLabels = ['Top align', 'Expand lists', 'Auto-optimise if multiple items shown']; var tfLabels = ['Biography', 'Review', 'Title', 'Lyrics', 'Title']; var tfServLabels = ['%BIO_ALBUMARTIST%', '%BIO_ARTIST%', '%BIO_ALBUM%', '%BIO_TITLE%', 'Composition tag']; - var themeLabels = !pptBio['themed_internal'].value ? ['User interface', 'Dark', 'Blend', 'Light', 'Random', 'Always cover based', 'Swap colours'] : ['N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set']; + var themeLabels = !bioSet['themed_internal'].value ? ['User interface', 'Dark', 'Blend', 'Light', 'Random', 'Always cover based', 'Swap colours'] : ['N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set']; var themeBlurLabels = ['Auto-fill', 'Level (%)', 'Opacity (%)', '%', 'Load delay (ms)', 'Leading item: load immediately (ignore delay)']; var whitelistLabels = ['Write all last.fm tags', 'Apply filter to remove non-standard genres using this whitelist',]; @@ -1415,20 +1415,20 @@ function setDropShadowLevel(n) { window.shadowFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying drop shadow level: ' + dropShadowLevelLabels[n]; - pptBio['dropShadowLevel_internal'].value = n; + bioSet['dropShadowLevel_internal'].value = n; window.shadowLevel.style.display = 'none'; } function setFilmAlignment(n) { window.film_dropdown.innerHTML = filmStripLabels[n]; - pptBio['filmStripMargin_internal'].value = n; + bioSet['filmStripMargin_internal'].value = n; window.filmStripAlign.style.display = 'none'; } function setHeadingShow(n, type) { var prop = ['heading', 'trackHeading', 'sourceHeading'][type]; window[prop + '_dropdown'].innerHTML = '' + ['Main', 'Track', 'Source'][type] + ': ' + (type == 0 ? headingLabels[n] : sbarShowLabels[n]); - pptBio[prop + '_internal'].value = n; + bioSet[prop + '_internal'].value = n; switch (type) { case 0: window.mainShow.style.display = 'none'; break; case 1: window.trackShow.style.display = 'none'; break; @@ -1439,7 +1439,7 @@ function setHeadingFont(n, type) { var prop = ['headFontStyle', 'trackStyle', 'sourceStyle', 'wikiStyle', 'lyricsFontStyle', 'summaryStyle'][type]; window[prop + '_dropdown'].innerHTML = (type == 3 ? 'Wikipedia section heading: ' : type == 4 ? 'Lyrics & nowplaying: font style: ' : type == 5 ? 'Summary font style: ' : '') + headingFontLabels[n]; - pptBio[prop + '_internal'].value = n; + bioSet[prop + '_internal'].value = n; setHeadingFontStyle(n, type); switch (type) { case 0: window.mainFont.style.display = 'none'; break; @@ -1460,37 +1460,37 @@ function setLyricsScrollMethod(n) { window.lyricsScrollMethod_dropdown.innerHTML = lyricsScrollMethodLabels[n]; - pptBio['lyricsScrollMaxMethod_internal'].value = n; + bioSet['lyricsScrollMaxMethod_internal'].value = n; window.lyricsScrollMethod.style.display = 'none'; } function setOverlayType(n) { window.overlay_dropdown.innerHTML = overlayLabels[n]; - pptBio['typeOverlay_internal'].value = n; + bioSet['typeOverlay_internal'].value = n; window.overlayType.style.display = 'none'; } function ratingTextPosn(n) { window.rating_dropdown.innerHTML = 'Text position: ' + ratingTextPosLabels[n]; - pptBio['ratingTextPos_internal'].value = n; + bioSet['ratingTextPos_internal'].value = n; window.ratingTextPos.style.display = 'none'; } function setReflectionType(n) { window.reflection_dropdown.innerHTML = reflPosLabels[n]; - pptBio['imgReflType_internal'].value = n; + bioSet['imgReflType_internal'].value = n; window.reflPos.style.display = 'none'; } function setSbarShow(n) { window.sbarShow_dropdown.innerHTML = sbarShowLabels[n]; - pptBio['sbarShow_internal'].value = n; + bioSet['sbarShow_internal'].value = n; window.sbarShow.style.display = 'none'; } function setsbarType(n) { window.sbarType_dropdown.innerHTML = sbarTypeLabels[n]; - pptBio['sbarType_internal'].value = n; + bioSet['sbarType_internal'].value = n; sbar_col_dropdown.style.display = n < 2 ? "inline-block" : "none"; sbar_icon_dropdown.style.display = n < 2 ? "inline-block" : "none" window.sbarType.style.display = 'none'; @@ -1498,44 +1498,44 @@ function setSbarCol(n) { window.sbarCol_dropdown.innerHTML = sbarColLabels[n]; - pptBio['sbarCol_internal'].value = n; + bioSet['sbarCol_internal'].value = n; window.sbarCol.style.display = 'none'; } function setSbarIcon(n) { window.sbarIcon_dropdown.innerHTML = sbarIconLabels[n]; - pptBio['sbarButType_internal'].value = n; + bioSet['sbarButType_internal'].value = n; window.sbarIcon.style.display = 'none'; } function setSeekerShow(n) { window.seeker_dropdown.innerHTML = sbarShowLabels[n]; - pptBio['imgSeekerShow_internal'].value = n; + bioSet['imgSeekerShow_internal'].value = n; window.seeker.style.display = 'none'; } function setTheme(n) { window.theme_dropdown.innerHTML = themeLabels[n]; - pptBio['theme_internal'].value = n; + bioSet['theme_internal'].value = n; window.themeBg.style.display = 'none'; } function setBehaviour() { for (i = 0; i < 2; i++) {window.layout.insertAdjacentHTML('beforeend', '
');} - window.sbarShow_dropdown.innerHTML = sbarShowLabels[pptBio['sbarShow_internal'].value]; + window.sbarShow_dropdown.innerHTML = sbarShowLabels[bioSet['sbarShow_internal'].value]; for (i = 0; i < 3; i++) {window.sbarShow.insertAdjacentHTML('beforeend', ''+sbarShowLabels[i]+'');} - window.sbarType_dropdown.innerHTML = sbarTypeLabels[pptBio['sbarType_internal'].value]; + window.sbarType_dropdown.innerHTML = sbarTypeLabels[bioSet['sbarType_internal'].value]; for (i = 0; i < 4; i++) {window.sbarType.insertAdjacentHTML('beforeend', ''+sbarTypeLabels[i]+'');} - window.sbarCol_dropdown.innerHTML = sbarColLabels[pptBio['sbarCol_internal'].value]; + window.sbarCol_dropdown.innerHTML = sbarColLabels[bioSet['sbarCol_internal'].value]; for (i = 0; i < 2; i++) {window.sbarCol.insertAdjacentHTML('beforeend', ''+sbarColLabels[i]+'');} - sbar_col_dropdown.style.display = pptBio['sbarType_internal'].value < 2 ? "inline-block" : "none"; - window.sbarIcon_dropdown.innerHTML = sbarIconLabels[pptBio['sbarButType_internal'].value]; + sbar_col_dropdown.style.display = bioSet['sbarType_internal'].value < 2 ? "inline-block" : "none"; + window.sbarIcon_dropdown.innerHTML = sbarIconLabels[bioSet['sbarButType_internal'].value]; for (i = 0; i < 3; i++) {window.sbarIcon.insertAdjacentHTML('beforeend', ''+sbarIconLabels[i]+'');} - sbar_icon_dropdown.style.display = pptBio['sbarType_internal'].value != 2 ? "inline-block" : "none"; + sbar_icon_dropdown.style.display = bioSet['sbarType_internal'].value != 2 ? "inline-block" : "none"; for (i = 0; i < 4; i++) {window.scrolling.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 2; i++) {window.fall_back.insertAdjacentHTML('beforeend', '
');} - window.overlay_dropdown.innerHTML = overlayLabels[pptBio['typeOverlay_internal'].value]; + window.overlay_dropdown.innerHTML = overlayLabels[bioSet['typeOverlay_internal'].value]; for (i = 0; i < 5; i++) {window.overlayType.insertAdjacentHTML('beforeend', ''+overlayLabels[i]+'');} for (i = 5; i < 8; i++) {window.overlayMetrics.insertAdjacentHTML('beforeend', '
');} generateTable('menu'); @@ -1550,7 +1550,7 @@ i = 5; window.metrics2.insertAdjacentHTML('beforeend', '
'); for (i = 0; i < 3; i++) {window.textStyle.insertAdjacentHTML('beforeend', '
');} - window.theme_dropdown.innerHTML = themeLabels[pptBio['theme_internal'].value]; + window.theme_dropdown.innerHTML = themeLabels[bioSet['theme_internal'].value]; for (i = 0; i < 5; i++) {window.themeBg.insertAdjacentHTML('beforeend', ''+themeLabels[i]+'');} for (i = 5; i < 7; i++) {window.themeStyle.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 9; i++) {window.highlight.insertAdjacentHTML('beforeend', '
');} @@ -1558,13 +1558,13 @@ for (i = 0; i < 2; i++) {window.rating1.insertAdjacentHTML('beforeend', '
');} for (i = 2; i < 4; i++) {window.rating2.insertAdjacentHTML('beforeend', '
');} - window.rating_dropdown.innerHTML = 'Text position: ' + ratingTextPosLabels[pptBio['ratingTextPos_internal'].value]; + window.rating_dropdown.innerHTML = 'Text position: ' + ratingTextPosLabels[bioSet['ratingTextPos_internal'].value]; for (i = 0; i < 3; i++) {window.ratingTextPos.insertAdjacentHTML('beforeend', ''+ratingTextPosLabels[i]+'');} for (i = 4; i < 6; i++) {window.rating3.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 6; i++) {window.summary.insertAdjacentHTML('beforeend', '
');} - window.summaryStyle_dropdown.innerHTML = 'Summary font style: ' + headingFontLabels[pptBio['summaryStyle_internal'].value]; - setHeadingFontStyle(pptBio['summaryStyle_internal'].value, 5); + window.summaryStyle_dropdown.innerHTML = 'Summary font style: ' + headingFontLabels[bioSet['summaryStyle_internal'].value]; + setHeadingFontStyle(bioSet['summaryStyle_internal'].value, 5); for (i = 0; i < 6; i++) {window.summaryFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} } @@ -1581,24 +1581,24 @@ for (i = 0; i < 2; i++) {window.flag.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 3; i++) {window.headingPadding.insertAdjacentHTML('beforeend', '
');} - window.heading_dropdown.innerHTML = 'Main: ' + headingLabels[pptBio['heading_internal'].value]; + window.heading_dropdown.innerHTML = 'Main: ' + headingLabels[bioSet['heading_internal'].value]; for (i = 0; i < 2; i++) {window.mainShow.insertAdjacentHTML('beforeend', ''+headingLabels[i]+'');} - window.trackHeading_dropdown.innerHTML = 'Track: ' + sbarShowLabels[pptBio['trackHeading_internal'].value]; + window.trackHeading_dropdown.innerHTML = 'Track: ' + sbarShowLabels[bioSet['trackHeading_internal'].value]; for (i = 0; i < 3; i++) {window.trackShow.insertAdjacentHTML('beforeend', ''+sbarShowLabels[i]+'');} - window.sourceHeading_dropdown.innerHTML = 'Source: ' + sbarShowLabels[pptBio['sourceHeading_internal'].value]; + window.sourceHeading_dropdown.innerHTML = 'Source: ' + sbarShowLabels[bioSet['sourceHeading_internal'].value]; for (i = 0; i < 3; i++) {window.sourceShow.insertAdjacentHTML('beforeend', ''+sbarShowLabels[i]+'');} - window.headFontStyle_dropdown.innerHTML = headingFontLabels[pptBio['headFontStyle_internal'].value]; - setHeadingFontStyle(pptBio['headFontStyle_internal'].value, 0); + window.headFontStyle_dropdown.innerHTML = headingFontLabels[bioSet['headFontStyle_internal'].value]; + setHeadingFontStyle(bioSet['headFontStyle_internal'].value, 0); for (i = 0; i < 6; i++) {window.mainFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} - window.trackStyle_dropdown.innerHTML = headingFontLabels[pptBio['trackStyle_internal'].value]; - setHeadingFontStyle(pptBio['trackStyle_internal'].value, 1); + window.trackStyle_dropdown.innerHTML = headingFontLabels[bioSet['trackStyle_internal'].value]; + setHeadingFontStyle(bioSet['trackStyle_internal'].value, 1); for (i = 0; i < 6; i++) {window.trackFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} - window.sourceStyle_dropdown.innerHTML = headingFontLabels[pptBio['sourceStyle_internal'].value]; - setHeadingFontStyle(pptBio['sourceStyle_internal'].value, 2); + window.sourceStyle_dropdown.innerHTML = headingFontLabels[bioSet['sourceStyle_internal'].value]; + setHeadingFontStyle(bioSet['sourceStyle_internal'].value, 2); for (i = 0; i < 6; i++) {window.sourceFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} - window.wikiStyle_dropdown.innerHTML = 'Wikipedia section heading: ' + headingFontLabels[pptBio['wikiStyle_internal'].value]; - setHeadingFontStyle(pptBio['wikiStyle_internal'].value, 3); + window.wikiStyle_dropdown.innerHTML = 'Wikipedia section heading: ' + headingFontLabels[bioSet['wikiStyle_internal'].value]; + setHeadingFontStyle(bioSet['wikiStyle_internal'].value, 3); for (i = 0; i < 6; i++) {window.wikiFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} } @@ -1606,8 +1606,8 @@ for (i = 0; i < 2; i++) {window.mainImage.insertAdjacentHTML('beforeend', '
');} i = 2; window.mainImage.insertAdjacentHTML('beforeend', '
'); - window.seeker_dropdown.innerHTML = sbarShowLabels[pptBio['imgSeekerShow_internal'].value]; - if (!pptBio['imgSeekerDisabled_internal'].value) for (i = 0; i < 3; i++) {window.seeker.insertAdjacentHTML('beforeend', ''+sbarShowLabels[i]+'');} + window.seeker_dropdown.innerHTML = sbarShowLabels[bioSet['imgSeekerShow_internal'].value]; + if (!bioSet['imgSeekerDisabled_internal'].value) for (i = 0; i < 3; i++) {window.seeker.insertAdjacentHTML('beforeend', ''+sbarShowLabels[i]+'');} else { window.seeker_dropdown.style.background = '#EBEBE4'; window.seeker_dropdown.style.color = '#C6C6C6'; @@ -1617,8 +1617,8 @@ for (i = 2; i < 4; i++) {window.seekerDots.insertAdjacentHTML('beforeend', '
');} i = 0; window.filmStripShow.insertAdjacentHTML('beforeend', '
'); - window.film_dropdown.innerHTML = filmStripLabels[pptBio['filmStripMargin_internal'].value]; - if (!pptBio['filmStripOverlay_internal'].value || pptBio['text_only_internal'].value) for (i = 0; i < 5; i++) {window.filmStripAlign.insertAdjacentHTML('beforeend', ''+filmStripLabels[i]+'');} + window.film_dropdown.innerHTML = filmStripLabels[bioSet['filmStripMargin_internal'].value]; + if (!bioSet['filmStripOverlay_internal'].value || bioSet['text_only_internal'].value) for (i = 0; i < 5; i++) {window.filmStripAlign.insertAdjacentHTML('beforeend', ''+filmStripLabels[i]+'');} else { window.film_dropdown.style.background = '#EBEBE4'; window.film_dropdown.style.color = '#C6C6C6'; @@ -1630,7 +1630,7 @@ i = 3; window.input_cntf2.insertAdjacentHTML('beforeend', '
'); generateTable('imageFormat'); - window.reflection_dropdown.innerHTML = reflPosLabels[pptBio['imgReflType_internal'].value]; + window.reflection_dropdown.innerHTML = reflPosLabels[bioSet['imgReflType_internal'].value]; for (i = 0; i < 5; i++) {window.reflPos.insertAdjacentHTML('beforeend', ''+reflPosLabels[i]+'');} for (i = 5; i < 8; i++) {window.reflectionMetrics.insertAdjacentHTML('beforeend', '
');} } @@ -1639,7 +1639,7 @@ function setCustomColours(value) { var items = ['text', 'text_h', 'headingBtn', 'headingText', 'stars', 'summary', 'rectOv', 'rectOvBor', 'bg', 'line', 'bgTrans', 'frame']; for (var i = 0; i < items.length; i++) { - bg[i] = assign(pptBio[items[i] + '_internal'][value], i < 8 ? 0 : 1); + bg[i] = assign(bioSet[items[i] + '_internal'][value], i < 8 ? 0 : 1); fg[i] = getSelCol(bg[i]); bg[i] = "rgb("+bg[i][0]+","+bg[i][1]+","+bg[i][2]+")"; var el = document.getElementById('colour_container'+i); @@ -1654,9 +1654,9 @@ for (i = 0; i < 4; i++) {window.cusText2.insertAdjacentHTML('beforeend', '
')} for (i = 4; i < 8; i++) {window.cusText3.insertAdjacentHTML('beforeend', '
')} - window.lyricsFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying: font style: ' + headingFontLabels[pptBio['lyricsFontStyle_internal'].value]; - window.shadowFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying drop shadow level: ' + dropShadowLevelLabels[pptBio['dropShadowLevel_internal'].value]; - setHeadingFontStyle(pptBio['lyricsFontStyle_internal'].value, 4); + window.lyricsFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying: font style: ' + headingFontLabels[bioSet['lyricsFontStyle_internal'].value]; + window.shadowFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying drop shadow level: ' + dropShadowLevelLabels[bioSet['dropShadowLevel_internal'].value]; + setHeadingFontStyle(bioSet['lyricsFontStyle_internal'].value, 4); for (i = 0; i < 6; i++) {window.lyricsFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} for (i = 0; i < 11; i++) {window.shadowLevel.insertAdjacentHTML('beforeend', ''+dropShadowLevelLabels[i]+'');} i = 0; window.largerHighlighted.insertAdjacentHTML('beforeend', '
'); @@ -1667,7 +1667,7 @@ i = 5; window.lyricsFadeHeight.insertAdjacentHTML('beforeend', '
'); i = 6; window.lyricsScrollTimeAvg.insertAdjacentHTML('beforeend', '
'); i = 7; window.lyricsScrollTimeMax.insertAdjacentHTML('beforeend', '
'); - window.lyricsScrollMethod_dropdown.innerHTML = lyricsScrollMethodLabels[pptBio['lyricsScrollMaxMethod_internal'].value]; + window.lyricsScrollMethod_dropdown.innerHTML = lyricsScrollMethodLabels[bioSet['lyricsScrollMaxMethod_internal'].value]; for (i = 0; i < 2; i++) {window.lyricsScrollMethod.insertAdjacentHTML('beforeend', ''+lyricsScrollMethodLabels[i]+'');} i = 8; window.rowStripes.insertAdjacentHTML('beforeend', '
'); i = 9; window.lineDividers.insertAdjacentHTML('beforeend', '
'); @@ -1685,7 +1685,7 @@ function setAdvanced() { for (i = 0; i < 4; i++) {window.cusScroll.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 5; i++) {window.scrollbar.insertAdjacentHTML('beforeend', '
');} - var fFamily = pptBio['butCustIconFont_internal'].value; + var fFamily = bioSet['butCustIconFont_internal'].value; i = 5; window.scrollbar.insertAdjacentHTML('beforeend', '
'); for (i = 6; i < 8; i++) {window.scrollbar.insertAdjacentHTML('beforeend', '
');} i = 8; window.scrollbar.insertAdjacentHTML('beforeend', '
'); @@ -1794,7 +1794,7 @@ function setInputs(page, value) { var nm = page.toString()+'_input', inputs = document.querySelectorAll(page == 'all' ? "[name$='_input']" : "[name="+nm+"]"); for (i = 0; i < inputs.length; i++) { - inputs[i].value = (pptBio[input[page].name[i] + '_internal'] || cfg[input[page].name[i] + '_internal'])[value]; + inputs[i].value = (bioSet[input[page].name[i] + '_internal'] || cfg[input[page].name[i] + '_internal'])[value]; } } @@ -1811,9 +1811,9 @@ window.filterCheckbox3.parentNode.disabled = !window.filterCheckbox0.checked && !window.filterCheckbox1.checked && !window.filterCheckbox2.checked window.fuzzyLevel.disabled = !misc_checkbox3.checked; window.headFontStyle_dropdown.style.display = window.font_checkbox1.checked && window.font_container1.value.length ? "none" : "inline-block"; - window.flag_checkbox0.parentNode.disabled = pptBio['hdPos_internal'].value != 0; - window.flag_checkbox1.parentNode.disabled = pptBio['hdPos_internal'].value != 0; - window.flagInfo.innerHTML = pptBio['hdPos_internal'].value == 0 ? "Country codes are saved during wikipedia bio download" : "Flag display requires heading format set to left"; + window.flag_checkbox0.parentNode.disabled = bioSet['hdPos_internal'].value != 0; + window.flag_checkbox1.parentNode.disabled = bioSet['hdPos_internal'].value != 0; + window.flagInfo.innerHTML = bioSet['hdPos_internal'].value == 0 ? "Country codes are saved during wikipedia bio download" : "Flag display requires heading format set to left"; window.highlight_checkbox0.parentNode.disabled = window.colour_checkbox2.checked; window.highlight_checkbox1.parentNode.disabled = window.colour_checkbox3.checked; window.highlight_checkbox2.parentNode.disabled = window.colour_checkbox9.checked || window.lines_checkbox.checked; @@ -1821,21 +1821,21 @@ window.highlight_checkbox5.parentNode.disabled = window.colour_checkbox4.checked; window.highlight_checkbox7.parentNode.disabled = window.colour_checkbox5.checked; window.highlight_checkbox8.parentNode.disabled = window.colour_checkbox0.checked; - var disabled1 = !pptBio['imgSeekerShow_internal'].value || pptBio['imgSeekerDisabled_internal'].value; + var disabled1 = !bioSet['imgSeekerShow_internal'].value || bioSet['imgSeekerDisabled_internal'].value; var disabled2 = disabled1 || !window.imgSeekerStyle1.checked; window.imgSeekerStyle0.parentNode.disabled = disabled1; window.imgSeekerStyle1.parentNode.disabled = disabled1; window.imgSeekerDots2.parentNode.disabled = disabled2; window.imgSeekerDots3.parentNode.disabled = disabled2; - window.seekerNA.innerHTML = pptBio['imgSeekerDisabled_internal'].value ? (pptBio['filmStripOverlay_internal'].value ? "Grayed items items N/A with filmstrip overlay of image area" : "Seeker & counter N/A with current overlay metrics") : ""; - window.scroll_checkbox1.parentNode.disabled = pptBio['sbarType_internal'].value == 2; - window.lyricsScrollTimeMax.style.display = pptBio['lyricsScrollMaxMethod_internal'].value == 0 ? "none" : "block"; - window.lyricsScrollTimeAvg.style.display = pptBio['lyricsScrollMaxMethod_internal'].value == 1 ? "none" : "block"; - document.getElementById('ratingTextName').style.display = pptBio['star_internal'].value ? "block" : "none"; - window.rating3.style.display = pptBio['star_internal'].value ? "block" : "none"; - window.theme_checkbox5.parentNode.disabled = pptBio['theme_internal'].value == 0; - window.theme_checkbox6.parentNode.disabled = pptBio['theme_internal'].value == 1 || pptBio['theme_internal'].value == 4; - if (pptBio['themed_internal'].value) window.themeStyle.style.display = "none"; + window.seekerNA.innerHTML = bioSet['imgSeekerDisabled_internal'].value ? (bioSet['filmStripOverlay_internal'].value ? "Grayed items items N/A with filmstrip overlay of image area" : "Seeker & counter N/A with current overlay metrics") : ""; + window.scroll_checkbox1.parentNode.disabled = bioSet['sbarType_internal'].value == 2; + window.lyricsScrollTimeMax.style.display = bioSet['lyricsScrollMaxMethod_internal'].value == 0 ? "none" : "block"; + window.lyricsScrollTimeAvg.style.display = bioSet['lyricsScrollMaxMethod_internal'].value == 1 ? "none" : "block"; + document.getElementById('ratingTextName').style.display = bioSet['star_internal'].value ? "block" : "none"; + window.rating3.style.display = bioSet['star_internal'].value ? "block" : "none"; + window.theme_checkbox5.parentNode.disabled = bioSet['theme_internal'].value == 0; + window.theme_checkbox6.parentNode.disabled = bioSet['theme_internal'].value == 1 || bioSet['theme_internal'].value == 4; + if (bioSet['themed_internal'].value) window.themeStyle.style.display = "none"; window.whitelistInfo.style.display = window.useWhitelist1.checked ? "block" : "none"; window.lfmGenreTag.style.display = window.useWhitelist1.checked ? "block" : "none"; @@ -1873,7 +1873,7 @@ function setCheckboxes(page, value) { var nm = page.toString()+'_checkbox', boxes = document.querySelectorAll(page == 'all' ? "[name$='_checkbox']" : "[name="+nm+"]"); for (i = 0; i < boxes.length; i++) { - boxes[i].checked = (pptBio[box[page][i] + '_internal'] || cfg[box[page][i] + '_internal'])[value]; + boxes[i].checked = (bioSet[box[page][i] + '_internal'] || cfg[box[page][i] + '_internal'])[value]; } } @@ -1887,7 +1887,7 @@ for (i = 0; i < radios.length; i++) { var radioBtns = document.getElementsByName(radios[i]); for (j = 0; j < radioBtns.length; j++) { - if (j == (pptBio[radios[i] + '_internal'] || cfg[radios[i] + '_internal'])[value]) { + if (j == (bioSet[radios[i] + '_internal'] || cfg[radios[i] + '_internal'])[value]) { radioBtns[j].checked = true; radioBtns[j].value = j; } @@ -1898,19 +1898,19 @@ function getValues() { var inputs = document.querySelectorAll("[name$='_input']"); for (i = 0; i < inputs.length; i++) { - (pptBio[input.all.name[i] + '_internal'] || cfg[input.all.name[i] + '_internal']).value = input.all.type[i] == 'text' ? inputs[i].value : input.all.type[i] == 'int' ? parseInt(inputs[i].value) : parseFloat(inputs[i].value); + (bioSet[input.all.name[i] + '_internal'] || cfg[input.all.name[i] + '_internal']).value = input.all.type[i] == 'text' ? inputs[i].value : input.all.type[i] == 'int' ? parseInt(inputs[i].value) : parseFloat(inputs[i].value); } var boxes = document.querySelectorAll("[name$='_checkbox']"); for (i = 0; i < boxes.length; i++) { - (pptBio[box.all[i] + '_internal'] || cfg[box.all[i] + '_internal']).value = boxes[i].checked; + (bioSet[box.all[i] + '_internal'] || cfg[box.all[i] + '_internal']).value = boxes[i].checked; } for (i = 0; i < radio.all.length; i++) { var radioBtns = document.getElementsByName(radio.all[i]); for (j = 0; j < radioBtns.length; j++) { if (radioBtns[j].checked) { - (pptBio[radio.all[i] + '_internal'] || cfg[radio.all[i] + '_internal']).value = j; + (bioSet[radio.all[i] + '_internal'] || cfg[radio.all[i] + '_internal']).value = j; break; } } @@ -1927,7 +1927,7 @@ window.btn_ok.onclick = function () { getValues(); - var new_pptBio = JSON.stringify(pptBio); + var new_pptBio = JSON.stringify(bioSet); var new_cfg = JSON.stringify(cfg); var new_cur = JSON.stringify(cur); if (new_pptBio == parsed_args[0]) new_pptBio = ''; @@ -1941,7 +1941,7 @@ window.btn_apply.onclick = function () { getValues(); - var new_pptBio = JSON.stringify(pptBio); + var new_pptBio = JSON.stringify(bioSet); var new_cfg = JSON.stringify(cfg); if (new_pptBio == parsed_args[0]) new_pptBio = ''; else parsed_args[0] = new_pptBio; if (new_cfg == parsed_args[1]) new_cfg = ''; else parsed_args[1] = new_cfg; @@ -1955,31 +1955,31 @@ setInputs(cur.page, 'default_value'); setRadioBtns(cur.page, 'default_value'); if (cur.page == 'behaviour') { - setOverlayType(pptBio['typeOverlay_internal'].default_value); - setSbarShow(pptBio['sbarShow_internal'].default_value); - setsbarType(pptBio['sbarType_internal'].default_value); - setSbarCol(pptBio['sbarCol_internal'].default_value); - setSbarIcon(pptBio['sbarButType_internal'].default_value); + setOverlayType(bioSet['typeOverlay_internal'].default_value); + setSbarShow(bioSet['sbarShow_internal'].default_value); + setsbarType(bioSet['sbarType_internal'].default_value); + setSbarCol(bioSet['sbarCol_internal'].default_value); + setSbarIcon(bioSet['sbarButType_internal'].default_value); } if (cur.page == 'display') { - setTheme(pptBio['theme_internal'].default_value); + setTheme(bioSet['theme_internal'].default_value); } if (cur.page == 'headings') { - setHeadingShow(pptBio['heading_internal'].default_value, 0); - setHeadingShow(pptBio['trackHeading_internal'].default_value, 1); - setHeadingShow(pptBio['sourceHeading_internal'].default_value, 2); - setHeadingFont(pptBio['headFontStyle_internal'].default_value, 0); - setHeadingFont(pptBio['trackStyle_internal'].default_value, 1); - setHeadingFont(pptBio['sourceStyle_internal'].default_value, 2); + setHeadingShow(bioSet['heading_internal'].default_value, 0); + setHeadingShow(bioSet['trackHeading_internal'].default_value, 1); + setHeadingShow(bioSet['sourceHeading_internal'].default_value, 2); + setHeadingFont(bioSet['headFontStyle_internal'].default_value, 0); + setHeadingFont(bioSet['trackStyle_internal'].default_value, 1); + setHeadingFont(bioSet['sourceStyle_internal'].default_value, 2); } if (cur.page == 'image') { - setFilmAlignment(pptBio['filmStripMargin_internal'].default_value); - setReflectionType(pptBio['imgReflType_internal'].default_value); - setSeekerShow(pptBio['imgSeekerShow_internal'].default_value); + setFilmAlignment(bioSet['filmStripMargin_internal'].default_value); + setReflectionType(bioSet['imgReflType_internal'].default_value); + setSeekerShow(bioSet['imgSeekerShow_internal'].default_value); } if (cur.page == 'custom') setCustomColours('default_value'); if (cur.page == 'textreader') { - setHeadingFont(pptBio['lyricsFontStyle_internal'].default_value, 3); + setHeadingFont(bioSet['lyricsFontStyle_internal'].default_value, 3); } set_btn_apply_state(); } diff --git a/profile/georgia-reborn/scripts/Biography/bio-main.js b/profile/georgia-reborn/scripts/Biography/bio-main.js index ea42811c..a3beb9db 100644 --- a/profile/georgia-reborn/scripts/Biography/bio-main.js +++ b/profile/georgia-reborn/scripts/Biography/bio-main.js @@ -1,25 +1,132 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Biography * // -// * Author: TT * // -// * Org. Author: WilB * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 1.4.2 * // -// * Dev. started: 2016-10-18 * // -// * Last change: 2023-12-13 (Mod change 2023-12-24) * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Biography * // +// * Author: TT * // +// * Org. Author: WilB * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 1.4.2 * // +// * Dev. started: 18-10-2016 * // +// * Last change: 13-12-2023 (Mod change 18-02-2024) * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; +/////////////////////////////////////////////////////////// +// ! IMPORTANT NOTICE AND DOCUMENTATION FOR OTHER DEVS ! // +/////////////////////////////////////////////////////////// +// The original biography script is designed for use in isolated panel containers to prevent interference with global variables and classes. +// The Georgia-ReBORN theme uses only one Spider Monkey Panel, which shares its global namespace with all scripts. +// To avoid conflicts, several global variables and classes were renamed with a 'bio' prefix to indicate they are biography-specific. +// All biography class instances were also added to the `bio` main object. + +// * GLOBAL VARS * // +// let isRadioStreamParser -> bioIsRadioStreamParser + +// const parse -> bioParse + +// const requiredVersionStr -> bioRequiredVersionStr +// const doc -> bioDoc +// const fso -> bioFSO +// const tooltip -> bioTooltip +// const WshShell -> bioWshShell +// const $ -> $Bio +// const ease -> bioEase +// const md5 -> bioMD5 +// const codeToCountry -> bioCodeToCountry +// const countryToCode -> bioCountryToCode + +// const art -> bioArt +// const cov -> bioCov + +// const bio -> the added bio main object + +// let colourSelector -> bioColourSelector +// let sync -> bioSync +// const syncer -> bioSyncer + +// const English -> bioEnglish +// const simplifiedChinese -> bioSimplifiedChinese +// const traditionalChinese -> bioTraditionalChinese +// const lg -> bioLg + +// const MF_GRAYED -> BIO_MF_GRAYED +// const MF_STRING -> BIO_MF_STRING +// const clearArr -> bioClearArr +// const menu -> bioMenu +// const bMenu -> bioBMenu + +// let properties -> bioProperties +// const ppt -> bioSet + +// const cfg -> bioCfg +// let settings -> bioSettings +// let item_properties -> bio_item_properties +// let item_properties_alternative_grouping -> bio_item_properties_alternative_grouping +// let nowplaying -> bioNowplaying +// let radioParser -> bioRadioParser + +// const my_utils -> bio_my_utils + +// * FUNCTIONS * // +// function Bezier -> BioBezier +// function MD5 -> BioMD5 +// function onStateChange -> bioOnStateChange +// function send -> bioSend + +// * CLASSES * // +// class DldAllmusic -> BioDldAllmusic +// class DldAllmusicRev -> BioDldAllmusicRev +// class Parse -> BioParse +// class Buttons -> BioButtons +// class Btn -> BioBtn +// class Tooltip -> BioTooltip +// class TooltipTimer -> BioTooltipTimer +// class Transition -> BioTransition +// class FilmStrip -> BioFilmStrip +// class Helpers -> BioHelpers +// class Images -> BioImages +// class ImageCache -> BioImageCache +// class Seeker -> BioSeeker +// class UserInterface -> BioUserInterface +// class Vkeys -> BioVkeys +// class DldLastfm -> BioDldLastfm +// class DldArtImages -> BioDldArtImages +// class LfmArtImg -> BioLfmArtImg +// class LfmAlbum -> BioLfmAlbum +// class LfmTrack -> BioLfmTrack +// class LfmSimilarArtists -> BioLfmSimilarArtists +// class LfmTopAlbums -> BioLfmTopAlbums +// class DldLastfmGenresWhitelist -> BioDldLastfmGenresWhitelist +// class Library -> BioLibrary +// class Lyrics -> BioLyrics +// class MenuManager -> BioMenuManager +// class MenuItems -> BioMenuItems +// class Names -> BioNames +// class Panel -> BioPanel +// class PopUpBox -> BioPopUpBox +// class PanelProperty -> BioPanelProperty +// class PanelProperties -> BioPanelProperties +// class ResizeHandler -> BioResizeHandler +// class Scrollbar -> BioScrollbar +// class Server -> BioServer +// class Setting -> BioSetting +// class Settings -> BioSettings +// class Tagger -> BioTagger +// class Text -> BioText +// class Timers -> BioTimers +// class DldWikipedia -> BioDldWikipedia +// class Infobox -> BioInfobox + + //////////////////////////////////////////////// // ! ALL FILES LOADED IN GR-ASYNC-LOADER.JS ! // //////////////////////////////////////////////// -let biographyInitialized = false; -let isRadioStreamParser = false; +/** @global @type {boolean} */ +let bioIsRadioStreamParser = false; -if (typeof my_utilsBio === 'undefined') include(`${basePath}scripts\\biography\\scripts\\bio-utils.js`); +if (typeof bio_my_utils === 'undefined') include(`${grPath.base}scripts\\biography\\scripts\\bio-utils.js`); // include(fb.ProfilePath + 'elements\\colourSelector.js'); // sort handling n/a standalone // const loadAsync = false; // window.GetProperty('Panel Biography - Load Biography Asynchronously', true); // changed to false: issue on loading fth with many panels @@ -27,7 +134,7 @@ if (typeof my_utilsBio === 'undefined') include(`${basePath}scripts\\biography\\ // async function readFiles(files) { // for (const file of files) { // if (window.ID) { // fix pss issue -// await include(my_utilsBio.getScriptPath + file); +// await include(bio_my_utils.getScriptPath + file); // } // } // } @@ -37,7 +144,7 @@ if (typeof my_utilsBio === 'undefined') include(`${basePath}scripts\\biography\\ // 'bio-properties.js', // 'bio-settings.js', // 'bio-interface.js', -// 'bio-language.js', +// 'bio-language.js', // 'bio-panel.js', // 'bio-server.js', // 'bio-allmusic.js', @@ -67,5 +174,5 @@ if (typeof my_utilsBio === 'undefined') include(`${basePath}scripts\\biography\\ // window.Repaint(); // }); // } else { -// files.forEach(v => include(my_utilsBio.getScriptPath + v)); +// files.forEach(v => include(bio_my_utils.getScriptPath + v)); // } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-allmusic.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-allmusic.js index 7207b479..145676a9 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-allmusic.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-allmusic.js @@ -1,6 +1,6 @@ 'use strict'; -function onStateChange(resolve, reject, func = null) { // credit regorxxx +function bioOnStateChange(resolve, reject, func = null) { // credit regorxxx if (this !== null) { // this is xmlhttp bound if (this.Status === 200) { return func ? func(this.ResponseText, this) : resolve(this.ResponseText); @@ -10,7 +10,7 @@ function onStateChange(resolve, reject, func = null) { // credit regorxxx } // May be used to async run a func for the response or as promise -function send({ method = 'GET', URL, body = void (0), func = null, requestHeader = [/*[header, type]*/], bypassCache = false, timeout = 5000 }) { // credit regorxxx +function bioSend({ method = 'GET', URL, body = void (0), func = null, requestHeader = [/*[header, type]*/], bypassCache = false, timeout = 5000 }) { // credit regorxxx return new Promise(async (resolve, reject) => { const xmlhttp = new ActiveXObject('WinHttp.WinHttpRequest.5.1'); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#bypassing_the_cache @@ -34,9 +34,10 @@ function send({ method = 'GET', URL, body = void (0), func = null, requestHeader xmlhttp.Send(method === 'POST' ? body : void (0)); // Add a timer for timeout const timer = setTimeout(() => { + clearInterval(checkResponse); try { xmlhttp.WaitForResponse(-1); - onStateChange.call(xmlhttp, resolve, reject, func); + bioOnStateChange.call(xmlhttp, resolve, reject, func); } catch (e) { let status = 400; if (e.message.indexOf('0x80072ee7') !== -1) { status = 400; } // No network @@ -50,12 +51,12 @@ function send({ method = 'GET', URL, body = void (0), func = null, requestHeader try { xmlhttp.Status && xmlhttp.ResponseText } catch (e) { return; } clearTimeout(timer); clearInterval(checkResponse); - onStateChange.call(xmlhttp, resolve, reject, func); + bioOnStateChange.call(xmlhttp, resolve, reject, func); }, 30); }); } -class DldAllmusicBio { +class BioDldAllmusic { init(URL, referer, p_title, p_artist, p_fo_bio, p_pth_bio, p_force) { this.active = ''; this.artist = p_artist; @@ -80,7 +81,7 @@ class DldAllmusicBio { let list = []; switch (item) { case 'id': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -90,32 +91,32 @@ class DldAllmusicBio { URL }).then( (response) => { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = response; - list = parse.amSearch(div, 'performers', 'song'); - i = serverBio.match(this.artist, this.title, list, 'song'); + list = bioParse.amSearch(div, 'performers', 'song'); + i = bio.server.match(this.artist, this.title, list, 'song'); if (i != -1) { this.artistLink = list[i].artistLink; if (this.artistLink) { - docBio.close(); + bioDoc.close(); return this.search('biography', `${this.artistLink}/biographyAjax`, this.artistLink); } } - if (this.artist) this.search('artist', `${serverBio.url.am}artists/${encodeURIComponent(this.artist)}`, 'https://allmusic.com'); - docBio.close(); + if (this.artist) this.search('artist', `${bio.server.url.am}artists/${encodeURIComponent(this.artist)}`, 'https://allmusic.com'); + bioDoc.close(); }, (error) => { - $Bio.trace(`allmusic review / biography: ${serverBio.album} / ${serverBio.albumArtist}: not found Status error: ${this.xmlhttp.status}`, true); + $Bio.trace(`allmusic review / biography: ${bio.server.album} / ${bio.server.albumArtist}: not found Status error: ${this.xmlhttp.status}`, true); } ).catch((error) => { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.artist} - ${this.title}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.artist} - ${this.title}`); if (!$Bio.file(this.pth_bio)) $Bio.trace(`allmusic biography: ${this.artist}: not found`, true); }); break; case 'artist': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -125,8 +126,8 @@ class DldAllmusicBio { URL }).then( (response) => { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = response; const artists = []; const artist = $Bio.strip(this.artist); @@ -137,26 +138,26 @@ class DldAllmusicBio { const href = a.length && a[0].href ? a[0].href : ''; if (name && href && artist == name) artists.push(href); }); - docBio.close(); + bioDoc.close(); if (artists.length == 1 && artists[0]) { return this.search('biography', `${artists[0]}/biographyAjax`, artists[0]); } - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.artist} - ${this.title}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.artist} - ${this.title}`); if (!$Bio.file(this.pth_bio)) { $Bio.trace(`allmusic biography: ${this.artist}${artists.length > 1 ? ': unable to disambiguate multiple artists of same name: discriminators, album name or track title, either not matched or absent (e.g. menu look ups)' : ': not found'}`, true); } }, (error) => { - $Bio.trace(`allmusic review / biography: ${serverBio.album} / ${serverBio.albumArtist}: not found Status error: ${this.xmlhttp.status}`, true); + $Bio.trace(`allmusic review / biography: ${bio.server.album} / ${bio.server.albumArtist}: not found Status error: ${this.xmlhttp.status}`, true); } ).catch((error) => { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.artist} - ${this.title}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.artist} - ${this.title}`); if (!$Bio.file(this.pth_bio)) $Bio.trace(`allmusic biography: ${this.artist}: not found`, true); }); break; case 'biography': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -166,7 +167,7 @@ class DldAllmusicBio { URL }).then( (response) => { - parse.amBio(this, response); + bioParse.amBio(this, response); if (this.artistLink) { this.search('artistPage', this.artistLink, 'https://allmusic.com'); } @@ -176,7 +177,7 @@ class DldAllmusicBio { break; case 'artistPage': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -186,16 +187,16 @@ class DldAllmusicBio { URL }).then( (response) => { - parse.amArtist(this, response, this.artist, '', this.title, this.fo_bio, this.pth_bio, ''); + bioParse.amArtist(this, response, this.artist, '', this.title, this.fo_bio, this.pth_bio, ''); }, (error) => { $Bio.trace(`allmusic review / biography: ${this.album} / ${this.albumArtist}: not found Status error: ${JSON.stringify(error)}`, true) } ).catch((error) => { if (this.album) { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.pth_rev}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.pth_rev}`); } else { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.artist} - ${this.title}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.artist} - ${this.title}`); } if (!$Bio.file(this.pth_bio)) $Bio.trace(`allmusic biography: ${this.artist}: not found`, true); }); @@ -204,7 +205,7 @@ class DldAllmusicBio { } } -class DldAllmusicRev { +class BioDldAllmusicRev { init(URL, referer, p_album, p_alb_artist, p_artist, p_va, p_dn_type, p_fo_rev, p_pth_rev, p_fo_bio, p_pth_bio, p_art, p_force) { this.album = p_album; this.albumArtist = p_alb_artist; @@ -245,7 +246,7 @@ class DldAllmusicRev { let list = []; switch (item) { case 'id': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -255,49 +256,49 @@ class DldAllmusicRev { URL }).then( (response) => { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = response; const item = {}; if (this.dn_type.startsWith('review') || this.dn_type == 'biography') { item.art = 'artist'; item.type = 'album'; } // this.dn_type choices: 'review+biography'* || 'composition+biography' || 'review' || 'composition' || 'track' || 'biography' // *falls back to trying track / artist based biography if art_upd needed else if (this.dn_type == 'track') { item.art = 'performers'; item.type = 'song'; } else { item.art = 'composer'; item.type = 'composition'; } - list = parse.amSearch(div, item.art, item.type); - const i = serverBio.match(this.albumArtist, this.album, list, item.type); + list = bioParse.amSearch(div, item.art, item.type); + const i = bio.server.match(this.albumArtist, this.album, list, item.type); if (i != -1) { if (!this.va) this.artistLink = list[i].artistLink; if (this.dn_type != 'biography') { - docBio.close(); + bioDoc.close(); this.titleLink = list[i].titleLink; if (this.titleLink) { return this.search('review', this.titleLink + (item.type != 'composition' ? '/reviewAjax' : '/descriptionAjax'), this.titleLink); } } else if (!this.va) { - docBio.close(); + bioDoc.close(); if (this.artistLink) { return this.search('biography', `${this.artistLink}/biographyAjax`, this.artistLink); } } } - serverBio.getBio(this.force, this.art, 1); - if (this.dn_type.includes('biography')) serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.pth_rev}`); - serverBio.updateNotFound(`Rev ${cfg.partialMatch} ${this.pth_rev}${this.dn_type != 'track' ? '' : ` ${this.album} ${this.albumArtist}`}`); + bio.server.getBio(this.force, this.art, 1); + if (this.dn_type.includes('biography')) bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.pth_rev}`); + bio.server.updateNotFound(`Rev ${bioCfg.partialMatch} ${this.pth_rev}${this.dn_type != 'track' ? '' : ` ${this.album} ${this.albumArtist}`}`); $Bio.trace(`allmusic review: ${this.album} / ${this.albumArtist}: not found`, true); - docBio.close(); + bioDoc.close(); }, (error) => { $Bio.trace(`allmusic review / biography: ${this.album} / ${this.albumArtist}: not found Status error: ${JSON.stringify(error)}`, true) } ).catch((error) => { - serverBio.getBio(this.force, this.art, 1); - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.pth_rev}`); - serverBio.updateNotFound(`Rev ${cfg.partialMatch} ${this.pth_rev}${this.dn_type != 'track' ? '' : ` ${this.album} ${this.albumArtist}`}`); + bio.server.getBio(this.force, this.art, 1); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.pth_rev}`); + bio.server.updateNotFound(`Rev ${bioCfg.partialMatch} ${this.pth_rev}${this.dn_type != 'track' ? '' : ` ${this.album} ${this.albumArtist}`}`); $Bio.trace(`allmusic review: ${this.album} / ${this.albumArtist}: not found`, true); }); break; case 'review': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -307,18 +308,18 @@ class DldAllmusicRev { URL }).then( (response) => { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = response; const dv = div.getElementsByTagName('div'); const module = this.dn_type == 'track' ? 'songContentSubModule' : this.dn_type.includes('composition') ? 'compositionContentSubModule' : 'albumContentSubModule'; $Bio.htmlParse(dv, 'className', module, v => this.review = v.innerHTML); this.review = this.dn_type != 'track' && !this.dn_type.includes('composition') ? this.review.split(/<\/h3>/i) : this.review.split(/<\/h2>/i); if (this.review.length == 2) { - this.reviewAuthor = serverBio.format(this.review[0]); - this.review = serverBio.format(this.review[1]); - } else this.review = serverBio.format(this.review[0]); - docBio.close(); + this.reviewAuthor = bio.server.format(this.review[0]); + this.review = bio.server.format(this.review[1]); + } else this.review = bio.server.format(this.review[0]); + bioDoc.close(); if (this.titleLink) { if (!this.dn_type.includes('composition')) this.search('moodsThemes', `${this.titleLink}/moodsThemesAjax`, this.titleLink); else this.search('titlePage', this.titleLink, 'https://allmusic.com'); @@ -329,7 +330,7 @@ class DldAllmusicRev { break; case 'moodsThemes': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -339,8 +340,8 @@ class DldAllmusicRev { URL }).then( (response) => { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = response; const a = div.getElementsByTagName('a'); const reviewMood = []; @@ -363,7 +364,7 @@ class DldAllmusicRev { if (this.dn_type != 'track') this.reviewTheme = `Album Themes: ${reviewTheme.join('\u200b, ')}` else this.songTheme = reviewTheme; } - docBio.close(); + bioDoc.close(); if (this.titleLink) this.search('titlePage', this.titleLink, 'https://allmusic.com'); }, (error) => {} @@ -371,7 +372,7 @@ class DldAllmusicRev { break; case 'titlePage': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -381,8 +382,8 @@ class DldAllmusicRev { URL }).then( (response) => { - docBio.open(); - const div = docBio.createElement('div') + bioDoc.open(); + const div = bioDoc.createElement('div') div.innerHTML = response; const a = div.getElementsByTagName('a'); const dv = div.getElementsByTagName('div'); @@ -425,7 +426,7 @@ class DldAllmusicRev { } this.saveTrackReview(); } - docBio.close(); + bioDoc.close(); if (this.dn_type.includes('+biography') && this.artistLink) { return this.search('biography', `${this.artistLink}/biographyAjax`, this.artistLink); @@ -441,14 +442,14 @@ class DldAllmusicRev { if (this.dn_type.includes('+biography') && this.artistLink) { return this.search('biography', `${this.artistLink}/biographyAjax`, this.artistLink); } - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.pth_rev}`); - serverBio.updateNotFound(`Rev ${cfg.partialMatch} ${this.pth_rev}${this.dn_type != 'track' ? '' : ` ${this.album} ${this.albumArtist}`}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.pth_rev}`); + bio.server.updateNotFound(`Rev ${bioCfg.partialMatch} ${this.pth_rev}${this.dn_type != 'track' ? '' : ` ${this.album} ${this.albumArtist}`}`); $Bio.trace(`allmusic review: ${this.album} / ${this.albumArtist}: not found`, true); }); break; case 'biography': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -458,7 +459,7 @@ class DldAllmusicRev { URL }).then( (response) => { - parse.amBio(this, response); + bioParse.amBio(this, response); if (this.artistLink) this.search('artistPage', this.artistLink, 'https://allmusic.com'); }, (error) => {} @@ -466,7 +467,7 @@ class DldAllmusicRev { break; case 'artistPage': - send({ + bioSend({ method: 'GET', bypassCache: this.force, requestHeader: [ @@ -476,16 +477,16 @@ class DldAllmusicRev { URL }).then( (response) => { - parse.amArtist(this, response, this.artist, this.album, '', this.fo_bio, this.pth_bio, this.pth_rev); + bioParse.amArtist(this, response, this.artist, this.album, '', this.fo_bio, this.pth_bio, this.pth_rev); }, (error) => { $Bio.trace(`allmusic review / biography: ${this.album} / ${this.albumArtist}: not found Status error: ${JSON.stringify(error)}`, true) } ).catch((error) => { if (this.album) { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.pth_rev}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.pth_rev}`); } else { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${this.artist} - ${this.title}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${this.artist} - ${this.title}`); } if (!$Bio.file(this.pth_bio)) $Bio.trace(`allmusic biography: ${this.artist}: not found`, true); }); @@ -495,16 +496,16 @@ class DldAllmusicRev { saveAlbumReview() { this.review = `>> Album rating: ${this.rating} << ${this.review}`; - this.review = txt.add([this.reviewGenre, this.reviewMood, this.reviewTheme, this.releaseDate, this.reviewAuthor], this.review); + this.review = bio.txt.add([this.reviewGenre, this.reviewMood, this.reviewTheme, this.releaseDate, this.reviewAuthor], this.review); this.review = this.review.trim(); if (this.review.length > 22) { if (this.fo_rev) { $Bio.buildPth(this.fo_rev); $Bio.save(this.pth_rev, this.review, true); - serverBio.res(); + bio.server.res(); } } else { - serverBio.updateNotFound(`Rev ${cfg.partialMatch} ${this.pth_rev}`); + bio.server.updateNotFound(`Rev ${bioCfg.partialMatch} ${this.pth_rev}`); $Bio.trace(`allmusic this.review: ${this.album} / ${this.albumArtist}: not found`, true); } } @@ -527,24 +528,24 @@ class DldAllmusicRev { } if (this.reviewAuthor || this.reviewGenre || this.reviewMood || this.reviewTheme || this.review || this.songReleaseYear || this.composer) { - serverBio.res(); + bio.server.res(); } else { - serverBio.updateNotFound(`Rev ${cfg.partialMatch} ${this.pth_rev} ${this.album} ${this.albumArtist}`); + bio.server.updateNotFound(`Rev ${bioCfg.partialMatch} ${this.pth_rev} ${this.album} ${this.albumArtist}`); $Bio.trace(`allmusic review: ${this.album} / ${this.albumArtist}: not found`, true); } } } -class Parse { +class BioParse { amArtist(that, responseText, artist, album, title, fo_bio, pth_bio, pth_rev) { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = responseText; const dv = div.getElementsByTagName('div'); let tg = ''; - $Bio.htmlParse(dv, 'className', 'birth', v => that.start = serverBio.format(v.innerHTML).replace(/Born/i, 'Born:').replace(/Formed/i, 'Formed:')); - $Bio.htmlParse(dv, 'className', 'death', v => that.end = serverBio.format(v.innerHTML).replace(/Died/i, 'Died:').replace(/Disbanded/i, 'Disbanded:')); + $Bio.htmlParse(dv, 'className', 'birth', v => that.start = bio.server.format(v.innerHTML).replace(/Born/i, 'Born:').replace(/Formed/i, 'Formed:')); + $Bio.htmlParse(dv, 'className', 'death', v => that.end = bio.server.format(v.innerHTML).replace(/Died/i, 'Died:').replace(/Disbanded/i, 'Disbanded:')); $Bio.htmlParse(dv, 'className', 'activeDates', v => that.active = v.innerText.replace(/Active/i, 'Active: ').trim()); $Bio.htmlParse(div.getElementsByTagName('a'), false, false, v => { @@ -565,21 +566,21 @@ class Parse { that.groupMembers = that.groupMembers.length ? `Group Members: ${that.groupMembers.join('\u200b, ')}` : ''; this.saveBiography(that, artist, album, title, fo_bio, pth_bio, pth_rev); - docBio.close(); + bioDoc.close(); } amBio(that, responseText) { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = responseText; const dv = div.getElementsByTagName('div'); $Bio.htmlParse(dv, 'className', 'artistContentSubModule', v => that.biography = v.innerHTML); that.biography = that.biography.split(/<\/h2>/i); if (that.biography.length == 2) { - that.biographyAuthor = serverBio.format(that.biography[0]); - that.biography = serverBio.format(that.biography[1]); - } else that.biography = serverBio.format(that.biography[0]); - docBio.close(); + that.biographyAuthor = bio.server.format(that.biography[0]); + that.biography = bio.server.format(that.biography[1]); + } else that.biography = bio.server.format(that.biography[0]); + bioDoc.close(); } amSearch(div, artist, item) { @@ -606,24 +607,29 @@ class Parse { } saveBiography(that, artist, album, title, fo_bio, pth_bio, pth_rev) { - that.biography = txt.add([that.active, that.start, that.end, that.biographyGenre, that.groupMembers, that.biographyAuthor], that.biography); + that.biography = bio.txt.add([that.active, that.start, that.end, that.biographyGenre, that.groupMembers, that.biographyAuthor], that.biography); that.biography = that.biography.trim(); if (that.biography.length > 19) { if (fo_bio) { $Bio.buildPth(fo_bio); $Bio.save(pth_bio, that.biography, true); - serverBio.res(); + bio.server.res(); } } else { if (album) { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${pth_rev}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${pth_rev}`); } else { - serverBio.updateNotFound(`Bio ${cfg.partialMatch} ${artist} - ${title}`); + bio.server.updateNotFound(`Bio ${bioCfg.partialMatch} ${artist} - ${title}`); } if (!$Bio.file(pth_bio)) $Bio.trace(`allmusic biography: ${artist}: not found`, true); } } } -const parse = new Parse(); +/** + * The instance of `BioParse` class for biography parsing operations. + * @typedef {BioParse} + * @global + */ +const bioParse = new BioParse(); diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-buttons.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-buttons.js index a27bcb6e..0b37133c 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-buttons.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-buttons.js @@ -1,6 +1,6 @@ 'use strict'; -class ButtonsBio { +class BioButtons { constructor() { this.alpha = 255; this.btns = {}; @@ -11,14 +11,14 @@ class ButtonsBio { this.transition; this.flag = { - x: panelBio.heading.x, - h: Math.round(uiBio.font.heading_h * 0.56) + x: bio.panel.heading.x, + h: Math.round(bio.ui.font.heading_h * 0.56) }; - this.flag.y = panelBio.text.t - uiBio.heading.h + Math.round((uiBio.font.heading_h - this.flag.h) / 2); + this.flag.y = bio.panel.text.t - bio.ui.heading.h + Math.round((bio.ui.font.heading_h - this.flag.h) / 2); this.lookUp = { baseSize: 15 * $Bio.scale, - col: $Bio.toRGB(uiBio.col.text), + col: $Bio.toRGB(bio.ui.col.text), gap: 8, img: null, imgLock: null, @@ -48,7 +48,7 @@ class ButtonsBio { iconFontName: 'Segoe UI Symbol', iconFontStyle: 0, init: true, - pad: $Bio.clamp(pptBio.sbarButPad / 100, -0.5, 0.3) + pad: $Bio.clamp(bioSet.sbarButPad / 100, -0.5, 0.3) }; this.src = { @@ -93,22 +93,22 @@ class ButtonsBio { this.tooltipBio = { heading: '', name: false, - show: pref.showTooltipBiography || pref.showTooltipTruncated, + show: grSet.showTooltipBiography || grSet.showTooltipTruncated, start: Date.now() - 2000, x: 0, w: 100 }; - this.lookUp.zoomSize = Math.max(Math.round(this.lookUp.baseSize * pptBio.zoomLookUpBtn / 100), 7); + this.lookUp.zoomSize = Math.max(Math.round(this.lookUp.baseSize * bioSet.zoomLookUpBtn / 100), 7); this.lookUp.scale = Math.round(this.lookUp.zoomSize / this.lookUp.baseSize * 100); this.lookUp.font = gdi.Font('FontAwesome', SCALE(18) * this.lookUp.scale / 100, 0); this.lookUp.fontLock = gdi.Font('FontAwesome', SCALE(17) * this.lookUp.scale / 100, 0); this.scr.btns = this.scr.albBtns.concat(this.scr.artBtns); this.src.iconFont = this.src.font; - if (uiBio.stars == 1 && uiBio.show.btnRedLastfm) this.rating.imagesLfm = []; + if (bio.ui.stars == 1 && bio.ui.show.btnRedLastfm) this.rating.imagesLfm = []; - pptBio.zoomLookUpBtn = this.lookUp.scale; + bioSet.zoomLookUpBtn = this.lookUp.scale; this.setSbarIcon(); this.createImages('all'); @@ -118,71 +118,71 @@ class ButtonsBio { check(refresh) { if (!refresh) { - (pptBio.sbarShow != 1 || !this.scr.init) && !txt.lyricsDisplayed() ? this.setScrollBtnsHide() : this.setScrollBtnsHide(true, 'both'); + (bioSet.sbarShow != 1 || !this.scr.init) && !bio.txt.lyricsDisplayed() ? this.setScrollBtnsHide() : this.setScrollBtnsHide(true, 'both'); } - this.rating.show = uiBio.stars == 1 && !pptBio.artistView && (txt.rev.loaded.am && txt.rating.am != -1 || txt.rev.loaded.lfm && txt.rating.lfm != -1); - this.src.name = uiBio.show.btnBg ? ' ' : ''; + this.rating.show = bio.ui.stars == 1 && !bioSet.artistView && (bio.txt.rev.loaded.am && bio.txt.rating.am != -1 || bio.txt.rev.loaded.lfm && bio.txt.rating.lfm != -1); + this.src.name = bio.ui.show.btnBg ? ' ' : ''; switch (true) { - case !pptBio.artistView: { - const ix = txt.rev.loaded.ix == -1 ? pptBio.sourcerev : txt.rev.loaded.ix; + case !bioSet.artistView: { + const ix = bio.txt.rev.loaded.ix == -1 ? bioSet.sourcerev : bio.txt.rev.loaded.ix; this.src.name += [this.src.amRev, this.src.lfmRev, this.src.wikiRev, this.src.txtRev][ix]; break; } - case pptBio.artistView: { - const ix = txt.bio.loaded.ix == -1 ? pptBio.sourcebio : txt.bio.loaded.ix; + case bioSet.artistView: { + const ix = bio.txt.bio.loaded.ix == -1 ? bioSet.sourcebio : bio.txt.bio.loaded.ix; this.src.name += [this.src.amBio, this.src.lfmBio, this.src.wikiBio, this.src.txtBio][ix]; break; } } - this.src.name += uiBio.show.btnBg || this.rating.show ? ' ' : ''; - this.src.text = pptBio.heading && this.btns.heading && pptBio.hdBtnShow && (!!(this.src.icon || this.src.name.trim().length)); - if (!this.btns.heading || !pptBio.heading) return; - this.src.visible = pptBio.hdBtnShow && (this.rating.show || this.src.text) && pptBio.hdPos != 2; + this.src.name += bio.ui.show.btnBg || this.rating.show ? ' ' : ''; + this.src.text = bioSet.heading && this.btns.heading && bioSet.hdBtnShow && (!!(this.src.icon || this.src.name.trim().length)); + if (!this.btns.heading || !bioSet.heading) return; + this.src.visible = bioSet.hdBtnShow && (this.rating.show || this.src.text) && bioSet.hdPos != 2; if (!this.src.visible) this.src.w = 0; else { this.src.name_w = 0; - if (this.rating.show) this.src.name_w = txt.rev.loaded.am ? this.src.item_w.amRev : this.src.item_w.lfmRev; - this.src.name_w = this.src.name_w + this.src.item_w.space * (uiBio.show.btnBg ? (this.src.name_w ? 2 : 1) : 0); + if (this.rating.show) this.src.name_w = bio.txt.rev.loaded.am ? this.src.item_w.amRev : this.src.item_w.lfmRev; + this.src.name_w = this.src.name_w + this.src.item_w.space * (bio.ui.show.btnBg ? (this.src.name_w ? 2 : 1) : 0); this.src.w = 0; switch (true) { case this.rating.show: - this.src.w = this.src.name_w + this.rating.w2 + (this.src.text || uiBio.show.btnBg ? this.src.item_w.space : 0); + this.src.w = this.src.name_w + this.rating.w2 + (this.src.text || bio.ui.show.btnBg ? this.src.item_w.space : 0); break; case this.src.text: switch (true) { - case !pptBio.artistView: { - const ix = txt.rev.loaded.ix == -1 ? pptBio.sourcerev : txt.rev.loaded.ix; + case !bioSet.artistView: { + const ix = bio.txt.rev.loaded.ix == -1 ? bioSet.sourcerev : bio.txt.rev.loaded.ix; this.src.w = [this.src.item_w.amRev, this.src.item_w.lfmRev, this.src.item_w.wikiRev, this.src.item_w.txtRev][ix]; break; } - case pptBio.artistView: { - const ix = txt.bio.loaded.ix == -1 ? pptBio.sourcebio : txt.bio.loaded.ix; + case bioSet.artistView: { + const ix = bio.txt.bio.loaded.ix == -1 ? bioSet.sourcebio : bio.txt.bio.loaded.ix; this.src.w = [this.src.item_w.amBio, this.src.item_w.lfmBio, this.src.item_w.wikiBio, this.src.item_w.txtBio][ix]; break; } } - this.src.w += this.src.item_w.space * (uiBio.show.btnBg ? 2 : 0); + this.src.w += this.src.item_w.space * (bio.ui.show.btnBg ? 2 : 0); break; } - if (!uiBio.show.btnBg) this.src.name_w += this.src.item_w.space * (this.src.text ? 2 : 0); + if (!bio.ui.show.btnBg) this.src.name_w += this.src.item_w.space * (this.src.text ? 2 : 0); } } checkScrollBtns(x, y, hover_btn) { - const arr = alb_scrollbar.timer_but ? this.scr.albBtns : art_scrollbar.timer_but ? this.scr.artBtns : false; + const arr = bio.alb_scrollbar.timer_but ? this.scr.albBtns : bio.art_scrollbar.timer_but ? this.scr.artBtns : false; if (arr) { if ((this.btns[arr[0]].down || this.btns[arr[1]].down) && !this.btns[arr[0]].trace(x, y) && !this.btns[arr[1]].trace(x, y)) { this.btns[arr[0]].cs('normal'); this.btns[arr[1]].cs('normal'); - if (alb_scrollbar.timer_but) { - clearTimeout(alb_scrollbar.timer_but); - alb_scrollbar.timer_but = null; - alb_scrollbar.count = -1; + if (bio.alb_scrollbar.timer_but) { + clearTimeout(bio.alb_scrollbar.timer_but); + bio.alb_scrollbar.timer_but = null; + bio.alb_scrollbar.count = -1; } - if (art_scrollbar.timer_but) { - clearTimeout(art_scrollbar.timer_but); - art_scrollbar.timer_but = null; - art_scrollbar.count = -1; + if (bio.art_scrollbar.timer_but) { + clearTimeout(bio.art_scrollbar.timer_but); + bio.art_scrollbar.timer_but = null; + bio.art_scrollbar.count = -1; } } } else if (hover_btn) { @@ -201,33 +201,33 @@ class ButtonsBio { } clearTooltip() { - if (!tooltipBio.Text || !this.btns.lookUp.tt) return; + if (!bioTooltip.Text || !this.btns.lookUp.tt) return; this.btns.lookUp.tt.stop(); } createImages(n) { if (n == 'all') { - const sz = this.scr.arrow == 0 ? Math.max(Math.round(uiBio.sbar.but_h * 1.666667), 1) : 100; + const sz = this.scr.arrow == 0 ? Math.max(Math.round(bio.ui.sbar.but_h * 1.666667), 1) : 100; const sc = sz / 100; const iconFont = gdi.Font(this.scr.iconFontName, sz, this.scr.iconFontStyle); - this.alpha = !uiBio.sbar.col ? [180, 220, 255] : [180, 220, 255]; - const hovAlpha = (!uiBio.sbar.col ? 75 : (!uiBio.sbar.type ? 68 : 51)) * 0.4; - this.scr.hover = pptBio.sbarType == 3 ? RGBA(55, 55, 55, 255) : !uiBio.sbar.col ? RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, hovAlpha) : uiBio.col.text & RGBA(255, 255, 255, hovAlpha); + this.alpha = !bio.ui.sbar.col ? [180, 220, 255] : [180, 220, 255]; + const hovAlpha = (!bio.ui.sbar.col ? 75 : (!bio.ui.sbar.type ? 68 : 51)) * 0.4; + this.scr.hover = bioSet.sbarType == 3 ? RGBA(55, 55, 55, 255) : !bio.ui.sbar.col ? RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, hovAlpha) : bio.ui.col.text & RGBA(255, 255, 255, hovAlpha); this.scr.img = $Bio.gr(sz, sz, true, g => { g.SetTextRenderingHint(3); g.SetSmoothingMode(2); - if (pptBio.sbarType == 3) { + if (bioSet.sbarType == 3) { g.DrawString(this.scr.arrow, iconFont, RGBA(103, 103, 103, 255), 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); - } else if (pptBio.sbarCol) { - this.scr.arrow == 0 ? g.FillPolygon(uiBio.col.text, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, uiBio.col.text, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); + } else if (bioSet.sbarCol) { + this.scr.arrow == 0 ? g.FillPolygon(bio.ui.col.text, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, bio.ui.col.text, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); } else { - this.scr.arrow == 0 ? g.FillPolygon(RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, 255), 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, 255), 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); + this.scr.arrow == 0 ? g.FillPolygon(RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, 255), 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, 255), 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); } g.SetSmoothingMode(0); }); } if (n == 'all' || n == 'lookUp') { - this.lookUp.col = $Bio.toRGB(uiBio.col.text); + this.lookUp.col = $Bio.toRGB(bio.ui.col.text); $Bio.gr(1, 1, false, g => { this.lookUp.sz = Math.max(g.CalcTextWidth('\uF107', this.lookUp.font), g.CalcTextWidth('\uF023', this.lookUp.fontLock), g.CalcTextHeight('\uF107', this.lookUp.font), g.CalcTextHeight('\uF023', this.lookUp.fontLock)); }); @@ -235,26 +235,26 @@ class ButtonsBio { } createStars(force) { - this.src.icon = uiBio.show.btnLabel == 2 ? 1 : 0; - const hs = uiBio.font.heading.Size; - const fs = uiBio.stars != 1 ? (this.src.icon ? (this.src.bahnInstalled ? 12 : 11) : 10) * $Bio.scale : (RES_4K ? 26 : 14); + this.src.icon = bio.ui.show.btnLabel == 2 ? 1 : 0; + const hs = bio.ui.font.heading.Size; + const fs = bio.ui.stars != 1 ? (this.src.icon ? (this.src.bahnInstalled ? 12 : 11) : 10) * $Bio.scale : (RES._4K ? 26 : 14); const srcFontSize = this.src.fontSize; - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`] || 14; - this.src.fontSize = $Bio.clamp(Math.round(hs * 1.0) + (pptBio.zoomHeadBtn - 100) / 10, Math.min(fs, hs), Math.max(fs, hs)); + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`] || 14; + this.src.fontSize = $Bio.clamp(Math.round(hs * 1.0) + (bioSet.zoomHeadBtn - 100) / 10, Math.min(fs, hs), Math.max(fs, hs)); if (this.src.fontSize != srcFontSize || force) this.src.font = gdi.Font('Segoe UI', this.src.fontSize, 1); $Bio.gr(1, 1, false, g => { this.src.h = g.CalcTextHeight('allmusic', this.src.font); switch (this.src.icon) { case 0: - this.src.amBio = cfg.amDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.amRev = cfg.amDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.lfmBio = cfg.lfmDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.lfmRev = cfg.lfmDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.wikiBio = cfg.wikiDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.wikiRev = cfg.wikiDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.txtBio = (txt.bio.subhead.txt[0] || '').toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.txtRev = (txt.rev.subhead.txt[0] || '').toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - if (!uiBio.show.btnLabel) { + this.src.amBio = bioCfg.amDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.amRev = bioCfg.amDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.lfmBio = bioCfg.lfmDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.lfmRev = bioCfg.lfmDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.wikiBio = bioCfg.wikiDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.wikiRev = bioCfg.wikiDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.txtBio = (bio.txt.bio.subhead.txt[0] || '').toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.txtRev = (bio.txt.rev.subhead.txt[0] || '').toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + if (!bio.ui.show.btnLabel) { this.src.amBio = ''; this.src.amRev = ''; this.src.lfmBio = ''; @@ -269,13 +269,13 @@ class ButtonsBio { }); break; case 1: { - this.src.amBio = this.src.amRev = cfg.amDisplayName.toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.lfmBio = this.src.lfmRev = `\uF202${!pptBio.sourceAll ? '' : '... '}`; - this.src.wikiBio = this.src.wikiRev = `\uF266${!pptBio.sourceAll ? '' : '... '}`; - this.src.txtBio = (txt.bio.subhead.txt[0] || '').toLowerCase() + (!pptBio.sourceAll ? '' : '... '); - this.src.txtRev = (txt.rev.subhead.txt[0] || '').toLowerCase() + (!pptBio.sourceAll ? '' : '... '); + this.src.amBio = this.src.amRev = bioCfg.amDisplayName.toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.lfmBio = this.src.lfmRev = `\uF202${!bioSet.sourceAll ? '' : '... '}`; + this.src.wikiBio = this.src.wikiRev = `\uF266${!bioSet.sourceAll ? '' : '... '}`; + this.src.txtBio = (bio.txt.bio.subhead.txt[0] || '').toLowerCase() + (!bioSet.sourceAll ? '' : '... '); + this.src.txtRev = (bio.txt.rev.subhead.txt[0] || '').toLowerCase() + (!bioSet.sourceAll ? '' : '... '); if (this.src.fontSize != srcFontSize || force) { - this.src.font = gdi.Font(uiBio.font.heading.Name, Math.max(Math.round(uiBio.font.headingBaseSize * uiBio.font.zoomSize / (biographyFontSize) * (100 + ((pptBio.zoomHead - 100) / uiBio.font.boldAdjust)) / 100), 6), uiBio.font.headingStyle); // gdi.Font(this.src.bahnInstalled ? this.src.bahn : 'Segoe UI Semibold', this.src.fontSize, 0); + this.src.font = gdi.Font(bio.ui.font.heading.Name, Math.max(Math.round(bio.ui.font.headingBaseSize * bio.ui.font.zoomSize / (biographyFontSize) * (100 + ((bioSet.zoomHead - 100) / bio.ui.font.boldAdjust)) / 100), 6), bio.ui.font.headingStyle); // gdi.Font(this.src.bahnInstalled ? this.src.bahn : 'Segoe UI Semibold', this.src.fontSize, 0); this.src.iconFont = gdi.Font('FontAwesome', Math.round(this.src.fontSize * (this.src.bahnInstalled ? 1.09 : 1.16)), 0); } const alt_w = []; @@ -285,17 +285,17 @@ class ButtonsBio { this.src.item_w[v] = g.CalcTextWidth(i < 9 ? this.src[v] : alt_w[i], fonts[i], true); }); this.src.item_w.space = Math.max(this.src.item_w.space, this.src.item_w.spaceIconFont); - const n = pptBio.artistView ? 'bio' : 'rev'; - this.src.y = this.src.fontSize < 12 || txt[n].loaded.ix == 2 ? 1 : 0; + const n = bioSet.artistView ? 'bio' : 'rev'; + this.src.y = this.src.fontSize < 12 || bio.txt[n].loaded.ix == 2 ? 1 : 0; break; } } }); - if (uiBio.stars == 1) this.setRatingImages(Math.round(this.src.h / 1.3) * 5, Math.round(this.src.h / 1.3), uiBio.col.starOn, uiBio.col.starOff, uiBio.col.starBor, false); - else if (uiBio.stars == 2) { - this.setRatingImages(Math.round(uiBio.font.main_h / 1.75) * 5, Math.round(uiBio.font.main_h / 1.75), uiBio.col.starOn, uiBio.col.starOff, uiBio.col.starBor, false); + if (bio.ui.stars == 1) this.setRatingImages(Math.round(this.src.h / 1.3) * 5, Math.round(this.src.h / 1.3), bio.ui.col.starOn, bio.ui.col.starOff, bio.ui.col.starBor, false); + else if (bio.ui.stars == 2) { + this.setRatingImages(Math.round(bio.ui.font.main_h / 1.75) * 5, Math.round(bio.ui.font.main_h / 1.75), bio.ui.col.starOn, bio.ui.col.starOff, bio.ui.col.starBor, false); } - if (uiBio.stars == 1 && uiBio.show.btnRedLastfm) this.setRatingImages(Math.round(this.src.h / 1.5) * 5, Math.round(this.src.h / 1.5), RGBA(225, 225, 245, 255), RGBA(225, 225, 245, 60), uiBio.col.starBor, true); + if (bio.ui.stars == 1 && bio.ui.show.btnRedLastfm) this.setRatingImages(Math.round(this.src.h / 1.5) * 5, Math.round(this.src.h / 1.5), RGBA(225, 225, 245, 255), RGBA(225, 225, 245, 60), bio.ui.col.starBor, true); this.src.pxShift = /[gjpqy]/.test(this.src.amRev + this.src.lfmRev + this.src.wikiRev + this.src.txtRev + this.src.amBio + this.src.lfmBio + this.src.wikiBio + this.src.txtBio); } @@ -330,13 +330,13 @@ class ButtonsBio { } isNextSourceAvailable() { - let n = pptBio.artistView ? 'Bio' : 'Rev'; - if (pptBio.lockBio && !pptBio.sourceAll) return true; - n = pptBio.artistView ? 'bio' : 'rev'; - const types = txt[n].reader && panelBio.stndItem() ? $Bio.source.amLfmWikiTxt : $Bio.source.amLfmWiki; + let n = bioSet.artistView ? 'Bio' : 'Rev'; + if (bioSet.lockBio && !bioSet.sourceAll) return true; + n = bioSet.artistView ? 'bio' : 'rev'; + const types = bio.txt[n].reader && bio.panel.stndItem() ? $Bio.source.amLfmWikiTxt : $Bio.source.amLfmWiki; let found = 0; return types.some(type => { - if (txt[n][type]) found++; + if (bio.txt[n][type]) found++; if (found == 2) return true; }); } @@ -380,7 +380,7 @@ class ButtonsBio { this.scr.init = false; this.checkScrollBtns(x, y, hover_btn); if (hover_btn) hand = hover_btn.hand; - if (!resize.down) window.SetCursor(!hand && !seeker.hand && !filmStrip.hand ? 32512 : 32649); + if (!bio.resize.down) window.SetCursor(!hand && !bio.seeker.hand && !bio.filmStrip.hand ? 32512 : 32649); if (hover_btn && hover_btn.hide) { if (this.cur) { this.cur.cs('normal'); @@ -418,98 +418,98 @@ class ButtonsBio { refresh(upd) { if (upd) { - this.scr.x1 = panelBio.sbar.x; - this.scr.yUp1 = Math.round(panelBio.sbar.y); - this.scr.yDn1 = Math.round(panelBio.sbar.y + panelBio.sbar.h - uiBio.sbar.but_h); + this.scr.x1 = bio.panel.sbar.x; + this.scr.yUp1 = Math.round(bio.panel.sbar.y); + this.scr.yDn1 = Math.round(bio.panel.sbar.y + bio.panel.sbar.h - bio.ui.sbar.but_h); - if (uiBio.sbar.type != 2) { + if (bio.ui.sbar.type != 2) { this.scr.x1 -= 1; - this.scr.x2 = (uiBio.sbar.but_h - uiBio.sbar.but_w) / 2; - this.scr.yUp2 = -uiBio.sbar.arrowPad + this.scr.yUp1 + (uiBio.sbar.but_h - 1 - uiBio.sbar.but_w) / 2; - this.scr.yDn2 = uiBio.sbar.arrowPad + this.scr.yDn1 + (uiBio.sbar.but_h - 1 - uiBio.sbar.but_w) / 2; + this.scr.x2 = (bio.ui.sbar.but_h - bio.ui.sbar.but_w) / 2; + this.scr.yUp2 = -bio.ui.sbar.arrowPad + this.scr.yUp1 + (bio.ui.sbar.but_h - 1 - bio.ui.sbar.but_w) / 2; + this.scr.yDn2 = bio.ui.sbar.arrowPad + this.scr.yDn1 + (bio.ui.sbar.but_h - 1 - bio.ui.sbar.but_w) / 2; } this.setLookUpPos(); } - const n = pptBio.artistView ? 'bio' : 'rev'; + const n = bioSet.artistView ? 'bio' : 'rev'; this.check(); - if (pptBio.heading) { - this.btns.heading = new BtnBio(panelBio.heading.x, panelBio.text.t - uiBio.heading.h, panelBio.heading.w - (this.lookUp.pos == 2 ? this.lookUp.sz + (pptBio.hdPos != 2 ? this.lookUp.gap : 10) * $Bio.scale : 0), uiBio.font.heading_h, 6, $Bio.clamp(Math.round(panelBio.text.t - uiBio.heading.h + (uiBio.font.heading_h - this.src.h) / 2 + pptBio.hdBtnPad), panelBio.text.t - uiBio.heading.h, panelBio.text.t - uiBio.heading.h + uiBio.font.heading_h - this.src.h), '', '', '', !pptBio.heading || pptBio.img_only, '', () => { + if (bioSet.heading) { + this.btns.heading = new BioBtn(bio.panel.heading.x, bio.panel.text.t - bio.ui.heading.h, bio.panel.heading.w - (this.lookUp.pos == 2 ? this.lookUp.sz + (bioSet.hdPos != 2 ? this.lookUp.gap : 10) * $Bio.scale : 0), bio.ui.font.heading_h, 6, $Bio.clamp(Math.round(bio.panel.text.t - bio.ui.heading.h + (bio.ui.font.heading_h - this.src.h) / 2 + bioSet.hdBtnPad), bio.panel.text.t - bio.ui.heading.h, bio.panel.text.t - bio.ui.heading.h + bio.ui.font.heading_h - this.src.h), '', '', '', !bioSet.heading || bioSet.img_only, '', () => { if (this.isNextSourceAvailable()) { - txt.na = ''; - menBio.toggle('', pptBio.artistView ? 'Bio' : 'Rev', '', panelBio.m.x > panelBio.heading.x + panelBio.heading.w / 2 ? 1 : -1); + bio.txt.na = ''; + bio.men.toggle('', bioSet.artistView ? 'Bio' : 'Rev', '', bio.panel.m.x > bio.panel.heading.x + bio.panel.heading.w / 2 ? 1 : -1); } else { - txt.na = panelBio.m.x > panelBio.heading.x + panelBio.heading.w / 2 ? 'Next N/A: ' : 'Previous N/A: '; - txt.paint(); - timerBio.clear(timerBio.source); - timerBio.source.id = setTimeout(() => { - txt.na = ''; - txt.paint(); - timerBio.source.id = null; + bio.txt.na = bio.panel.m.x > bio.panel.heading.x + bio.panel.heading.w / 2 ? 'Next N/A: ' : 'Previous N/A: '; + bio.txt.paint(); + bio.timer.clear(bio.timer.source); + bio.timer.source.id = setTimeout(() => { + bio.txt.na = ''; + bio.txt.paint(); + bio.timer.source.id = null; }, 5000); } this.check(true); - if (uiBio.style.isBlur) window.Repaint(); + if (bio.ui.style.isBlur) window.Repaint(); }, () => this.srcTiptext(), true, 'heading'); this.src.col = { - normal: txt[n].loaded.ix != 1 || !uiBio.show.btnRedLastfm ? uiBio.style.bg || !uiBio.style.bg && !uiBio.style.trans || uiBio.blur.dark || uiBio.blur.light || uiBio.col.headingBtn !== '' ? uiBio.col.headBtn : RGB(255, 255, 255) : RGB(225, 225, 245), - hover: txt[n].loaded.ix != 1 || !uiBio.show.btnRedLastfm ? uiBio.style.bg || !uiBio.style.bg && !uiBio.style.trans || uiBio.blur.dark || uiBio.blur.light || uiBio.col.headingBtn !== '' ? uiBio.col.text_h : RGB(255, 255, 255) : RGB(225, 225, 245) + normal: bio.txt[n].loaded.ix != 1 || !bio.ui.show.btnRedLastfm ? bio.ui.style.bg || !bio.ui.style.bg && !bio.ui.style.trans || bio.ui.blur.dark || bio.ui.blur.light || bio.ui.col.headingBtn !== '' ? bio.ui.col.headBtn : RGB(255, 255, 255) : RGB(225, 225, 245), + hover: bio.txt[n].loaded.ix != 1 || !bio.ui.show.btnRedLastfm ? bio.ui.style.bg || !bio.ui.style.bg && !bio.ui.style.trans || bio.ui.blur.dark || bio.ui.blur.light || bio.ui.col.headingBtn !== '' ? bio.ui.col.text_h : RGB(255, 255, 255) : RGB(225, 225, 245) }; - if (!pptBio.hdPos) { + if (!bioSet.hdPos) { this.flag = { - x: panelBio.heading.x, - h: Math.round(uiBio.font.heading_h * 0.56) + x: bio.panel.heading.x, + h: Math.round(bio.ui.font.heading_h * 0.56) }; - this.flag.y = panelBio.text.t - uiBio.heading.h + Math.round((uiBio.font.heading_h - this.flag.h) / 2); - if (uiBio.font.heading_h >= 28 && uiBio.font.heading_h % 2 == 0) this.flag.y++; + this.flag.y = bio.panel.text.t - bio.ui.heading.h + Math.round((bio.ui.font.heading_h - this.flag.h) / 2); + if (bio.ui.font.heading_h >= 28 && bio.ui.font.heading_h % 2 == 0) this.flag.y++; } else this.flag.sp = 0; } else delete this.btns.heading; - if (panelBio.id.lookUp) { - this.btns.lookUp = new BtnBio(this.lookUp.x, this.lookUp.y, this.lookUp.w, this.lookUp.h, 7, this.lookUp.p1, this.lookUp.p2, '', { + if (bio.panel.id.lookUp) { + this.btns.lookUp = new BioBtn(this.lookUp.x, this.lookUp.y, this.lookUp.w, this.lookUp.h, 7, this.lookUp.p1, this.lookUp.p2, '', { normal: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 100 : 50), hover: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 200 : this.alpha[1]) - }, !panelBio.id.lookUp, '', () => bMenu.load(this.lookUp.x + this.lookUp.p1, this.lookUp.y + this.lookUp.h), () => pref.showTooltipBiography ? `Click: look up...\r\n${!panelBio.id.lyricsSource && !panelBio.id.nowplayingSource ? `Middle click: ${!panelBio.lock ? 'lock: stop track change updates' : 'Unlock'}...` : 'Lock N/A with enabled lyrics or nowplaying sources'}` : '', true, 'lookUp'); + }, !bio.panel.id.lookUp, '', () => bioBMenu.load(this.lookUp.x + this.lookUp.p1, this.lookUp.y + this.lookUp.h), () => grSet.showTooltipBiography ? `Click: look up...\r\n${!bio.panel.id.lyricsSource && !bio.panel.id.nowplayingSource ? `Middle click: ${!bio.panel.lock ? 'lock: stop track change updates' : 'Unlock'}...` : 'Lock N/A with enabled lyrics or nowplaying sources'}` : '', true, 'lookUp'); } else delete this.btns.lookUp; - if (pptBio.summaryShow) { - const hide = txt[n].loaded.txt && (txt.reader[n].lyrics || txt.reader[n].props || txt.reader[n].nowplaying) || pptBio.img_only; - this.btns.summary = new Btn(panelBio.text.l, panelBio.text.t, panelBio.text.w, - pptBio.artistView ? (txt.line.h.bio * txt.bio.summaryEnd) : (txt.line.h.rev * txt.rev.summaryEnd), 8, this.lookUp.p1, this.lookUp.p2, '', { + if (bioSet.summaryShow) { + const hide = bio.txt[n].loaded.txt && (bio.txt.reader[n].lyrics || bio.txt.reader[n].props || bio.txt.reader[n].nowplaying) || bioSet.img_only; + this.btns.summary = new LibBtn(bio.panel.text.l, bio.panel.text.t, bio.panel.text.w, + bioSet.artistView ? (bio.txt.line.h.bio * bio.txt.bio.summaryEnd) : (bio.txt.line.h.rev * bio.txt.rev.summaryEnd), 8, this.lookUp.p1, this.lookUp.p2, '', { normal: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 100 : 50), hover: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 200 : this.alpha[1]) - }, hide, '', () => { pptBio.toggle('summaryCompact'); txt.refresh(1); }, '', false, 'summary'); + }, hide, '', () => { bioSet.toggle('summaryCompact'); bio.txt.refresh(1); }, '', false, 'summary'); } else delete this.btns.summary; - if (pptBio.sbarShow) { - switch (pptBio.sbarType) { + if (bioSet.sbarShow) { + switch (bioSet.sbarType) { case 2: - this.btns.alb_scrollUp = new BtnBio(this.scr.x1, this.scr.yUp1, uiBio.sbar.but_h, uiBio.sbar.but_h, 5, '', '', '', { + this.btns.alb_scrollUp = new BioBtn(this.scr.x1, this.scr.yUp1, bio.ui.sbar.but_h, bio.ui.sbar.but_h, 5, '', '', '', { normal: 1, hover: 2, down: 3 - }, pptBio.sbarShow == 1 && alb_scrollbar.narrow.show || !this.scrollAlb(), () => alb_scrollbar.but(1), '', '', false, 'alb_scrollUp'); - this.btns.alb_scrollDn = new BtnBio(this.scr.x1, this.scr.yDn1, uiBio.sbar.but_h, uiBio.sbar.but_h, 5, '', '', '', { + }, bioSet.sbarShow == 1 && bio.alb_scrollbar.narrow.show || !this.scrollAlb(), () => bio.alb_scrollbar.but(1), '', '', false, 'alb_scrollUp'); + this.btns.alb_scrollDn = new BioBtn(this.scr.x1, this.scr.yDn1, bio.ui.sbar.but_h, bio.ui.sbar.but_h, 5, '', '', '', { normal: 5, hover: 6, down: 7 - }, pptBio.sbarShow == 1 && alb_scrollbar.narrow.show || !this.scrollAlb(), () => alb_scrollbar.but(-1), '', '', false, 'alb_scrollDn'); - this.btns.art_scrollUp = new BtnBio(this.scr.x1, this.scr.yUp1, uiBio.sbar.but_h, uiBio.sbar.but_h, 5, '', '', '', { + }, bioSet.sbarShow == 1 && bio.alb_scrollbar.narrow.show || !this.scrollAlb(), () => bio.alb_scrollbar.but(-1), '', '', false, 'alb_scrollDn'); + this.btns.art_scrollUp = new BioBtn(this.scr.x1, this.scr.yUp1, bio.ui.sbar.but_h, bio.ui.sbar.but_h, 5, '', '', '', { normal: 1, hover: 2, down: 3 - }, pptBio.sbarShow == 1 && art_scrollbar.narrow.show || !this.scrollArt(), () => art_scrollbar.but(1), '', '', false, 'art_scrollUp'); - this.btns.art_scrollDn = new BtnBio(this.scr.x1, this.scr.yDn1, uiBio.sbar.but_h, uiBio.sbar.but_h, 5, '', '', '', { + }, bioSet.sbarShow == 1 && bio.art_scrollbar.narrow.show || !this.scrollArt(), () => bio.art_scrollbar.but(1), '', '', false, 'art_scrollUp'); + this.btns.art_scrollDn = new BioBtn(this.scr.x1, this.scr.yDn1, bio.ui.sbar.but_h, bio.ui.sbar.but_h, 5, '', '', '', { normal: 5, hover: 6, down: 7 - }, pptBio.sbarShow == 1 && art_scrollbar.narrow.show || !this.scrollArt(), () => art_scrollbar.but(-1), '', '', false, 'art_scrollDn'); + }, bioSet.sbarShow == 1 && bio.art_scrollbar.narrow.show || !this.scrollArt(), () => bio.art_scrollbar.but(-1), '', '', false, 'art_scrollDn'); break; default: - this.btns.alb_scrollUp = new BtnBio(this.scr.x1, this.scr.yUp1 - panelBio.sbar.top_corr, uiBio.sbar.but_h, uiBio.sbar.but_h + panelBio.sbar.top_corr, 1, this.scr.x2, this.scr.yUp2, uiBio.sbar.but_w, '', pptBio.sbarShow == 1 && alb_scrollbar.narrow.show || !this.scrollAlb(), () => alb_scrollbar.but(1), '', '', false, 'alb_scrollUp'); - this.btns.alb_scrollDn = new BtnBio(this.scr.x1, this.scr.yDn1, uiBio.sbar.but_h, uiBio.sbar.but_h + panelBio.sbar.top_corr, 2, this.scr.x2, this.scr.yDn2, uiBio.sbar.but_w, '', pptBio.sbarShow == 1 && alb_scrollbar.narrow.show || !this.scrollAlb(), () => alb_scrollbar.but(-1), '', '', false, 'alb_scrollDn'); - this.btns.art_scrollUp = new BtnBio(this.scr.x1, this.scr.yUp1 - panelBio.sbar.top_corr, uiBio.sbar.but_h, uiBio.sbar.but_h + panelBio.sbar.top_corr, 3, this.scr.x2, this.scr.yUp2, uiBio.sbar.but_w, '', pptBio.sbarShow == 1 && art_scrollbar.narrow.show || !this.scrollArt(), () => art_scrollbar.but(1), '', '', false, 'art_scrollUp'); - this.btns.art_scrollDn = new BtnBio(this.scr.x1, this.scr.yDn1, uiBio.sbar.but_h, uiBio.sbar.but_h + panelBio.sbar.top_corr, 4, this.scr.x2, this.scr.yDn2, uiBio.sbar.but_w, '', pptBio.sbarShow == 1 && art_scrollbar.narrow.show || !this.scrollArt(), () => art_scrollbar.but(-1), '', '', false, 'art_scrollDn'); + this.btns.alb_scrollUp = new BioBtn(this.scr.x1, this.scr.yUp1 - bio.panel.sbar.top_corr, bio.ui.sbar.but_h, bio.ui.sbar.but_h + bio.panel.sbar.top_corr, 1, this.scr.x2, this.scr.yUp2, bio.ui.sbar.but_w, '', bioSet.sbarShow == 1 && bio.alb_scrollbar.narrow.show || !this.scrollAlb(), () => bio.alb_scrollbar.but(1), '', '', false, 'alb_scrollUp'); + this.btns.alb_scrollDn = new BioBtn(this.scr.x1, this.scr.yDn1, bio.ui.sbar.but_h, bio.ui.sbar.but_h + bio.panel.sbar.top_corr, 2, this.scr.x2, this.scr.yDn2, bio.ui.sbar.but_w, '', bioSet.sbarShow == 1 && bio.alb_scrollbar.narrow.show || !this.scrollAlb(), () => bio.alb_scrollbar.but(-1), '', '', false, 'alb_scrollDn'); + this.btns.art_scrollUp = new BioBtn(this.scr.x1, this.scr.yUp1 - bio.panel.sbar.top_corr, bio.ui.sbar.but_h, bio.ui.sbar.but_h + bio.panel.sbar.top_corr, 3, this.scr.x2, this.scr.yUp2, bio.ui.sbar.but_w, '', bioSet.sbarShow == 1 && bio.art_scrollbar.narrow.show || !this.scrollArt(), () => bio.art_scrollbar.but(1), '', '', false, 'art_scrollUp'); + this.btns.art_scrollDn = new BioBtn(this.scr.x1, this.scr.yDn1, bio.ui.sbar.but_h, bio.ui.sbar.but_h + bio.panel.sbar.top_corr, 4, this.scr.x2, this.scr.yDn2, bio.ui.sbar.but_w, '', bioSet.sbarShow == 1 && bio.art_scrollbar.narrow.show || !this.scrollArt(), () => bio.art_scrollbar.but(-1), '', '', false, 'art_scrollDn'); break; } } - this.transition = new TransitionBio(this.btns, v => v.state !== 'normal'); + this.transition = new BioTransition(this.btns, v => v.state !== 'normal'); } reset() { @@ -517,42 +517,42 @@ class ButtonsBio { } resetZoom() { - txt.bio.scrollPos = {}; - txt.rev.scrollPos = {}; - pptBio.zoomFont = 100; - pptBio.zoomHead = 115; + bio.txt.bio.scrollPos = {}; + bio.txt.rev.scrollPos = {}; + bioSet.zoomFont = 100; + bioSet.zoomHead = 115; this.lookUp.zoomSize = this.lookUp.baseSize; - this.lookUp.scale = pptBio.zoomLookUpBtn = 100; + this.lookUp.scale = bioSet.zoomLookUpBtn = 100; this.lookUp.font = gdi.Font('FontAwesome', SCALE(18) * this.lookUp.scale / 100, 0); this.lookUp.fontLock = gdi.Font('FontAwesome', SCALE(17) * this.lookUp.scale / 100, 0); - pptBio.zoomHeadBtn = 100; - pptBio.zoomTooltip = 100; - uiBio.getFont(); + bioSet.zoomHeadBtn = 100; + bioSet.zoomTooltip = 100; + bio.ui.getFont(); this.createStars(); this.createImages('lookUp'); this.setTooltipFont(); this.refresh(true); - txt.refresh(2); - const n = pptBio.artistView ? 'bio' : 'rev'; - if (txt[n].loaded.txt && txt.reader[n].lyrics) txt.getText(); - initTheme(); + bio.txt.refresh(2); + const n = bioSet.artistView ? 'bio' : 'rev'; + if (bio.txt[n].loaded.txt && bio.txt.reader[n].lyrics) bio.txt.getText(); + grm.ui.initTheme(); DebugLog('\n>>> initTheme -> Biography -> resetZoom <<<\n'); } scrollAlb() { - return pptBio.sbarShow && !pptBio.artistView && !pptBio.img_only && txt.rev.text.length && alb_scrollbar.scrollable_lines > 0 && alb_scrollbar.active && !alb_scrollbar.narrow.show && !txt.lyricsDisplayed(); + return bioSet.sbarShow && !bioSet.artistView && !bioSet.img_only && bio.txt.rev.text.length && bio.alb_scrollbar.scrollable_lines > 0 && bio.alb_scrollbar.active && !bio.alb_scrollbar.narrow.show && !bio.txt.lyricsDisplayed(); } scrollArt() { - return pptBio.sbarShow && pptBio.artistView && !pptBio.img_only && txt.bio.text.length && art_scrollbar.scrollable_lines > 0 && art_scrollbar.active && !art_scrollbar.narrow.show && !txt.lyricsDisplayed(); + return bioSet.sbarShow && bioSet.artistView && !bioSet.img_only && bio.txt.bio.text.length && bio.art_scrollbar.scrollable_lines > 0 && bio.art_scrollbar.active && !bio.art_scrollbar.narrow.show && !bio.txt.lyricsDisplayed(); } setLookUpPos() { - this.lookUp.pos = pptBio.hdLine == 2 && pptBio.hdPos == 2 ? 0 : pptBio.heading ? panelBio.id.lookUp : 0; - this.lookUp.x = [0, 1 * $Bio.scale, (!pptBio.heading || pptBio.img_only ? panelBio.w - 1 * $Bio.scale - this.lookUp.sz - 1 : panelBio.heading.x + panelBio.heading.w - this.lookUp.sz) - 9 * $Bio.scale][this.lookUp.pos]; - this.lookUp.y = [0, 0, !pptBio.heading || pptBio.img_only ? /*0*/ 9999 : panelBio.text.t - uiBio.heading.h + (uiBio.font.heading_h - this.lookUp.sz) / 2][this.lookUp.pos]; - this.lookUp.w = [12, this.lookUp.sz * 1.5, panelBio.w - this.lookUp.x][this.lookUp.pos]; - this.lookUp.h = [12, this.lookUp.sz * 1.5, Math.max(uiBio.font.heading_h, this.lookUp.sz)][this.lookUp.pos]; + this.lookUp.pos = bioSet.hdLine == 2 && bioSet.hdPos == 2 ? 0 : bioSet.heading ? bio.panel.id.lookUp : 0; + this.lookUp.x = [0, 1 * $Bio.scale, (!bioSet.heading || bioSet.img_only ? bio.panel.w - 1 * $Bio.scale - this.lookUp.sz - 1 : bio.panel.heading.x + bio.panel.heading.w - this.lookUp.sz) - 9 * $Bio.scale][this.lookUp.pos]; + this.lookUp.y = [0, 0, !bioSet.heading || bioSet.img_only ? /*0*/ 9999 : bio.panel.text.t - bio.ui.heading.h + (bio.ui.font.heading_h - this.lookUp.sz) / 2][this.lookUp.pos]; + this.lookUp.w = [12, this.lookUp.sz * 1.5, bio.panel.w - this.lookUp.x][this.lookUp.pos]; + this.lookUp.h = [12, this.lookUp.sz * 1.5, Math.max(bio.ui.font.heading_h, this.lookUp.sz)][this.lookUp.pos]; this.lookUp.p1 = [12, this.lookUp.sz + 1, this.lookUp.sz + 1 + 9 * $Bio.scale][this.lookUp.pos]; this.lookUp.p2 = this.lookUp.sz + 1; } @@ -562,7 +562,7 @@ class ButtonsBio { if (hash == this.rating.hash) return; else this.rating.hash = hash; if (lfm) this.rating.imagesLfm = []; - if (this.src.icon && uiBio.stars == 1) onCol = onCol & 0xe0ffffff; + if (this.src.icon && bio.ui.stars == 1) onCol = onCol & 0xe0ffffff; w = w * this.rating.scale; h = h * this.rating.scale; const star_indent = 2; @@ -618,31 +618,31 @@ class ButtonsBio { } setSbarIcon() { - if (pptBio.sbarType == 3) { - this.scr.arrow = uiBio.sbar.but_w < Math.round(14 * $Bio.scale) ? '\uE018' : '\uE0A0'; - this.scr.pad = uiBio.sbar.but_w < Math.round(14 * $Bio.scale) ? -0.26 : -0.22; + if (bioSet.sbarType == 3) { + this.scr.arrow = bio.ui.sbar.but_w < Math.round(14 * $Bio.scale) ? '\uE018' : '\uE0A0'; + this.scr.pad = bio.ui.sbar.but_w < Math.round(14 * $Bio.scale) ? -0.26 : -0.22; } else { - switch (pptBio.sbarButType) { + switch (bioSet.sbarButType) { case 0: this.scr.iconFontName = 'Segoe UI Symbol'; this.scr.iconFontStyle = 0; - if (!uiBio.sbar.type) { - this.scr.arrow = uiBio.sbar.but_w < Math.round(14 * $Bio.scale) ? '\uE018' : '\uE0A0'; - this.scr.pad = uiBio.sbar.but_w < Math.round(15 * $Bio.scale) ? -0.3 : -0.22; + if (!bio.ui.sbar.type) { + this.scr.arrow = bio.ui.sbar.but_w < Math.round(14 * $Bio.scale) ? '\uE018' : '\uE0A0'; + this.scr.pad = bio.ui.sbar.but_w < Math.round(15 * $Bio.scale) ? -0.3 : -0.22; } else { - this.scr.arrow = uiBio.sbar.but_w < Math.round(14 * $Bio.scale) ? '\uE018' : '\uE0A0'; - this.scr.pad = uiBio.sbar.but_w < Math.round(14 * $Bio.scale) ? -0.26 : -0.22; + this.scr.arrow = bio.ui.sbar.but_w < Math.round(14 * $Bio.scale) ? '\uE018' : '\uE0A0'; + this.scr.pad = bio.ui.sbar.but_w < Math.round(14 * $Bio.scale) ? -0.26 : -0.22; } break; case 1: this.scr.arrow = 0; break; case 2: - this.scr.iconFontName = pptBio.butCustIconFont; + this.scr.iconFontName = bioSet.butCustIconFont; this.scr.iconFontStyle = 0; - this.scr.arrow = pptBio.arrowSymbol.charAt().trim(); + this.scr.arrow = bioSet.arrowSymbol.charAt().trim(); if (!this.scr.arrow.length) this.scr.arrow = 0; - this.scr.pad = $Bio.clamp(pptBio.sbarButPad / 100, -0.5, 0.3); + this.scr.pad = $Bio.clamp(bioSet.sbarButPad / 100, -0.5, 0.3); break; } } @@ -654,9 +654,9 @@ class ButtonsBio { arr.forEach(v => { if (this.btns[v]) this.btns[v].hide = set; }); - txt.paint(); + bio.txt.paint(); } else { - if (!pptBio.sbarShow && !set) return; + if (!bioSet.sbarShow && !set) return; this.scr.btns.forEach((v, i) => { if (this.btns[v]) this.btns[v].hide = i < 2 ? !this.scrollAlb() : !this.scrollArt(); }); @@ -665,25 +665,25 @@ class ButtonsBio { setSrcFontSize(step) { this.src.fontSize += step; - const fs = uiBio.stars != 1 ? (this.src.icon ? (this.src.bahnInstalled ? 12 : 11) : 10) * $Bio.scale : (RES_4K ? 26 : 14); - const hs = uiBio.font.heading.Size; + const fs = bio.ui.stars != 1 ? (this.src.icon ? (this.src.bahnInstalled ? 12 : 11) : 10) * $Bio.scale : (RES._4K ? 26 : 14); + const hs = bio.ui.font.heading.Size; this.src.fontSize = $Bio.clamp(this.src.fontSize, Math.min(fs, hs), Math.max(fs, hs)); - pptBio.zoomHeadBtn = (this.src.fontSize - Math.round(uiBio.font.heading.Size * 0.47)) * 10 + 100; + bioSet.zoomHeadBtn = (this.src.fontSize - Math.round(bio.ui.font.heading.Size * 0.47)) * 10 + 100; } setTooltipFont() { - tooltipBio.SetFont(uiBio.font.main.Name, uiBio.font.main.Size, uiBio.font.main.Style); + bioTooltip.SetFont(bio.ui.font.main.Name, bio.ui.font.main.Size, bio.ui.font.main.Style); } srcTiptext() { - const n = pptBio.artistView ? 'bio' : 'rev'; - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`]; - const grFlag = flagImgs.length; - const flagWidth = grFlag ? flagImgs.reduce((sum, img) => sum + img.Width + SCALE(biographyFontSize) - (RES_4K ? 60 : 18), 0) : butBio.flag.w; - const flagTooltip = grFlag ? `[${GetMetaValues(tf.artist_country).join(' \u00B7 ')}] ${txt.artist}` : txt[n].flagCountry; - const suffix = pref.showTooltipBiography ? this.isNextSourceAvailable() ? 'text' : 'N/A' : ''; - const type = pref.showTooltipBiography ? panelBio.m.x > panelBio.heading.x + panelBio.heading.w / 2 ? `Next ${suffix}` : panelBio.m.x > panelBio.heading.x ? (txt[n].flag && txt[n].flagCountry && panelBio.m.x < panelBio.heading.x + flagWidth ? flagTooltip : `Previous ${suffix}`) : '' : ''; - return this.src.visible && this.trace_src(panelBio.m.x, panelBio.m.y) || !butBio.tooltipBio.name ? type : !this.fbv1 ? butBio.tooltipBio.name : butBio.tooltipBio.name.replace(/&/g, '&&'); + const n = bioSet.artistView ? 'bio' : 'rev'; + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`]; + const grFlag = grm.ui.flagImgs.length; + const flagWidth = grFlag ? grm.ui.flagImgs.reduce((sum, img) => sum + img.Width + SCALE(biographyFontSize) - (RES._4K ? 60 : 18), 0) : bio.but.flag.w; + const flagTooltip = grFlag ? `[${GetMetaValues(grTF.artist_country).join(' \u00B7 ')}] ${bio.txt.artist}` : bio.txt[n].flagCountry; + const suffix = grSet.showTooltipBiography ? this.isNextSourceAvailable() ? 'text' : 'N/A' : ''; + const type = grSet.showTooltipBiography ? bio.panel.m.x > bio.panel.heading.x + bio.panel.heading.w / 2 ? `Next ${suffix}` : bio.panel.m.x > bio.panel.heading.x ? (bio.txt[n].flag && bio.txt[n].flagCountry && bio.panel.m.x < bio.panel.heading.x + flagWidth ? flagTooltip : `Previous ${suffix}`) : '' : ''; + return this.src.visible && this.trace_src(bio.panel.m.x, bio.panel.m.y) || !bio.but.tooltipBio.name ? type : !this.fbv1 ? bio.but.tooltipBio.name : bio.but.tooltipBio.name.replace(/&/g, '&&'); } trace(btn, x, y) { @@ -692,34 +692,34 @@ class ButtonsBio { } trace_src(x, y) { - if (!pptBio.hdBtnShow || pptBio.hdPos == 2) return false; - return x > this.src.x && x < this.src.x + this.src.w && y > panelBio.text.t - uiBio.heading.h && y < panelBio.text.t - uiBio.heading.h + uiBio.font.heading_h; + if (!bioSet.hdBtnShow || bioSet.hdPos == 2) return false; + return x > this.src.x && x < this.src.x + this.src.w && y > bio.panel.text.t - bio.ui.heading.h && y < bio.panel.text.t - bio.ui.heading.h + bio.ui.font.heading_h; } tt(n, force) { - if (tooltipBio.Text !== n || force) { - tooltipBio.Text = n; - tooltipBio.SetMaxWidth(SCALE(pref.layout !== 'default' ? 600 : 800)); - tooltipBio.Activate(); + if (bioTooltip.Text !== n || force) { + bioTooltip.Text = n; + bioTooltip.SetMaxWidth(SCALE(grSet.layout !== 'default' ? 600 : 800)); + bioTooltip.Activate(); } } wheel(step) { - if (!this.trace('lookUp', panelBio.m.x, panelBio.m.y)) return; + if (!this.trace('lookUp', bio.panel.m.x, bio.panel.m.y)) return; this.lookUp.zoomSize += step; this.lookUp.zoomSize = $Bio.clamp(this.lookUp.zoomSize, 7, 100); const o = this.btns.lookUp; - window.RepaintRect(0, o.y, panelBio.w, o.h); + window.RepaintRect(0, o.y, bio.panel.w, o.h); this.lookUp.scale = Math.round(this.lookUp.zoomSize / this.lookUp.baseSize * 100); this.lookUp.font = gdi.Font('FontAwesome', SCALE(18) * this.lookUp.scale / 100, 0); this.lookUp.fontLock = gdi.Font('FontAwesome', SCALE(17) * this.lookUp.scale / 100, 0); this.createImages('lookUp'); this.refresh(true); - pptBio.zoomLookUpBtn = this.lookUp.scale; + bioSet.zoomLookUpBtn = this.lookUp.scale; } } -class BtnBio { +class BioBtn { constructor(x, y, w, h, type, p1, p2, p3, item, hide, l_dn, l_up, tiptext, hand, name) { this.x = x; this.y = y; @@ -733,7 +733,7 @@ class BtnBio { this.hide = hide; this.l_dn = l_dn; this.l_up = l_up; - this.tt = new TooltipBio(); + this.tt = new BioTooltip(); this.tiptext = tiptext; this.hand = hand; this.name = name; @@ -752,8 +752,8 @@ class BtnBio { draw(gr) { switch (this.type) { case 5: - uiBio.theme.SetPartAndStateID(1, this.item[this.state]); - uiBio.theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h); + bio.ui.theme.SetPartAndStateID(1, this.item[this.state]); + bio.ui.theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h); break; case 6: this.drawHeading(gr); @@ -770,153 +770,153 @@ class BtnBio { } drawHeading(gr) { - const n = pptBio.artistView ? 'bio' : 'rev'; - const flag = txt[n].flag; + const n = bioSet.artistView ? 'bio' : 'rev'; + const flag = bio.txt[n].flag; let dh; let dx1; let dx2; - const dw = this.w + (butBio.lookUp.pos == 2 ? butBio.lookUp.sz + (pptBio.hdLine != 2 ? butBio.lookUp.gap : 10) * $Bio.scale : 0); + const dw = this.w + (bio.but.lookUp.pos == 2 ? bio.but.lookUp.sz + (bioSet.hdLine != 2 ? bio.but.lookUp.gap : 10) * $Bio.scale : 0); let spacer = 0; - if (pptBio.hdPos != 2) { - if (!pptBio.hdBtnShow || pptBio.hdPos == 1) { - dh = pptBio.hdPos == 1 ? (butBio.rating.show || butBio.src.text ? (pptBio.hdPos != 1 && uiBio.show.btnBg ? '' : (pptBio.hdLine != 2 ? ' ' : ' ')) : '') + txt.na + txt.heading : txt.na + txt.heading; - dx1 = this.x + butBio.src.w; - dx2 = butBio.src.x = this.x; + if (bioSet.hdPos != 2) { + if (!bioSet.hdBtnShow || bioSet.hdPos == 1) { + dh = bioSet.hdPos == 1 ? (bio.but.rating.show || bio.but.src.text ? (bioSet.hdPos != 1 && bio.ui.show.btnBg ? '' : (bioSet.hdLine != 2 ? ' ' : ' ')) : '') + bio.txt.na + bio.txt.heading : bio.txt.na + bio.txt.heading; + dx1 = this.x + bio.but.src.w; + dx2 = bio.but.src.x = this.x; } else { - dh = txt.na + txt.heading; + dh = bio.txt.na + bio.txt.heading; dx1 = this.x; - dx2 = butBio.src.x = this.x + this.w - butBio.src.w; + dx2 = bio.but.src.x = this.x + this.w - bio.but.src.w; } - } else dh = txt.na + txt.heading; + } else dh = bio.txt.na + bio.txt.heading; dh = dh.trim(); switch (true) { - case pptBio.hdLine == 1: - gr.DrawLine(this.x, this.y + uiBio.heading.line_y, this.x + dw - 1, this.y + uiBio.heading.line_y, uiBio.style.l_w, uiBio.col.bottomLine); + case bioSet.hdLine == 1: + gr.DrawLine(this.x, this.y + bio.ui.heading.line_y, this.x + dw - 1, this.y + bio.ui.heading.line_y, bio.ui.style.l_w, bio.ui.col.bottomLine); break; - case pptBio.hdLine == 2: - if (pptBio.hdPos != 2) { - const src_w = butBio.src.w + (butBio.lookUp.pos == 2 ? butBio.lookUp.sz + (pptBio.hdBtnShow || pptBio.hdPos == 1 ? 10 * $Bio.scale : 0) : 0); - const dh_w = gr.CalcTextWidth(dh, uiBio.font.heading) + butBio.src.item_w.space * (pptBio.hdPos != 1 || dh ? 2 : 0) + (pptBio.hdPos == 1 && butBio.lookUp.pos == 2 ? butBio.lookUp.sz + 10 * $Bio.scale : 0); - if (!pptBio.hdPos && dh_w < dw - src_w - butBio.src.item_w.space * (pptBio.hdPos != 2 || !butBio.src.visible ? 3 : 1)) { - gr.DrawLine(this.x + dh_w + (flag ? butBio.flag.sp : 0), Math.round(this.y + this.h / 2), this.x + dw - src_w - butBio.src.item_w.space * 3, Math.round(this.y + this.h / 2), uiBio.style.l_w, uiBio.col.centerLine); + case bioSet.hdLine == 2: + if (bioSet.hdPos != 2) { + const src_w = bio.but.src.w + (bio.but.lookUp.pos == 2 ? bio.but.lookUp.sz + (bioSet.hdBtnShow || bioSet.hdPos == 1 ? 10 * $Bio.scale : 0) : 0); + const dh_w = gr.CalcTextWidth(dh, bio.ui.font.heading) + bio.but.src.item_w.space * (bioSet.hdPos != 1 || dh ? 2 : 0) + (bioSet.hdPos == 1 && bio.but.lookUp.pos == 2 ? bio.but.lookUp.sz + 10 * $Bio.scale : 0); + if (!bioSet.hdPos && dh_w < dw - src_w - bio.but.src.item_w.space * (bioSet.hdPos != 2 || !bio.but.src.visible ? 3 : 1)) { + gr.DrawLine(this.x + dh_w + (flag ? bio.but.flag.sp : 0), Math.round(this.y + this.h / 2), this.x + dw - src_w - bio.but.src.item_w.space * 3, Math.round(this.y + this.h / 2), bio.ui.style.l_w, bio.ui.col.centerLine); } - else if ((!pptBio.hdBtnShow || pptBio.hdPos != 0) && src_w + butBio.src.item_w.space * 2 + dh_w < dw) { - gr.DrawLine(dx1 + (butBio.src.visible ? butBio.src.item_w.space * (!uiBio.show.btnBg ? 2 : 3) : pptBio.hdPos == 1 ? 0 : dh_w), Math.ceil(this.y + this.h / 2), this.x + dw - (pptBio.hdBtnShow ? dh_w : pptBio.hdPos == 1 ? dh_w : 0), Math.ceil(this.y + this.h / 2), uiBio.style.l_w, uiBio.col.centerLine); - } else if (butBio.src.visible) { - spacer = butBio.src.item_w.space * (!uiBio.show.btnBg ? 2 : 3); + else if ((!bioSet.hdBtnShow || bioSet.hdPos != 0) && src_w + bio.but.src.item_w.space * 2 + dh_w < dw) { + gr.DrawLine(dx1 + (bio.but.src.visible ? bio.but.src.item_w.space * (!bio.ui.show.btnBg ? 2 : 3) : bioSet.hdPos == 1 ? 0 : dh_w), Math.ceil(this.y + this.h / 2), this.x + dw - (bioSet.hdBtnShow ? dh_w : bioSet.hdPos == 1 ? dh_w : 0), Math.ceil(this.y + this.h / 2), bio.ui.style.l_w, bio.ui.col.centerLine); + } else if (bio.but.src.visible) { + spacer = bio.but.src.item_w.space * (!bio.ui.show.btnBg ? 2 : 3); dx1 += spacer; } } else { - const dh_w = gr.CalcTextWidth(dh, uiBio.font.heading) + butBio.src.item_w.space * 4; + const dh_w = gr.CalcTextWidth(dh, bio.ui.font.heading) + bio.but.src.item_w.space * 4; const ln_l = (dw - dh_w) / 2; if (ln_l > 1) { - gr.DrawLine(this.x, Math.ceil(this.y + this.h / 2), this.x + ln_l, Math.ceil(this.y + this.h / 2), uiBio.style.l_w, uiBio.col.centerLine); - gr.DrawLine(this.x + ln_l + dh_w, Math.ceil(this.y + this.h / 2), this.x + dw, Math.ceil(this.y + this.h / 2), uiBio.style.l_w, uiBio.col.centerLine); + gr.DrawLine(this.x, Math.ceil(this.y + this.h / 2), this.x + ln_l, Math.ceil(this.y + this.h / 2), bio.ui.style.l_w, bio.ui.col.centerLine); + gr.DrawLine(this.x + ln_l + dh_w, Math.ceil(this.y + this.h / 2), this.x + dw, Math.ceil(this.y + this.h / 2), bio.ui.style.l_w, bio.ui.col.centerLine); } } break; } if (flag) { gr.SetInterpolationMode(7); - if (!pptBio.hdPos) { - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`]; - const grFlag = flagImgs.length; + if (!bioSet.hdPos) { + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`]; + const grFlag = grm.ui.flagImgs.length; const maxFlags = Math.min(grFlag, 6); - butBio.flag.w = Math.round(butBio.flag.h * flag.Width / flag.Height); - butBio.flag.sp = grFlag ? 0 : Math.round(butBio.flag.h * 0.75 + butBio.flag.w); - butBio.flag.x = this.x; + bio.but.flag.w = Math.round(bio.but.flag.h * flag.Width / flag.Height); + bio.but.flag.sp = grFlag ? 0 : Math.round(bio.but.flag.h * 0.75 + bio.but.flag.w); + bio.but.flag.x = this.x; if (grFlag) { for (let i = 0; i < maxFlags; i++) { - gr.DrawImage(flagImgs[i], butBio.flag.x, butBio.flag.y - butBio.flag.h * 0.33, flagImgs[i].Width + SCALE(biographyFontSize) - (RES_4K ? 76 : 26), butBio.flag.h * 1.66, 0, 0, flagImgs[i].Width, flagImgs[i].Height); - butBio.flag.x += flagImgs[i].Width + SCALE(biographyFontSize) - (RES_4K ? 60 : 18); - butBio.flag.sp += flagImgs[i].Width + SCALE(biographyFontSize) - (RES_4K ? 60 : 18); + gr.DrawImage(grm.ui.flagImgs[i], bio.but.flag.x, bio.but.flag.y - bio.but.flag.h * 0.33, grm.ui.flagImgs[i].Width + SCALE(biographyFontSize) - (RES._4K ? 76 : 26), bio.but.flag.h * 1.66, 0, 0, grm.ui.flagImgs[i].Width, grm.ui.flagImgs[i].Height); + bio.but.flag.x += grm.ui.flagImgs[i].Width + SCALE(biographyFontSize) - (RES._4K ? 60 : 18); + bio.but.flag.sp += grm.ui.flagImgs[i].Width + SCALE(biographyFontSize) - (RES._4K ? 60 : 18); } } else { - gr.DrawImage(flag, butBio.flag.x, butBio.flag.y, butBio.flag.w, butBio.flag.h, 0, 0, flag.Width, flag.Height, '', 212); - // const w = uiBio.style.l_w; + gr.DrawImage(flag, bio.but.flag.x, bio.but.flag.y, bio.but.flag.w, bio.but.flag.h, 0, 0, flag.Width, flag.Height, '', 212); + // const w = bio.ui.style.l_w; // const o = Math.floor(w / 2); - // gr.DrawRect(butBio.flag.x + o, butBio.flag.y + o, butBio.flag.w - w, butBio.flag.h - w + 1, w, uiBio.col.imgBor); + // gr.DrawRect(bio.but.flag.x + o, bio.but.flag.y + o, bio.but.flag.w - w, bio.but.flag.h - w + 1, w, bio.ui.col.imgBor); } } gr.SetInterpolationMode(2); - const h_x = (pptBio.hdPos != 2 ? dx1 : this.x) + butBio.flag.sp; - const h_w = (pptBio.hdPos != 2 ? this.w - spacer - butBio.src.w - (!pptBio.hdPos ? 10 : 0) : this.w - spacer) - butBio.flag.sp; - gr.GdiDrawText(dh, uiBio.font.heading, uiBio.col.headingText, h_x, this.y, h_w, this.h, pptBio.hdPos != 2 ? txt.c[pptBio.hdPos] : txt.cc); - butBio.tooltipBio.name = gr.CalcTextWidth(dh, uiBio.font.heading) > h_w ? (!flag || !txt[n].flagCountry ? dh : pref.showTooltipBiography ? `${txt[n].flagCountry} | ${dh}` : '') : ''; - butBio.tooltipBio.x = h_x; - butBio.tooltipBio.w = h_w; + const h_x = (bioSet.hdPos != 2 ? dx1 : this.x) + bio.but.flag.sp; + const h_w = (bioSet.hdPos != 2 ? this.w - spacer - bio.but.src.w - (!bioSet.hdPos ? 10 : 0) : this.w - spacer) - bio.but.flag.sp; + gr.GdiDrawText(dh, bio.ui.font.heading, bio.ui.col.headingText, h_x, this.y, h_w, this.h, bioSet.hdPos != 2 ? bio.txt.c[bioSet.hdPos] : bio.txt.cc); + bio.but.tooltipBio.name = gr.CalcTextWidth(dh, bio.ui.font.heading) > h_w ? (!flag || !bio.txt[n].flagCountry ? dh : grSet.showTooltipBiography ? `${bio.txt[n].flagCountry} | ${dh}` : '') : ''; + bio.but.tooltipBio.x = h_x; + bio.but.tooltipBio.w = h_w; } else { - const h_x = (pptBio.hdPos != 2 ? dx1 : this.x); - const h_w = pptBio.hdPos != 2 ? this.w - spacer - butBio.src.w - (!pptBio.hdPos ? 10 : 0) : this.w - spacer; - gr.GdiDrawText(dh, uiBio.font.heading, uiBio.col.headingText, (pptBio.hdPos != 2 ? dx1 : this.x), this.y, pptBio.hdPos != 2 ? this.w - spacer - butBio.src.w - (!pptBio.hdPos ? 10 : 0) : this.w - spacer, this.h, pptBio.hdPos != 2 ? txt.c[pptBio.hdPos] : txt.cc); - butBio.tooltipBio.name = gr.CalcTextWidth(dh, uiBio.font.heading) > h_w ? dh : ''; - butBio.tooltipBio.x = h_x; - butBio.tooltipBio.w = h_w; + const h_x = (bioSet.hdPos != 2 ? dx1 : this.x); + const h_w = bioSet.hdPos != 2 ? this.w - spacer - bio.but.src.w - (!bioSet.hdPos ? 10 : 0) : this.w - spacer; + gr.GdiDrawText(dh, bio.ui.font.heading, bio.ui.col.headingText, (bioSet.hdPos != 2 ? dx1 : this.x), this.y, bioSet.hdPos != 2 ? this.w - spacer - bio.but.src.w - (!bioSet.hdPos ? 10 : 0) : this.w - spacer, this.h, bioSet.hdPos != 2 ? bio.txt.c[bioSet.hdPos] : bio.txt.cc); + bio.but.tooltipBio.name = gr.CalcTextWidth(dh, bio.ui.font.heading) > h_w ? dh : ''; + bio.but.tooltipBio.x = h_x; + bio.but.tooltipBio.w = h_w; } - if (!butBio.src.visible) return; + if (!bio.but.src.visible) return; let col; - if (uiBio.show.btnBg) { + if (bio.ui.show.btnBg) { gr.SetSmoothingMode(2); - if (txt[n].loaded.ix != 1 || !uiBio.show.btnRedLastfm) { - if (this.state !== 'down') gr.FillRoundRect(dx2, this.p1 - (butBio.src.pxShift ? 1 : 0), butBio.src.w, butBio.src.h + (butBio.src.pxShift ? 2 : 0), 2, 2, RGBA(uiBio.col.blend4[0], uiBio.col.blend4[1], uiBio.col.blend4[2], uiBio.col.blend4[3] * (1 - this.transition_factor))); - col = this.state !== 'down' ? uiBio.getBlend(uiBio.col.blend2, uiBio.col.blend1, this.transition_factor) : uiBio.col.blend2; - gr.FillRoundRect(dx2, this.p1 - (butBio.src.pxShift ? 1 : 0), butBio.src.w, butBio.src.h + (butBio.src.pxShift ? 2 : 0), 2, 2, col); - gr.DrawRoundRect(dx2, this.p1 - (butBio.src.pxShift ? 1 : 0), butBio.src.w, butBio.src.h + (butBio.src.pxShift ? 2 : 0), 2, 2, uiBio.style.l_w, uiBio.col.blend3); + if (bio.txt[n].loaded.ix != 1 || !bio.ui.show.btnRedLastfm) { + if (this.state !== 'down') gr.FillRoundRect(dx2, this.p1 - (bio.but.src.pxShift ? 1 : 0), bio.but.src.w, bio.but.src.h + (bio.but.src.pxShift ? 2 : 0), 2, 2, RGBA(bio.ui.col.blend4[0], bio.ui.col.blend4[1], bio.ui.col.blend4[2], bio.ui.col.blend4[3] * (1 - this.transition_factor))); + col = this.state !== 'down' ? bio.ui.getBlend(bio.ui.col.blend2, bio.ui.col.blend1, this.transition_factor) : bio.ui.col.blend2; + gr.FillRoundRect(dx2, this.p1 - (bio.but.src.pxShift ? 1 : 0), bio.but.src.w, bio.but.src.h + (bio.but.src.pxShift ? 2 : 0), 2, 2, col); + gr.DrawRoundRect(dx2, this.p1 - (bio.but.src.pxShift ? 1 : 0), bio.but.src.w, bio.but.src.h + (bio.but.src.pxShift ? 2 : 0), 2, 2, bio.ui.style.l_w, bio.ui.col.blend3); } else { - gr.FillRoundRect(dx2, this.p1 - (butBio.src.pxShift ? 1 : 0), butBio.src.w, butBio.src.h + (butBio.src.pxShift ? 2 : 0), 2, 2, RGBA(210, 19, 9, 114)); - col = this.state !== 'down' ? uiBio.getBlend(RGBA(244, 31, 19, 255), RGBA(210, 19, 9, 228), this.transition_factor) : RGBA(244, 31, 19, 255); - gr.FillRoundRect(dx2, this.p1 - (butBio.src.pxShift ? 1 : 0), butBio.src.w, butBio.src.h + (butBio.src.pxShift ? 2 : 0), 2, 2, col); + gr.FillRoundRect(dx2, this.p1 - (bio.but.src.pxShift ? 1 : 0), bio.but.src.w, bio.but.src.h + (bio.but.src.pxShift ? 2 : 0), 2, 2, RGBA(210, 19, 9, 114)); + col = this.state !== 'down' ? bio.ui.getBlend(RGBA(244, 31, 19, 255), RGBA(210, 19, 9, 228), this.transition_factor) : RGBA(244, 31, 19, 255); + gr.FillRoundRect(dx2, this.p1 - (bio.but.src.pxShift ? 1 : 0), bio.but.src.w, bio.but.src.h + (bio.but.src.pxShift ? 2 : 0), 2, 2, col); } } - col = this.state !== 'down' ? uiBio.getBlend(butBio.src.col.hover, butBio.src.col.normal, this.transition_factor) : butBio.src.col.hover; - switch (butBio.src.icon) { + col = this.state !== 'down' ? bio.ui.getBlend(bio.but.src.col.hover, bio.but.src.col.normal, this.transition_factor) : bio.but.src.col.hover; + switch (bio.but.src.icon) { case 0: - gr.GdiDrawText(butBio.src.name, butBio.src.font, uiBio.col.headingText, dx2, this.p1, butBio.src.w, butBio.src.h, !butBio.rating.show ? txt.cc : txt.c[0]); + gr.GdiDrawText(bio.but.src.name, bio.but.src.font, bio.ui.col.headingText, dx2, this.p1, bio.but.src.w, bio.but.src.h, !bio.but.rating.show ? bio.txt.cc : bio.txt.c[0]); break; case 1: { let iconFont = false; - iconFont = !pptBio.lockBio || pptBio.sourceAll ? txt[n].loaded.ix == 1 || txt[n].loaded.ix == 2 : pptBio[`source${n}`] == 1 || pptBio[`source${n}`] == 2; - gr.GdiDrawText(butBio.src.name, !iconFont ? butBio.src.font : butBio.src.iconFont, col, dx2, this.p1 + (!iconFont ? 0 : butBio.src.y), butBio.src.w, butBio.src.h, !butBio.rating.show ? txt.cc : txt.c[0]); + iconFont = !bioSet.lockBio || bioSet.sourceAll ? bio.txt[n].loaded.ix == 1 || bio.txt[n].loaded.ix == 2 : bioSet[`source${n}`] == 1 || bioSet[`source${n}`] == 2; + gr.GdiDrawText(bio.but.src.name, !iconFont ? bio.but.src.font : bio.but.src.iconFont, col, dx2, this.p1 + (!iconFont ? 0 : bio.but.src.y), bio.but.src.w, bio.but.src.h, !bio.but.rating.show ? bio.txt.cc : bio.txt.c[0]); break; } } - if (butBio.rating.show) { - const rating = txt.rev.loaded.am ? txt.rating.am : txt.rating.lfm; - const ratingImg = !uiBio.show.btnRedLastfm || txt.rev.loaded.am ? butBio.rating.images[rating] : butBio.rating.imagesLfm[rating]; - if (ratingImg) gr.DrawImage(ratingImg, !pptBio.hdPos ? this.x + this.w - spacer - butBio.rating.w2 - (uiBio.show.btnBg ? butBio.src.item_w.space : 0) + SCALE(4) : dx2 + butBio.src.name_w, this.p1 + (Math.round(butBio.src.h - butBio.rating.h2) / 2), butBio.rating.w2, butBio.rating.h2, 0, 0, butBio.rating.w1, butBio.rating.h1, 0, 255); + if (bio.but.rating.show) { + const rating = bio.txt.rev.loaded.am ? bio.txt.rating.am : bio.txt.rating.lfm; + const ratingImg = !bio.ui.show.btnRedLastfm || bio.txt.rev.loaded.am ? bio.but.rating.images[rating] : bio.but.rating.imagesLfm[rating]; + if (ratingImg) gr.DrawImage(ratingImg, !bioSet.hdPos ? this.x + this.w - spacer - bio.but.rating.w2 - (bio.ui.show.btnBg ? bio.but.src.item_w.space : 0) + SCALE(4) : dx2 + bio.but.src.name_w, this.p1 + (Math.round(bio.but.src.h - bio.but.rating.h2) / 2), bio.but.rating.w2, bio.but.rating.h2, 0, 0, bio.but.rating.w1, bio.but.rating.h1, 0, 255); } } drawLookUp(gr) { - // const col = this.state !== 'down' ? uiBio.getBlend(this.item.hover, this.item.normal, this.transition_factor) : this.item.hover; - const col = uiBio.col.headingText; + // const col = this.state !== 'down' ? bio.ui.getBlend(this.item.hover, this.item.normal, this.transition_factor) : this.item.hover; + const col = bio.ui.col.headingText; gr.SetTextRenderingHint(3); // AntiAliasGridFit - if (!panelBio.lock) { - gr.DrawString(!panelBio.style.moreTags || !pptBio.artistView ? '\uF107' : '\uF107', butBio.lookUp.font, col, this.x - (RES_4K ? 1 : 0), this.y + SCALE(1), this.p1, this.p2, StringFormat(2, 0)); - gr.DrawString(!panelBio.style.moreTags || !pptBio.artistView ? '\uF107' : '\uF107', butBio.lookUp.font, col, this.x - (RES_4K ? 1 : 0), this.y + SCALE(1), this.p1, this.p2, StringFormat(2, 0)); - if (this.state == 'hover') gr.DrawString(!panelBio.style.moreTags || !pptBio.artistView ? '\uF107' : '\uF107', butBio.lookUp.font, col, this.x, this.y + SCALE(1), this.p1, this.p2, StringFormat(2, 0)); + if (!bio.panel.lock) { + gr.DrawString(!bio.panel.style.moreTags || !bioSet.artistView ? '\uF107' : '\uF107', bio.but.lookUp.font, col, this.x - (RES._4K ? 1 : 0), this.y + SCALE(1), this.p1, this.p2, StringFormat(2, 0)); + gr.DrawString(!bio.panel.style.moreTags || !bioSet.artistView ? '\uF107' : '\uF107', bio.but.lookUp.font, col, this.x - (RES._4K ? 1 : 0), this.y + SCALE(1), this.p1, this.p2, StringFormat(2, 0)); + if (this.state == 'hover') gr.DrawString(!bio.panel.style.moreTags || !bioSet.artistView ? '\uF107' : '\uF107', bio.but.lookUp.font, col, this.x, this.y + SCALE(1), this.p1, this.p2, StringFormat(2, 0)); } else { - gr.DrawString('\uF023', butBio.lookUp.fontLock, col, this.x, this.y + 2 * $Bio.scale, this.p1, this.p2, StringFormat(2, 0)); + gr.DrawString('\uF023', bio.but.lookUp.fontLock, col, this.x, this.y + 2 * $Bio.scale, this.p1, this.p2, StringFormat(2, 0)); } } drawScrollBtn(gr) { gr.SetSmoothingMode(3); - const type3 = pptBio.sbarType == 3; - const a = type3 ? 255 : this.state !== 'down' ? Math.min(butBio.alpha[0] + (butBio.alpha[1] - butBio.alpha[0]) * this.transition_factor, butBio.alpha[1]) : butBio.alpha[2]; - // if (this.state !== 'normal' && (uiBio.sbar.type == 1 || type3)) gr.FillSolidRect(panelBio.sbar.x + (!type3 ? 0 : uiBio.style.l_w), this.y, uiBio.sbar.w - (!type3 ? 0 : uiBio.style.l_w * 2), this.h, butBio.scr.hover); - if (butBio.scr.img) gr.DrawImage(butBio.scr.img, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, butBio.scr.img.Width, butBio.scr.img.Height, this.type == 1 || this.type == 3 ? 0 : 180, a); + const type3 = bioSet.sbarType == 3; + const a = type3 ? 255 : this.state !== 'down' ? Math.min(bio.but.alpha[0] + (bio.but.alpha[1] - bio.but.alpha[0]) * this.transition_factor, bio.but.alpha[1]) : bio.but.alpha[2]; + // if (this.state !== 'normal' && (bio.ui.sbar.type == 1 || type3)) gr.FillSolidRect(bio.panel.sbar.x + (!type3 ? 0 : bio.ui.style.l_w), this.y, bio.ui.sbar.w - (!type3 ? 0 : bio.ui.style.l_w * 2), this.h, bio.but.scr.hover); + if (bio.but.scr.img) gr.DrawImage(bio.but.scr.img, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, bio.but.scr.img.Width, bio.but.scr.img.Height, this.type == 1 || this.type == 3 ? 0 : 180, a); gr.SetSmoothingMode(0); } lbtn_dn(x, y) { - if (!butBio.Dn) return; + if (!bio.but.Dn) return; this.l_dn && this.l_dn(x, y); } lbtn_up(x, y) { - if (panelBio.isTouchEvent(x, y)) return; + if (bio.panel.isTouchEvent(x, y)) return; if (this.l_up) this.l_up(); } @@ -927,56 +927,56 @@ class BtnBio { } trace(x, y) { - butBio.traceBtn = !this.hide && x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; - if (this.name == 'summary' && (pptBio.artistView && art_scrollbar.delta > txt.line.h.bio * txt.bio.summaryEnd || !pptBio.artistView && alb_scrollbar.delta > txt.line.h.rev * txt.rev.summaryEnd)) butBio.traceBtn = false; - return butBio.traceBtn; + bio.but.traceBtn = !this.hide && x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; + if (this.name == 'summary' && (bioSet.artistView && bio.art_scrollbar.delta > bio.txt.line.h.bio * bio.txt.bio.summaryEnd || !bioSet.artistView && bio.alb_scrollbar.delta > bio.txt.line.h.rev * bio.txt.rev.summaryEnd)) bio.but.traceBtn = false; + return bio.but.traceBtn; } } -class TooltipBio { +class BioTooltip { constructor() { this.id = Math.ceil(Math.random().toFixed(8) * 1000); - this.tt_timer = new TooltipTimerBio(); + this.tt_timer = new BioTooltipTimer(); } // * METHODS * // clear() { - styledTooltipReady = false; + grm.ui.styledTooltipReady = false; this.tt_timer.stop(this.id); } show(text) { - if (Date.now() - butBio.tooltipBio.start > 2000) this.showDelayed(text); + if (Date.now() - bio.but.tooltipBio.start > 2000) this.showDelayed(text); else this.showImmediate(text); - butBio.tooltipBio.start = Date.now(); + bio.but.tooltipBio.start = Date.now(); } showDelayed(text) { - styledTooltipText = text; - styledTooltipReady = true; - if (!pref.showStyledTooltips) { + grm.ui.styledTooltipText = text; + grm.ui.styledTooltipReady = true; + if (!grSet.showStyledTooltips) { this.tt_timer.start(this.id, text); } } showImmediate(text) { - styledTooltipText = text; - styledTooltipReady = true; - if (!pref.showStyledTooltips) { + grm.ui.styledTooltipText = text; + grm.ui.styledTooltipReady = true; + if (!grSet.showStyledTooltips) { this.tt_timer.set_id(this.id); this.tt_timer.stop(this.id); - butBio.tt(text); + bio.but.tt(text); } } stop() { - styledTooltipReady = false; + grm.ui.styledTooltipReady = false; this.tt_timer.forceStop(); } } -class TooltipTimerBio { +class BioTooltipTimer { constructor() { this.delay_timer; this.tt_caller = undefined; @@ -985,7 +985,7 @@ class TooltipTimerBio { // * METHODS * // forceStop() { - butBio.tt(''); + bio.but.tt(''); if (this.delay_timer) { clearTimeout(this.delay_timer); this.delay_timer = null; @@ -999,12 +999,12 @@ class TooltipTimerBio { start(id, text) { const old_caller = this.tt_caller; this.tt_caller = id; - if (!this.delay_timer && tooltipBio.Text) butBio.tt(text, old_caller !== this.tt_caller); + if (!this.delay_timer && bioTooltip.Text) bio.but.tt(text, old_caller !== this.tt_caller); else { this.forceStop(); if (!this.delay_timer) { this.delay_timer = setTimeout(() => { - butBio.tt(text); + bio.but.tt(text); this.delay_timer = null; }, 500); } @@ -1016,7 +1016,7 @@ class TooltipTimerBio { } } -class TransitionBio { +class BioTransition { constructor(items, hover) { this.hover = hover; this.items = items; diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-callbacks.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-callbacks.js index 2e405e4e..ad34cd84 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-callbacks.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-callbacks.js @@ -1,76 +1,83 @@ 'use strict'; -class BiographyCallbacks { +class BioCallbacks { + constructor() { + /** @type {string} */ + this.windowMetricsPathBio = grSet.customBiographyDir + ? `${grCfg.customBiographyDir}cache\\biography\\themed\\windowMetrics.json` + : `${fb.ProfilePath}cache\\biography\\themed\\windowMetrics.json`; + } + on_colours_changed() { - uiBio.getColours(); - alb_scrollbar.setCol(); - art_scrollbar.setCol(); - imgBio.createImages(); - filmStrip.logScrollPos(); - filmStrip.clearCache(); - filmStrip.createBorder(); - butBio.createImages('all'); - butBio.refresh(true); - alb_scrollbar.resetAuto(); - art_scrollbar.resetAuto(); - if (uiBio.font.heading && uiBio.font.heading.Size) butBio.createStars(); - imgBio.clearCache(); - imgBio.getImages(); - if (panelBio.id.lyricsSource) { - lyricsBio.transBot = {} - lyricsBio.transTop = {} + bio.ui.getColours(); + bio.alb_scrollbar.setCol(); + bio.art_scrollbar.setCol(); + bio.img.createImages(); + bio.filmStrip.logScrollPos(); + bio.filmStrip.clearCache(); + bio.filmStrip.createBorder(); + bio.but.createImages('all'); + bio.but.refresh(true); + bio.alb_scrollbar.resetAuto(); + bio.art_scrollbar.resetAuto(); + if (bio.ui.font.heading && bio.ui.font.heading.Size) bio.but.createStars(); + bio.img.clearCache(); + bio.img.getImages(); + if (bio.panel.id.lyricsSource) { + bio.lyrics.transBot = {} + bio.lyrics.transTop = {} } - txt.rev.cur = ''; - txt.bio.cur = ''; - txt.albCalc(); - txt.artCalc(); - txt.paint(); + bio.txt.rev.cur = ''; + bio.txt.bio.cur = ''; + bio.txt.albCalc(); + bio.txt.artCalc(); + bio.txt.paint(); } on_font_changed() { - uiBio.getFont(); - alb_scrollbar.reset(); - art_scrollbar.reset(); - alb_scrollbar.resetAuto(); - art_scrollbar.resetAuto(); - txt.on_size(); - imgBio.on_size(); + bio.ui.getFont(); + bio.alb_scrollbar.reset(); + bio.art_scrollbar.reset(); + bio.alb_scrollbar.resetAuto(); + bio.art_scrollbar.resetAuto(); + bio.txt.on_size(); + bio.img.on_size(); window.Repaint(); } on_focus(is_focused) { - resize.focus = is_focused; + bio.resize.focus = is_focused; } on_get_album_art_done(handle, art_id, image, image_path) { - imgBio.on_get_album_art_done(handle, art_id, image, image_path); + bio.img.on_get_album_art_done(handle, art_id, image, image_path); } on_item_focus_change() { - if (!pptBio.panelActive) return; - if (fb.IsPlaying && !panelBio.id.focus) return; - txt.notifyTags(); - if (panelBio.id.lookUp) panelBio.getList(true, true); - else if (!panelBio.updateNeeded()) return; - if (panelBio.block() && !$Bio.server) { - imgBio.get = true; - txt.get = panelBio.id.focus ? 2 : 1; - imgBio.artistReset(); - txt.albumReset(); - txt.artistReset(); + if (!bioSet.panelActive) return; + if (fb.IsPlaying && !bio.panel.id.focus) return; + bio.txt.notifyTags(); + if (bio.panel.id.lookUp) bio.panel.getList(true, true); + else if (!bio.panel.updateNeeded()) return; + if (bio.panel.block() && !$Bio.server) { + bio.img.get = true; + bio.txt.get = bio.panel.id.focus ? 2 : 1; + bio.img.artistReset(); + bio.txt.albumReset(); + bio.txt.artistReset(); } else { - if (panelBio.block() && $Bio.server) { - imgBio.get = true; - txt.get = 1; - imgBio.artistReset(); - txt.albumReset(); - txt.artistReset(); + if (bio.panel.block() && $Bio.server) { + bio.img.get = true; + bio.txt.get = 1; + bio.img.artistReset(); + bio.txt.albumReset(); + bio.txt.artistReset(); } else { - imgBio.get = false; - txt.get = 0; + bio.img.get = false; + bio.txt.get = 0; } - panelBio.focusLoad(); - panelBio.focusServer(); + bio.panel.focusLoad(); + bio.panel.focusServer(); } } @@ -82,34 +89,34 @@ class BiographyCallbacks { window.Repaint(); break; case 0x21: - if (panelBio.trace.text) { - if (!txt.lyricsDisplayed()) txt.scrollbar_type().pageThrottle(1); - } else if (panelBio.trace.film) filmStrip.scrollerType().pageThrottle(1); + if (bio.panel.trace.text) { + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().pageThrottle(1); + } else if (bio.panel.trace.film) bio.filmStrip.scrollerType().pageThrottle(1); break; case 0x22: - if (panelBio.trace.text) { - if (!txt.lyricsDisplayed()) txt.scrollbar_type().pageThrottle(-1); - } else if (panelBio.trace.film) filmStrip.scrollerType().pageThrottle(-1); + if (bio.panel.trace.text) { + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().pageThrottle(-1); + } else if (bio.panel.trace.film) bio.filmStrip.scrollerType().pageThrottle(-1); break; case 35: - if (panelBio.trace.text) { - if (!txt.lyricsDisplayed()) txt.scrollbar_type().scrollToEnd(); - } else if (panelBio.trace.film) filmStrip.scrollerType().scrollToEnd(); + if (bio.panel.trace.text) { + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().scrollToEnd(); + } else if (bio.panel.trace.film) bio.filmStrip.scrollerType().scrollToEnd(); break; case 36: - if (panelBio.trace.text) { - if (!txt.lyricsDisplayed()) txt.scrollbar_type().checkScroll(0, 'full'); - } else if (panelBio.trace.film) filmStrip.scrollerType().checkScroll(0, 'full'); + if (bio.panel.trace.text) { + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().checkScroll(0, 'full'); + } else if (bio.panel.trace.film) bio.filmStrip.scrollerType().checkScroll(0, 'full'); break; case 37: case 38: - if (panelBio.imgBoxTrace(panelBio.m.x, panelBio.m.y)) imgBio.wheel(1); - else if (panelBio.trace.film) filmStrip.scrollerType().wheel(1); + if (bio.panel.imgBoxTrace(bio.panel.m.x, bio.panel.m.y)) bio.img.wheel(1); + else if (bio.panel.trace.film) bio.filmStrip.scrollerType().wheel(1); break; case 39: case 40: - if (panelBio.imgBoxTrace(panelBio.m.x, panelBio.m.y)) imgBio.wheel(-1); - else if (panelBio.trace.film) filmStrip.scrollerType().wheel(-1); + if (bio.panel.imgBoxTrace(bio.panel.m.x, bio.panel.m.y)) bio.img.wheel(-1); + else if (bio.panel.trace.film) bio.filmStrip.scrollerType().wheel(-1); break; } } @@ -119,170 +126,170 @@ class BiographyCallbacks { } on_library_items_added() { - if (!biographyInitialized || !pptBio.panelActive) return; - if (!libBio) return; - libBio.update = true; + if (!bio.initialized || !bioSet.panelActive) return; + if (!bio.lib) return; + bio.lib.update = true; } on_library_items_removed() { - if (!biographyInitialized || !pptBio.panelActive) return; - if (!libBio) return; - libBio.update = true; + if (!bio.initialized || !bioSet.panelActive) return; + if (!bio.lib) return; + bio.lib.update = true; } on_library_items_changed() { - if (!biographyInitialized || !pptBio.panelActive) return; - if (!libBio) return; - libBio.update = true; + if (!bio.initialized || !bioSet.panelActive) return; + if (!bio.lib) return; + bio.lib.update = true; } on_load_image_done(task_id, image, image_path) { - imgBio.on_load_image_done(image, image_path); - filmStrip.on_load_image_done(image, image_path); + bio.img.on_load_image_done(image, image_path); + bio.filmStrip.on_load_image_done(image, image_path); } on_metadb_changed() { - if (!pptBio.panelActive) return; - if (panelBio.isRadio(panelBio.id.focus) || panelBio.block() && !$Bio.server || !panelBio.updateNeeded() || txt.lyricsDisplayed()) return; - panelBio.getList(true, true); - panelBio.focusLoad(); - panelBio.focusServer(); + if (!bioSet.panelActive) return; + if (bio.panel.isRadio(bio.panel.id.focus) || bio.panel.block() && !$Bio.server || !bio.panel.updateNeeded() || bio.txt.lyricsDisplayed()) return; + bio.panel.getList(true, true); + bio.panel.focusLoad(); + bio.panel.focusServer(); } on_mouse_lbtn_dblclk(x, y) { - if (!pptBio.panelActive) return; - butBio.lbtn_dn(x, y); - if (!txt.lyricsDisplayed()) txt.scrollbar_type().lbtn_dblclk(x, y); - if (!pptBio.dblClickToggle) return; - if (pptBio.touchControl) { - panelBio.id.last_pressed_coord = { + if (!bioSet.panelActive) return; + bio.but.lbtn_dn(x, y); + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().lbtn_dblclk(x, y); + if (!bioSet.dblClickToggle) return; + if (bioSet.touchControl) { + bio.panel.id.last_pressed_coord = { x, y }; } - if (!panelBio.trace.film) panelBio.click(x, y); - else filmStrip.lbtn_dblclk(x, y); + if (!bio.panel.trace.film) bio.panel.click(x, y); + else bio.filmStrip.lbtn_dblclk(x, y); } on_mouse_lbtn_down(x, y) { - if (!pptBio.panelActive) return; - if (pptBio.touchControl) { - panelBio.id.last_pressed_coord = { + if (!bioSet.panelActive) return; + if (bioSet.touchControl) { + bio.panel.id.last_pressed_coord = { x, y }; } - if (panelBio.trace.image && vkBio.k('alt')) { - const imgPth = imgBio.pth().imgPth; + if (bio.panel.trace.image && bio.vk.k('alt')) { + const imgPth = bio.img.pth().imgPth; if (imgPth) $Bio.browser(`explorer /select,"${imgPth}"`, false) } else { - resize.lbtn_dn(x, y); - butBio.lbtn_dn(x, y); - if (!txt.lyricsDisplayed()) txt.scrollbar_type().lbtn_dn(x, y); - // filmStrip.scrollerType().lbtn_dn(x, y); // Causes locked mouse follow on filmstrip click - seeker.lbtn_dn(x, y); - imgBio.lbtn_dn(x); + bio.resize.lbtn_dn(x, y); + bio.but.lbtn_dn(x, y); + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().lbtn_dn(x, y); + // bio.filmStrip.scrollerType().lbtn_dn(x, y); // Causes locked mouse follow on filmstrip click + bio.seeker.lbtn_dn(x, y); + bio.img.lbtn_dn(x); } } on_mouse_lbtn_up(x, y) { - if (!pptBio.panelActive) { panelBio.inactivate(); return; } - alb_scrollbar.lbtn_drag_up(); - art_scrollbar.lbtn_drag_up(); - art_scroller.lbtn_drag_up(); - cov_scroller.lbtn_drag_up(); - if (!pptBio.dblClickToggle && !butBio.Dn && !seeker.dn && !panelBio.trace.film) panelBio.click(x, y); - if (!txt.lyricsDisplayed()) txt.scrollbar_type().lbtn_up(); - panelBio.clicked = false; - resize.lbtn_up(); - butBio.lbtn_up(x, y); - filmStrip.lbtn_up(x, y); - imgBio.lbtn_up(); - seeker.lbtn_up(); + if (!bioSet.panelActive) { bio.panel.inactivate(); return; } + bio.alb_scrollbar.lbtn_drag_up(); + bio.art_scrollbar.lbtn_drag_up(); + bio.art_scroller.lbtn_drag_up(); + bio.cov_scroller.lbtn_drag_up(); + if (!bioSet.dblClickToggle && !bio.but.Dn && !bio.seeker.dn && !bio.panel.trace.film) bio.panel.click(x, y); + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().lbtn_up(); + bio.panel.clicked = false; + bio.resize.lbtn_up(); + bio.but.lbtn_up(x, y); + bio.filmStrip.lbtn_up(x, y); + bio.img.lbtn_up(); + bio.seeker.lbtn_up(); } on_mouse_leave() { - if (!pptBio.panelActive) return; - panelBio.leave(); - butBio.leave(); - alb_scrollbar.leave(); - art_scrollbar.leave(); - art_scroller.leave(); - cov_scroller.leave(); - txt.leave(); - imgBio.leave(); - filmStrip.leave(); - panelBio.m.y = -1; + if (!bioSet.panelActive) return; + bio.panel.leave(); + bio.but.leave(); + bio.alb_scrollbar.leave(); + bio.art_scrollbar.leave(); + bio.art_scroller.leave(); + bio.cov_scroller.leave(); + bio.txt.leave(); + bio.img.leave(); + bio.filmStrip.leave(); + bio.panel.m.y = -1; } on_mouse_mbtn_up(x, y, mask) { // UIHacks at default settings blocks on_mouse_mbtn_up, at least in windows; workaround configure hacks: main window > move with > caption only & ensure pseudo-caption doesn't overlap buttons switch (true) { case mask == 0x0004: - panelBio.inactivate(); + bio.panel.inactivate(); break; case utils.IsKeyPressed(0x12): - filmStrip.mbtn_up('onOff'); + bio.filmStrip.mbtn_up('onOff'); break; - case panelBio.trace.film && !butBio.trace('lookUp', x, y): - filmStrip.mbtn_up('showCurrent'); + case bio.panel.trace.film && !bio.but.trace('lookUp', x, y): + bio.filmStrip.mbtn_up('showCurrent'); break; - case pptBio.panelActive: - panelBio.mbtn_up(x, y); + case bioSet.panelActive: + bio.panel.mbtn_up(x, y); break; } } on_mouse_move(x, y) { - if (!pptBio.panelActive) return; - if (panelBio.m.x == x && panelBio.m.y == y) return; - panelBio.move(x, y); - butBio.move(x, y); - if (!txt.lyricsDisplayed()) txt.scrollbar_type().move(x, y); - filmStrip.scrollerType().move(x, y); - resize.imgMove(x, y); - resize.move(x, y); - resize.filmMove(x, y); - seeker.move(x, y); - txt.move(x, y); - imgBio.move(x, y); - filmStrip.move(x, y); - panelBio.m.x = x; - panelBio.m.y = y; + if (!bioSet.panelActive) return; + if (bio.panel.m.x == x && bio.panel.m.y == y) return; + bio.panel.move(x, y); + bio.but.move(x, y); + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().move(x, y); + bio.filmStrip.scrollerType().move(x, y); + bio.resize.imgMove(x, y); + bio.resize.move(x, y); + bio.resize.filmMove(x, y); + bio.seeker.move(x, y); + bio.txt.move(x, y); + bio.img.move(x, y); + bio.filmStrip.move(x, y); + bio.panel.m.x = x; + bio.panel.m.y = y; } on_mouse_rbtn_up(x, y) { - menBio.rbtn_up(x, y); + bio.men.rbtn_up(x, y); return true; } on_mouse_wheel(step) { - if (!pptBio.panelActive) return; - txt.deactivateTooltip(); - switch (panelBio.zoom()) { + if (!bioSet.panelActive) return; + bio.txt.deactivateTooltip(); + switch (bio.panel.zoom()) { case false: switch (true) { - case butBio.trace('lookUp', panelBio.m.x, panelBio.m.y): - menBio.wheel(step, true); + case bio.but.trace('lookUp', bio.panel.m.x, bio.panel.m.y): + bio.men.wheel(step, true); break; - case panelBio.trace.film: - filmStrip.scrollerType().wheel(step, false); + case bio.panel.trace.film: + bio.filmStrip.scrollerType().wheel(step, false); break; - case panelBio.trace.text: - if (!txt.lyricsDisplayed()) txt.scrollbar_type().wheel(step, false); - else if (panelBio.id.lyricsSource) lyricsBio.on_mouse_wheel(step); + case bio.panel.trace.text: + if (!bio.txt.lyricsDisplayed()) bio.txt.scrollbar_type().wheel(step, false); + else if (bio.panel.id.lyricsSource) bio.lyrics.on_mouse_wheel(step); break; default: - imgBio.wheel(step); + bio.img.wheel(step); break; } break; case true: - uiBio.wheel(step); - if (vkBio.k('ctrl')) butBio.wheel(step); - if (vkBio.k('shift')) { - imgBio.wheel(step); - if (butBio.trace('lookUp', panelBio.m.x, panelBio.m.y)) menBio.wheel(step, true); + bio.ui.wheel(step); + if (bio.vk.k('ctrl')) bio.but.wheel(step); + if (bio.vk.k('shift')) { + bio.img.wheel(step); + if (bio.but.trace('lookUp', bio.panel.m.x, bio.panel.m.y)) bio.men.wheel(step, true); } break; } @@ -290,13 +297,13 @@ class BiographyCallbacks { on_notify_data(name, info) { let clone; - if (uiBio.id.local && name.startsWith('opt_')) { + if (bio.ui.id.local && name.startsWith('opt_')) { clone = typeof info === 'string' ? String(info) : info; on_cui_notify(name, clone); } switch (name) { case 'bio_chkTrackRev': - if (!$Bio.server && pptBio.showTrackRevOptions) { + if (!$Bio.server && bioSet.showTrackRevOptions) { clone = JSON.parse(JSON.stringify(info)); clone.inclTrackRev = true; window.NotifyOthers('bio_isTrackRev', clone); @@ -305,303 +312,239 @@ class BiographyCallbacks { case 'bio_isTrackRev': if ($Bio.server && info.inclTrackRev == true) { clone = JSON.parse(JSON.stringify(info)); - serverBio.getTrack(clone); + bio.server.getTrack(clone); } break; case 'bio_imgChange': - imgBio.fresh(); - menBio.fresh(); + bio.img.fresh(); + bio.men.fresh(); break; case 'bio_checkImgArr': clone = JSON.parse(JSON.stringify(info)); - imgBio.checkArr(clone); + bio.img.checkArr(clone); break; case 'bio_checkNumServers': - window.NotifyOthers('bio_serverName', pptBio.serverName); + window.NotifyOthers('bio_serverName', bioSet.serverName); break; case 'bio_serverName': - if (info != pptBio.serverName) pptBio.multiServer = true; + if (info != bioSet.serverName) bioSet.multiServer = true; break; case 'bio_customStyle': clone = String(info); - panelBio.on_notify(clone); + bio.panel.on_notify(clone); break; case 'bio_forceUpdate': if ($Bio.server) { clone = JSON.parse(JSON.stringify(info)); - serverBio.download(1, clone[0], clone[1]); + bio.server.download(1, clone[0], clone[1]); } break; case 'bio_getLookUpList': - panelBio.getList('', true); + bio.panel.getList('', true); break; case 'bio_getRevImg': if ($Bio.server) { clone = JSON.parse(JSON.stringify(info)); - serverBio.getRevImg(clone[0], clone[1], clone[2], clone[3], false); + bio.server.getRevImg(clone[0], clone[1], clone[2], clone[3], false); } break; case 'bio_getImg': - imgBio.grab(!!info); + bio.img.grab(!!info); break; case 'bio_getText': - txt.grab(); + bio.txt.grab(); break; case 'bio_lookUpItem': if ($Bio.server) { clone = JSON.parse(JSON.stringify(info)); - serverBio.download(false, clone[0], clone[1], name); + bio.server.download(false, clone[0], clone[1], name); } break; - case `bio_newCfg${pptBio.serverName}`: - cfg.updateCfg($Bio.jsonParse(info, {})); + case `bio_newCfg${bioSet.serverName}`: + bioCfg.updateCfg($Bio.jsonParse(info, {})); break; - case `bio_notServer${pptBio.serverName}`: { + case `bio_notServer${bioSet.serverName}`: { const recTimestamp = info; - if (recTimestamp >= panelBio.notifyTimestamp) { + if (recTimestamp >= bio.panel.notifyTimestamp) { $Bio.server = false; - timerBio.clear(timerBio.img); - timerBio.clear(timerBio.zSearch); + bio.timer.clear(bio.timer.img); + bio.timer.clear(bio.timer.zSearch); } break; } case 'bio_blacklist': - imgBio.blackList.artist = ''; - imgBio.check(); + bio.img.blackList.artist = ''; + bio.img.check(); break; - case `bio_scriptUnload${pptBio.serverName}`: + case `bio_scriptUnload${bioSet.serverName}`: $Bio.server = true; - panelBio.notifyTimestamp = Date.now(); - window.NotifyOthers(`bio_notServer${pptBio.serverName}`, panelBio.notifyTimestamp); + bio.panel.notifyTimestamp = Date.now(); + window.NotifyOthers(`bio_notServer${bioSet.serverName}`, bio.panel.notifyTimestamp); break; case 'bio_refresh': window.Reload(); break; case 'bio_reload': - if (panelBio.stndItem()) window.Reload(); + if (bio.panel.stndItem()) window.Reload(); else { - txt.artistFlush(); - txt.albumFlush(); - txt.grab(); - if (pptBio.text_only) txt.paint(); + bio.txt.artistFlush(); + bio.txt.albumFlush(); + bio.txt.grab(); + if (bioSet.text_only) bio.txt.paint(); } break; case 'bio_followSelectedTrack': - if (!panelBio.id.lyricsSource && !panelBio.id.nowplayingSource && panelBio.id.focus !== info) { // if enabled, panel has to be in prefer nowplaying mode - panelBio.id.focus = pptBio.focus = info; - panelBio.changed(); - txt.on_playback_new_track(); - imgBio.on_playback_new_track(); + if (!bio.panel.id.lyricsSource && !bio.panel.id.nowplayingSource && bio.panel.id.focus !== info) { // if enabled, panel has to be in prefer nowplaying mode + bio.panel.id.focus = bioSet.focus = info; + bio.panel.changed(); + bio.txt.on_playback_new_track(); + bio.img.on_playback_new_track(); } break; case 'bio_status': - pptBio.panelActive = info; + bioSet.panelActive = info; window.Reload(); break; case 'bio_syncTags': if ($Bio.server) { - tagBio.force = true; - serverBio.call(info); + bio.tag.force = true; + bio.server.call(info); } break; case 'bio_webRequest': clone = String(info); - serverBio.urlRequested[clone] = Date.now(); // if multiServer enabled, limit URL requests for same item to one + bio.server.urlRequested[clone] = Date.now(); // if multiServer enabled, limit URL requests for same item to one break; case 'newThemeColours': - if (!pptBio.themed) break; - pptBio.theme = info.theme; - pptBio.themeBgImage = info.themeBgImage; - pptBio.themeColour = info.themeColour; + if (!bioSet.themed) break; + bioSet.theme = info.theme; + bioSet.themeBgImage = info.themeBgImage; + bioSet.themeColour = info.themeColour; on_colours_changed(); break; case 'Sync col': { - if (!pptBio.themed) break; - const themeLight = pptBio.themeLight; + if (!bioSet.themed) break; + const themeLight = bioSet.themeLight; if (themeLight != info.themeLight) { - pptBio.themeLight = info.themeLight; + bioSet.themeLight = info.themeLight; on_colours_changed(); } break; } case 'Sync image': - if (!pptBio.themed) break; - syncBio.img = { image: new GdiBitmap(info.image), id: info.id }; - if (!panelBio.block()) { - syncBio.image(syncBio.img.image, syncBio.img.id); - syncBio.get = false; - } else syncBio.get = true; + if (!bioSet.themed) break; + bioSync.img = { image: new GdiBitmap(info.image), id: info.id }; + if (!bio.panel.block()) { + bioSync.image(bioSync.img.image, bioSync.img.id); + bioSync.get = false; + } else bioSync.get = true; break; } } - // on_paint(gr) { - // if (uiBio.pss.checkOnSize) on_size(); - // uiBio.draw(gr); - // if (!pptBio.panelActive) { - // panelBio.draw(gr); - // return; - // } - // imgBio.draw(gr); - // seeker.draw(gr); - // txt.draw(gr); - // if (panelBio.id.lyricsSource) lyricsBio.draw(gr); - // filmStrip.draw(gr); - // butBio.draw(gr); - // resize.drawEd(gr); - // uiBio.lines(gr); - // } + on_paint(gr) { + if (bio.ui.pss.checkOnSize) on_size(); + bio.ui.draw(gr); + if (!bioSet.panelActive) { + bio.panel.draw(gr); + return; + } + bio.img.draw(gr); + bio.seeker.draw(gr); + bio.txt.draw(gr); + if (bio.panel.id.lyricsSource) bio.lyrics.draw(gr); + bio.filmStrip.draw(gr); + bio.but.draw(gr); + if (!grm.ui.displayCustomThemeMenu) bio.resize.drawEd(gr); + bio.ui.lines(gr); + } on_playback_dynamic_info_track() { - if (!pptBio.panelActive) return; - txt.rev.amFallback = true; - txt.rev.wikiFallback = true; - if ($Bio.server) serverBio.downloadDynamic(); - txt.reader.lyrics3Saved = false; - txt.reader.ESLyricSaved = false; - txt.reader.trackStartTime = fb.PlaybackTime; - txt.on_playback_new_track(); - imgBio.on_playback_new_track(); + if (!bioSet.panelActive) return; + bio.txt.rev.amFallback = true; + bio.txt.rev.wikiFallback = true; + if ($Bio.server) bio.server.downloadDynamic(); + bio.txt.reader.lyrics3Saved = false; + bio.txt.reader.ESLyricSaved = false; + bio.txt.reader.trackStartTime = fb.PlaybackTime; + bio.txt.on_playback_new_track(); + bio.img.on_playback_new_track(); } on_playback_new_track() { - if (!pptBio.panelActive) return; - if ($Bio.server) serverBio.call(); - if (pptBio.focus) return; - txt.rev.amFallback = true; - txt.rev.wikiFallback = true; - txt.reader.lyrics3Saved = false; - txt.reader.ESLyricSaved = false; - txt.reader.trackStartTime = 0; - txt.on_playback_new_track(); - imgBio.on_playback_new_track(); + if (!bioSet.panelActive) return; + if ($Bio.server) bio.server.call(); + if (bioSet.focus) return; + bio.txt.rev.amFallback = true; + bio.txt.rev.wikiFallback = true; + bio.txt.reader.lyrics3Saved = false; + bio.txt.reader.ESLyricSaved = false; + bio.txt.reader.trackStartTime = 0; + bio.txt.on_playback_new_track(); + bio.img.on_playback_new_track(); } on_playback_pause(state) { - if (panelBio.id.lyricsSource) lyricsBio.on_playback_pause(state); + if (bio.panel.id.lyricsSource) bio.lyrics.on_playback_pause(state); } on_playback_seek() { - if (panelBio.id.lyricsSource) lyricsBio.seek(); - if (panelBio.block()) return; - const n = pptBio.artistView ? 'bio' : 'rev'; - if ((txt[n].loaded.txt && txt.reader[n].nowplaying || pptBio.sourceAll) && txt.reader[n].perSec) { - txt.logScrollPos(); - txt.getText(); - txt.paint(); + if (bio.panel.id.lyricsSource) bio.lyrics.seek(); + if (bio.panel.block()) return; + const n = bioSet.artistView ? 'bio' : 'rev'; + if ((bio.txt[n].loaded.txt && bio.txt.reader[n].nowplaying || bioSet.sourceAll) && bio.txt.reader[n].perSec) { + bio.txt.logScrollPos(); + bio.txt.getText(); + bio.txt.paint(); } } on_playback_time() { - if (panelBio.block()) return; - const n = pptBio.artistView ? 'bio' : 'rev'; - if ((txt[n].loaded.txt && txt.reader[n].nowplaying || pptBio.sourceAll) && txt.reader[n].perSec) { - txt.logScrollPos(); - txt.getText('', '', 'playbackTime'); - txt.paint(); + if (bio.panel.block()) return; + const n = bioSet.artistView ? 'bio' : 'rev'; + if ((bio.txt[n].loaded.txt && bio.txt.reader[n].nowplaying || bioSet.sourceAll) && bio.txt.reader[n].perSec) { + bio.txt.logScrollPos(); + bio.txt.getText('', '', 'playbackTime'); + bio.txt.paint(); } } on_playback_stop(reason) { - if (!pptBio.panelActive) return; - const n = pptBio.artistView ? 'bio' : 'rev'; - if (reason != 2 && txt[n].loaded.txt && txt.reader[n].lyrics) txt.getText(); - if (panelBio.id.lyricsSource) lyricsBio.clear(); + if (!bioSet.panelActive) return; + const n = bioSet.artistView ? 'bio' : 'rev'; + if (reason != 2 && bio.txt[n].loaded.txt && bio.txt.reader[n].lyrics) bio.txt.getText(); + if (bio.panel.id.lyricsSource) bio.lyrics.clear(); if (reason == 2) return; on_item_focus_change(); } on_playlist_items_added() { - if (!pptBio.panelActive) return; + if (!bioSet.panelActive) return; on_item_focus_change(); } on_playlist_items_removed() { - if (!pptBio.panelActive) return; + if (!bioSet.panelActive) return; on_item_focus_change(); } on_playlist_switch() { - if (!pptBio.panelActive) return; + if (!bioSet.panelActive) return; on_item_focus_change(); } on_playlists_changed() { - if (!pptBio.panelActive) return; - menBio.playlists_changed(); + if (!bioSet.panelActive) return; + bio.men.playlists_changed(); } on_script_unload() { if ($Bio.server) { - window.NotifyOthers(`bio_scriptUnload${pptBio.serverName}`, 0); - timerBio.clear(timerBio.img); + window.NotifyOthers(`bio_scriptUnload${bioSet.serverName}`, 0); + bio.timer.clear(bio.timer.img); } - butBio.on_script_unload(); - txt.deactivateTooltip(); - } - - on_size() { - txt.repaint = false; - panelBio.w = window.Width; - panelBio.h = window.Height; - if (!window.IsVisible && uiBio.pss.installed && !pptBio.themed) { - uiBio.pss.checkOnSize = true; - return; - } - uiBio.pss.checkOnSize = false; - if (!panelBio.w || !panelBio.h) return; - txt.logScrollPos('bio'); - txt.logScrollPos('rev'); - uiBio.getParams(); - - if (!pptBio.panelActive) return; - txt.deactivateTooltip(); - panelBio.calcText = true; - txt.on_size(); - - if (pptBio.themed && (pptBio.theme || pptBio.themeBgImage)) { - const themed_image = pref.customLibraryDir ? `${globals.customLibraryDir}cache\\library\\themed\\themed_image.bmp` : `${fb.ProfilePath}cache\\library\\themed\\themed_image.bmp`; - if ($Bio.file(themed_image) && !panelBio.block()) syncBio.image(gdi.Image(themed_image)); - } - imgBio.on_size(); - filmStrip.on_size(); - txt.repaint = true; - imgBio.art.displayedOtherPanel = null; - - if (!pptBio.themed) return; - const windowMetrics = $Bio.jsonParse(windowMetricsPathBio, {}, 'file'); - windowMetrics[window.Name] = { - w: panelBio.w, - h: panelBio.h - } - $Bio.save(windowMetricsPathBio, JSON.stringify(windowMetrics, null, 3), true); - } -} - - -class BiographyPanel { - constructor() { - this.x = -1; // not set - this.y = -1; // not set - this.w = -1; // not set - this.h = -1; // not set - } - - on_paint(gr) { - if (uiBio.pss.checkOnSize) on_size(); - uiBio.draw(gr); - if (!pptBio.panelActive) { - panelBio.draw(gr); - return; - } - imgBio.draw(gr); - seeker.draw(gr); - txt.draw(gr); - if (panelBio.id.lyricsSource) lyricsBio.draw(gr); - filmStrip.draw(gr); - butBio.draw(gr); - if (!displayCustomThemeMenu) resize.drawEd(gr); - uiBio.lines(gr); + bio.but.on_script_unload(); + bio.txt.deactivateTooltip(); } on_size(x, y, width, height) { @@ -609,86 +552,51 @@ class BiographyPanel { this.y = y; this.w = width; this.h = height; - uiBio.x = x; - uiBio.y = y; - uiBio.w = width; - uiBio.h = height; + bio.ui.x = x; + bio.ui.y = y; + bio.ui.w = width; + bio.ui.h = height; // * Set guard for fixed Biography margin sizes in case user changed them in Biography options - pptBio.borT = SCALE(30); - pptBio.borL = SCALE(pref.layout === 'artwork' ? 30 : 40); - pptBio.borR = SCALE(pref.layout === 'artwork' ? 30 : 40); - pptBio.borB = SCALE(30); - pptBio.textT = pptBio.borT; - pptBio.textL = pptBio.borL; - pptBio.textR = pptBio.borR; - pptBio.textB = pptBio.borB; - pptBio.gap = SCALE(15); - - txt.repaint = false; - panelBio.w = width; - panelBio.h = height; - if (!window.IsVisible && uiBio.pss.installed) { - uiBio.pss.checkOnSize = true; + bioSet.borT = SCALE(30); + bioSet.borL = SCALE(grSet.layout === 'artwork' ? 30 : 40); + bioSet.borR = SCALE(grSet.layout === 'artwork' ? 30 : 40); + bioSet.borB = SCALE(30); + bioSet.textT = bioSet.borT; + bioSet.textL = bioSet.borL; + bioSet.textR = bioSet.borR; + bioSet.textB = bioSet.borB; + bioSet.gap = SCALE(15); + + bio.txt.repaint = false; + bio.panel.w = width; + bio.panel.h = height; + if (!window.IsVisible && bio.ui.pss.installed) { + bio.ui.pss.checkOnSize = true; return; } - uiBio.pss.checkOnSize = false; - if (!panelBio.w || !panelBio.h) return; - txt.logScrollPos('bio'); - txt.logScrollPos('rev'); - uiBio.getFont(); - if (!pptBio.panelActive) return; - txt.deactivateTooltip(); - panelBio.calcText = true; - txt.on_size(); - imgBio.on_size(); - filmStrip.on_size(); - txt.repaint = true; - imgBio.art.displayedOtherPanel = null; + bio.ui.pss.checkOnSize = false; + if (!bio.panel.w || !bio.panel.h) return; + bio.txt.logScrollPos('bio'); + bio.txt.logScrollPos('rev'); + bio.ui.getFont(); + if (!bioSet.panelActive) return; + bio.txt.deactivateTooltip(); + bio.panel.calcText = true; + bio.txt.on_size(); + bio.img.on_size(); + bio.filmStrip.on_size(); + bio.txt.repaint = true; + bio.img.art.displayedOtherPanel = null; + + if (!bioSet.themed) return; + const windowMetrics = $Bio.jsonParse(this.windowMetricsPathBio, {}, 'file'); + windowMetrics[window.Name] = { + w: bio.panel.w, + h: bio.panel.h + } + $Bio.save(this.windowMetricsPathBio, JSON.stringify(windowMetrics, null, 3), true); } } -//////////////////////// -// * INIT CALLBACKS * // -//////////////////////// -/** @type {BiographyCallbacks} */ -let biography = new BiographyCallbacks(); -/** @type {BiographyPanel} */ -let biographyPanel = new BiographyPanel(); - - -this.on_colours_changed = () => biography.on_colours_changed(); -this.on_font_changed = () => biography.on_font_changed(); -this.on_focus = (is_focused) => biography.on_focus(is_focused); -this.on_get_album_art_done = (handle, art_id, image, image_path) => biography.on_get_album_art_done(handle, art_id, image, image_path); -this.on_item_focus_change = () => biography.on_item_focus_change(); -this.on_key_down = (vkey) => biography.on_key_down(vkey); -this.on_key_up = (vkey) => biography.on_key_up(vkey); -this.on_library_items_added = () => biography.on_library_items_added(); -this.on_library_items_removed = () => biography.on_library_items_removed(); -this.on_library_items_changed = () => biography.on_library_items_changed(); -this.on_load_image_done = (task_id, image, image_path) => biography.on_load_image_done(task_id, image, image_path); -this.on_metadb_changed = () => biography.on_metadb_changed(); -this.on_mouse_lbtn_dblclk = (x, y) => biography.on_mouse_lbtn_dblclk(x, y); -this.on_mouse_lbtn_down = (x, y) => biography.on_mouse_lbtn_down(x, y); -this.on_mouse_lbtn_up = (x, y) => biography.on_mouse_lbtn_up(x, y); -this.on_mouse_leave = () => biography.on_mouse_leave(); -this.on_mouse_mbtn_up = (x, y, mask) => biography.on_mouse_mbtn_up(x, y, mask); -this.on_mouse_move = (x, y) => biography.on_mouse_move(x, y); -this.on_mouse_rbtn_up = (x, y) => biography.on_mouse_rbtn_up(x, y); -this.on_mouse_wheel = (step) => biography.on_mouse_wheel(step); -this.on_notify_data = (name, info) => biography.on_notify_data(name, info); -this.on_playback_dynamic_info_track = () => biography.on_playback_dynamic_info_track(); -this.on_playback_new_track = () => biography.on_playback_new_track(); -this.on_playback_pause = (state) => biography.on_playback_pause(state); -this.on_playback_seek = () => biography.on_playback_seek(); -this.on_playback_time = () => biography.on_playback_time(); -this.on_playback_stop = (reason) => biography.on_playback_stop(reason); -this.on_playlist_items_added = () => biography.on_playlist_items_added(); -this.on_playlist_items_removed = () => biography.on_playlist_items_removed(); -this.on_playlist_switch = () => biography.on_playlist_switch(); -this.on_playlists_changed = () => biography.on_playlists_changed(); -this.on_script_unload = () => biography.on_script_unload(); - - -const windowMetricsPathBio = pref.customBiographyDir ? `${globals.customBiographyDir}cache\\biography\\themed\\windowMetrics.json` : `${fb.ProfilePath}cache\\biography\\themed\\windowMetrics.json`; +bio.call = new BioCallbacks(); diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-filmstrip.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-filmstrip.js index 22673bcc..fc162cc0 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-filmstrip.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-filmstrip.js @@ -1,6 +1,6 @@ 'use strict'; -class FilmStrip { +class BioFilmStrip { constructor() { this.accessed = 0; this.blockSize = 80; @@ -18,8 +18,8 @@ class FilmStrip { this.y = 0; this.w = 0; - pptBio.thumbNailGap = Math.max(pptBio.thumbNailGap, 0); - pptBio.filmStripSize = $Bio.clamp(parseFloat(pptBio.filmStripSize.toFixed(15)), 0.02, 0.98); + bioSet.thumbNailGap = Math.max(bioSet.thumbNailGap, 0); + bioSet.filmStripSize = $Bio.clamp(parseFloat(bioSet.filmStripSize.toFixed(15)), 0.02, 0.98); this.blocks = { drawn: 6, @@ -47,8 +47,8 @@ class FilmStrip { this.repaint = { x: 0, y: 0, - w: panelBio.w, - h: panelBio.h + w: bio.panel.w, + h: bio.panel.h }; this.scroll = { @@ -63,14 +63,14 @@ class FilmStrip { fit: true, gap: 0, horizontal: false, - image: [pptBio.filmCoverStyle, pptBio.filmPhotoStyle] + image: [bioSet.filmCoverStyle, bioSet.filmPhotoStyle] }; this.imageDebounce = $Bio.debounce(() => { this.rate = // ! To always load filmStrip images successfully, debounce refresh rate must be lower than seekbar's refresh rate - pref.seekbar === 'progressbar' ? pref.progressBarRefreshRate : - pref.seekbar === 'peakmeterbar' ? pref.peakmeterBarRefreshRate : - pref.seekbar === 'waveformbar' ? pref.waveformBarRefreshRate : ''; + grSet.seekbar === 'progressbar' ? grSet.progressBarRefreshRate : + grSet.seekbar === 'peakmeterbar' ? grSet.peakmeterBarRefreshRate : + grSet.seekbar === 'waveformbar' ? grSet.waveformBarRefreshRate : ''; this.getImages(); }, Math.round(this.rate * 0.5), { leading: true, @@ -78,12 +78,12 @@ class FilmStrip { }); this.sizeDebounce = $Bio.debounce(() => { - if (!pptBio.showFilmStrip) return; + if (!bioSet.showFilmStrip) return; this.logScrollPos(); this.clearCache(); this.setSize(); this.check(); - txt.refresh(0); + bio.txt.refresh(0); }, 100); } @@ -91,7 +91,7 @@ class FilmStrip { async load_image_async(image_path) { const image = await gdi.LoadImageAsyncV2(0, image_path); - if (!panelBio.style.showFilmStrip) return; + if (!bio.panel.style.showFilmStrip) return; const key = this.getLoadKey(image_path); const o = this.cache[key]; if (o && o.img == 'called') this.cacheIt(image, key, o.style); @@ -100,34 +100,34 @@ class FilmStrip { cacheIt(image, key, style) { try { if (image) { - if (imgBio.filter.size && pptBio.artistView && (!pptBio.imgFilterBothPx ? image.Width < imgBio.filter.minPx && image.Height < imgBio.filter.minPx : image.Width < imgBio.filter.minPx || image.Height < imgBio.filter.minPx) && imgBio.art.images.length > imgBio.filter.minNo) { + if (bio.img.filter.size && bioSet.artistView && (!bioSet.imgFilterBothPx ? image.Width < bio.img.filter.minPx && image.Height < bio.img.filter.minPx : image.Width < bio.img.filter.minPx || image.Height < bio.img.filter.minPx) && bio.img.art.images.length > bio.img.filter.minNo) { const image_path = key.replace(/^\d+/, ''); - const rem_ix = imgBio.art.images.findIndex(v => v == image_path); - if (rem_ix != -1) imgBio.art.images.splice(rem_ix, 1); + const rem_ix = bio.img.art.images.findIndex(v => v == image_path); + if (rem_ix != -1) bio.img.art.images.splice(rem_ix, 1); this.trimCache(image_path); - seeker.upd(); + bio.seeker.upd(); this.logScrollPos(); this.check('imgUpd'); - !pptBio.imgSeeker ? this.paint() : txt.paint(); + !bioSet.imgSeeker ? this.paint() : bio.txt.paint(); return; } - if (imgBio.filter.size && !pptBio.artistView && imgBio.artFolder && (!pptBio.imgFilterBothPx ? image.Width < imgBio.filter.minPx && image.Height < imgBio.filter.minPx : image.Width < imgBio.filter.minPx || image.Height < imgBio.filter.minPx) && imgBio.cov.images.length > imgBio.filter.minNo + 1) { + if (bio.img.filter.size && !bioSet.artistView && bio.img.artFolder && (!bioSet.imgFilterBothPx ? image.Width < bio.img.filter.minPx && image.Height < bio.img.filter.minPx : image.Width < bio.img.filter.minPx || image.Height < bio.img.filter.minPx) && bio.img.cov.images.length > bio.img.filter.minNo + 1) { const image_path = key.replace(/^\d+/, ''); - const rem_ix = imgBio.cov.images.findIndex(v => v == image_path); + const rem_ix = bio.img.cov.images.findIndex(v => v == image_path); if (rem_ix != -1) { - imgBio.cov.list.splice(rem_ix, 1); - imgBio.cov.images.splice(rem_ix, 1); + bio.img.cov.list.splice(rem_ix, 1); + bio.img.cov.images.splice(rem_ix, 1); } this.trimCache(image_path); - seeker.upd(); + bio.seeker.upd(); this.logScrollPos(); this.check('imgUpd'); - !pptBio.imgSeeker ? this.paint() : txt.paint(); + !bioSet.imgSeeker ? this.paint() : bio.txt.paint(); return; } - } else if (!image) image = imgBio.stub.default[!key.includes('noitem') ? pptBio.artistView ? 1 : 0 : 2]; + } else if (!image) image = bio.img.stub.default[!key.includes('noitem') ? bioSet.artistView ? 1 : 0 : 2]; if (image) { - image = imgBio.format(image, 1, ['default', 'crop', 'circular'][style], this.im_w, this.im_w, 'filmStrip'); + image = bio.img.format(image, 1, ['default', 'crop', 'circular'][style], this.im_w, this.im_w, 'filmStrip'); this.checkCache(); this.cache[key] = { img: image, @@ -137,65 +137,65 @@ class FilmStrip { } } catch (e) { const image_path = key.replace(/^\d+/, ''); - if (pptBio.artistView) { - const rem_ix = imgBio.art.images.findIndex(v => v == image_path); - if (rem_ix != -1) imgBio.art.images.splice(rem_ix, 1); + if (bioSet.artistView) { + const rem_ix = bio.img.art.images.findIndex(v => v == image_path); + if (rem_ix != -1) bio.img.art.images.splice(rem_ix, 1); this.trimCache(image_path); } else { - const rem_ix = imgBio.cov.images.findIndex(v => v == image_path); + const rem_ix = bio.img.cov.images.findIndex(v => v == image_path); if (rem_ix != -1) { - imgBio.cov.list.splice(rem_ix, 1); - imgBio.cov.images.splice(rem_ix, 1); + bio.img.cov.list.splice(rem_ix, 1); + bio.img.cov.images.splice(rem_ix, 1); } this.trimCache(image_path); } - seeker.upd(); + bio.seeker.upd(); this.check(); - if (pptBio.imgSeeker) txt.paint(); + if (bioSet.imgSeeker) bio.txt.paint(); $Bio.trace(`unable to load thumbnail image: ${key}`); } this.paint(); } check(n) { - const y_text = !panelBio.style.fullWidthHeading || pptBio.img_only ? 0 : panelBio.text.t; + const y_text = !bio.panel.style.fullWidthHeading || bioSet.img_only ? 0 : bio.panel.text.t; if (this.text_y != y_text) this.setSize(); - panelBio.style.showFilmStrip = false; - if (pptBio.showFilmStrip) { + bio.panel.style.showFilmStrip = false; + if (bioSet.showFilmStrip) { this.images = []; this.items = []; switch (true) { - case pptBio.style == 4 && pptBio.filmStripOverlay: + case bioSet.style == 4 && bioSet.filmStripOverlay: break; case !this.style.auto: - panelBio.style.showFilmStrip = true; + bio.panel.style.showFilmStrip = true; switch (true) { - case pptBio.artistView: - if (!pptBio.cycPhoto || !imgBio.art.images.length) break; - this.images = imgBio.art.images; + case bioSet.artistView: + if (!bioSet.cycPhoto || !bio.img.art.images.length) break; + this.images = bio.img.art.images; break; - case !pptBio.artistView: - if (!panelBio.stnd(panelBio.alb.ix, panelBio.alb.list) || !imgBio.cov.cycle || !imgBio.cov.images.length) break; - this.images = imgBio.cov.images; + case !bioSet.artistView: + if (!bio.panel.stnd(bio.panel.alb.ix, bio.panel.alb.list) || !bio.img.cov.cycle || !bio.img.cov.images.length) break; + this.images = bio.img.cov.images; break; } break; case this.style.auto: switch (true) { - case pptBio.artistView: - if (!pptBio.cycPhoto || imgBio.art.images.length < 2) break; - this.images = imgBio.art.images; - panelBio.style.showFilmStrip = true; + case bioSet.artistView: + if (!bioSet.cycPhoto || bio.img.art.images.length < 2) break; + this.images = bio.img.art.images; + bio.panel.style.showFilmStrip = true; break; - case !pptBio.artistView: - if (!panelBio.stnd(panelBio.alb.ix, panelBio.alb.list) || !imgBio.cov.cycle || imgBio.cov.images.length < 2) break; - this.images = imgBio.cov.images; - panelBio.style.showFilmStrip = true; + case !bioSet.artistView: + if (!bio.panel.stnd(bio.panel.alb.ix, bio.panel.alb.list) || !bio.img.cov.cycle || bio.img.cov.images.length < 2) break; + this.images = bio.img.cov.images; + bio.panel.style.showFilmStrip = true; break; } break; } - if (!this.images.length && !this.style.auto) this.images[0] = imgBio.cur_pth(); + if (!this.images.length && !this.style.auto) this.images[0] = bio.img.cur_pth(); this.blocks.length = this.images.length; this.updScroll(n); if (n == 'imgUpd') this.scrollerType().checkScroll(this.scrollerType().scroll, 'step'); @@ -204,15 +204,15 @@ class FilmStrip { if (id.id != this.cur.id) { this.cur.id = id.id; if (n != 'clear') { - txt.logScrollPos(); + bio.txt.logScrollPos(); this.setSize(); // check required for initially hidden panels - txt.albumFlush(); // handle track change no filmStrip to needed - txt.artistFlush(); - txt.rev.cur = ''; - txt.bio.cur = ''; + bio.txt.albumFlush(); // handle track change no filmStrip to needed + bio.txt.artistFlush(); + bio.txt.rev.cur = ''; + bio.txt.bio.cur = ''; this.setFilmStripSize(); // check required for initially hidden panels - panelBio.setStyle(resize.down); // if clear: called by refresh(0) - panelBio.checkFilm(); // if clear: refresh(0) does text + bio.panel.setStyle(bio.resize.down); // if clear: called by refresh(0) + bio.panel.checkFilm(); // if clear: refresh(0) does text } } else if (id.borId != this.cur.borId) { this.cur.borId = id.borId; @@ -227,8 +227,8 @@ class FilmStrip { checkCache() { let keys = Object.keys(this.cache); const cacheLength = keys.length; - if (cacheLength < this.cachesize.max && !imgBio.memoryLimit()) return; - this.cache = imgBio.sortCache(this.cache, 'accessed'); + if (cacheLength < this.cachesize.max && !bio.img.memoryLimit()) return; + this.cache = bio.img.sortCache(this.cache, 'accessed'); keys = Object.keys(this.cache); const numToRemove = Math.round((Math.min(this.cachesize.max, cacheLength) - this.cachesize.min) / 5); if (numToRemove > 0) { @@ -242,12 +242,12 @@ class FilmStrip { } createBorder() { - if (!pptBio.showFilmStrip || this.blockSize < 2 || isNaN(this.blockSize)) return; + if (!bioSet.showFilmStrip || this.blockSize < 2 || isNaN(this.blockSize)) return; const sp = Math.round((this.blockSize - this.im_w) / 2); for (let j = 0; j < 3; j++) { for (let i = 0; i < 3; i++) { - const col = i < 2 ? uiBio.col.imgBor : uiBio.col.frame; - const w = i < 2 ? uiBio.style.l_w : uiBio.style.l_w * 3; + const col = i < 2 ? bio.ui.col.imgBor : bio.ui.col.frame; + const w = i < 2 ? bio.ui.style.l_w : bio.ui.style.l_w * 3; const floor = Math.floor(w / 2); const w1 = !this.style.horizontal || i == 2 ? w : i == 1 ? -100 : w; const w2 = this.style.horizontal || i == 2 ? w : i == 1 ? -100 : w; @@ -265,8 +265,8 @@ class FilmStrip { } draw(gr) { - if (!panelBio.style.showFilmStrip || panelBio.block()) return; - const imgStyle = this.style.image[Number(pptBio.artistView)]; + if (!bio.panel.style.showFilmStrip || bio.panel.block()) return; + const imgStyle = this.style.image[Number(bioSet.artistView)]; let box_x; let box_y; let iw; @@ -279,7 +279,7 @@ class FilmStrip { if (im) { iw = im.Width; ih = im.Height; - const sel = (!pptBio.text_only || uiBio.style.isBlur) && this.blocks.length > 1 && this.images[i] == imgBio.cur_pth(); + const sel = (!bioSet.text_only || bio.ui.style.isBlur) && this.blocks.length > 1 && this.images[i] == bio.img.cur_pth(); const x = box_x + Math.round((this.blockSize - iw) / 2); const y = box_y + Math.round((this.blockSize - ih) / 2); let bor = !sel ? this.blocks.style[imgStyle].bor[1] : this.blocks.style[imgStyle].bor[2]; @@ -328,9 +328,9 @@ class FilmStrip { } get_ix(x, y) { - if (!pptBio.showFilmStrip) return -1; - if (!this.images.length || butBio.trace('lookUp', panelBio.m.x, panelBio.m.y)) return -1; - if (panelBio.trace.film && y > this.y && y < this.y + this.h && x > this.x && x < this.x + this.w) { + if (!bioSet.showFilmStrip) return -1; + if (!this.images.length || bio.but.trace('lookUp', bio.panel.m.x, bio.panel.m.y)) return -1; + if (bio.panel.trace.film && y > this.y && y < this.y + this.h && x > this.x && x < this.x + this.w) { const idx = this.style.horizontal ? Math.ceil((x + this.scrollerType().delta - this.x) / this.blockSize) - 1 : Math.ceil((y + this.scrollerType().delta - this.y) / this.blockSize) - 1; return idx > this.images.length - 1 ? -1 : idx; } @@ -364,16 +364,16 @@ class FilmStrip { else if (window.ID) { const key = this.getKey(v.key); if (!this.cache[key]) { - const embeddedImg = imgBio.isEmbedded('thumb', v.ix); + const embeddedImg = bio.img.isEmbedded('thumb', v.ix); if (!embeddedImg) { this.cache[key] = { img: 'called', - style: this.style.image[Number(pptBio.artistView)], + style: this.style.image[Number(bioSet.artistView)], accessed: ++this.accessed }; gdi.LoadImageAsync(0, v.key); } else { - this.cacheIt(embeddedImg, key, this.style.image[Number(pptBio.artistView)]); + this.cacheIt(embeddedImg, key, this.style.image[Number(bioSet.artistView)]); } } this.items.shift(); @@ -399,7 +399,7 @@ class FilmStrip { } getKey(pth) { - return (this.im_w * 100 + this.style.image[Number(pptBio.artistView)] + pth); + return (this.im_w * 100 + this.style.image[Number(bioSet.artistView)] + pth); } getLoadKey(pth) { @@ -414,82 +414,82 @@ class FilmStrip { getScrollPos() { let v; - switch (pptBio.artistView) { + switch (bioSet.artistView) { case true: - v = imgBio.artist; - if (!this.scroll.pos.art[v]) return art_scroller.setScroll(0); + v = bio.img.artist; + if (!this.scroll.pos.art[v]) return bio.art_scroller.setScroll(0); else if (this.scroll.pos.art[v].blockSize == this.blockSize) { - if (imgBio.art.list.length && $Bio.equal(this.scroll.pos.art[v].images, imgBio.art.list)) art_scroller.setScroll(this.scroll.pos.art[v].scroll || 0); - else art_scroller.setScroll(0); + if (bio.img.art.list.length && $Bio.equal(this.scroll.pos.art[v].images, bio.img.art.list)) bio.art_scroller.setScroll(this.scroll.pos.art[v].scroll || 0); + else bio.art_scroller.setScroll(0); } else { - if (imgBio.art.list.length && $Bio.equal(this.scroll.pos.art[v].images, imgBio.art.list)) art_scroller.setScroll(Math.round(this.scroll.pos.art[v].scroll / this.scroll.pos.art[v].blockSize) * this.blockSize || 0); - else art_scroller.setScroll(0); + if (bio.img.art.list.length && $Bio.equal(this.scroll.pos.art[v].images, bio.img.art.list)) bio.art_scroller.setScroll(Math.round(this.scroll.pos.art[v].scroll / this.scroll.pos.art[v].blockSize) * this.blockSize || 0); + else bio.art_scroller.setScroll(0); this.logScrollPos(); } break; case false: { - if (!panelBio.stnd(panelBio.alb.ix, panelBio.alb.list)) return cov_scroller.setScroll(0); - v = imgBio.id.albCyc; - if (!this.scroll.pos.cov[v]) return cov_scroller.setScroll(0); - else if (this.scroll.pos.cov[v].blockSize == this.blockSize) cov_scroller.setScroll(this.scroll.pos.cov[v].scroll || 0); - else cov_scroller.setScroll(Math.round(this.scroll.pos.cov[v].scroll / this.scroll.pos.cov[v].blockSize) * this.blockSize || 0); + if (!bio.panel.stnd(bio.panel.alb.ix, bio.panel.alb.list)) return bio.cov_scroller.setScroll(0); + v = bio.img.id.albCyc; + if (!this.scroll.pos.cov[v]) return bio.cov_scroller.setScroll(0); + else if (this.scroll.pos.cov[v].blockSize == this.blockSize) bio.cov_scroller.setScroll(this.scroll.pos.cov[v].scroll || 0); + else bio.cov_scroller.setScroll(Math.round(this.scroll.pos.cov[v].scroll / this.scroll.pos.cov[v].blockSize) * this.blockSize || 0); break; } } } id() { - const needExtraId = pptBio.showFilmStrip && pptBio.filmStripOverlay && imgBio.isType('AnyBor'); - const id = panelBio.style.showFilmStrip + panelBio.filmStripSize.t + panelBio.filmStripSize.r + panelBio.filmStripSize.b + panelBio.filmStripSize.l + pptBio.filmStripPos; + const needExtraId = bioSet.showFilmStrip && bioSet.filmStripOverlay && bio.img.isType('AnyBor'); + const id = bio.panel.style.showFilmStrip + bio.panel.filmStripSize.t + bio.panel.filmStripSize.r + bio.panel.filmStripSize.b + bio.panel.filmStripSize.l + bioSet.filmStripPos; return { id: isNaN(id) ? Infinity : id, // stop NaN != NaN = true & too much recursion - borId: needExtraId ? imgBio.style.crop + imgBio.isType('Border') : 0 + borId: needExtraId ? bio.img.style.crop + bio.img.isType('Border') : 0 }; } lbtn_dblclk(p_x, p_y) { const new_ix = this.get_ix(p_x, p_y); - if (pptBio.artistView) { - if (new_ix != -1 && new_ix != imgBio.art.ix) { - imgBio.art.ix = new_ix; - imgBio.setPhoto(); + if (bioSet.artistView) { + if (new_ix != -1 && new_ix != bio.img.art.ix) { + bio.img.art.ix = new_ix; + bio.img.setPhoto(); } - } else if (new_ix != -1 && new_ix != imgBio.cov.ix) { - imgBio.cov.ix = new_ix; - imgBio.setCov(); + } else if (new_ix != -1 && new_ix != bio.img.cov.ix) { + bio.img.cov.ix = new_ix; + bio.img.setCov(); } } lbtn_up(p_x, p_y) { - if (!pptBio.dblClickToggle) { + if (!bioSet.dblClickToggle) { const new_ix = this.get_ix(p_x, p_y); - if (pptBio.artistView) { - if (new_ix != -1 && new_ix != imgBio.art.ix && (!pptBio.touchControl || uiBio.id.touch_dn == new_ix)) { - imgBio.art.ix = new_ix; - imgBio.setPhoto(); + if (bioSet.artistView) { + if (new_ix != -1 && new_ix != bio.img.art.ix && (!bioSet.touchControl || bio.ui.id.touch_dn == new_ix)) { + bio.img.art.ix = new_ix; + bio.img.setPhoto(); } - } else if (new_ix != -1 && new_ix != imgBio.cov.ix && (!pptBio.touchControl || uiBio.id.touch_dn == new_ix)) { - imgBio.cov.ix = new_ix; - imgBio.setCov(); + } else if (new_ix != -1 && new_ix != bio.img.cov.ix && (!bioSet.touchControl || bio.ui.id.touch_dn == new_ix)) { + bio.img.cov.ix = new_ix; + bio.img.setCov(); } } } leave() { - if (menBio.right_up) return; + if (bio.men.right_up) return; this.m_i = -1; this.paint(); } logScrollPos(images) { - if (!pptBio.showFilmStrip) return; + if (!bioSet.showFilmStrip) return; let keys = []; let v; - switch (pptBio.artistView) { + switch (bioSet.artistView) { case true: { keys = Object.keys(this.scroll.pos.art); if (keys.length > 25) delete this.scroll.pos.art[keys[0]]; - v = imgBio.artist; + v = bio.img.artist; if (!this.scroll.pos.art[v]) { this.scroll.pos.art[v] = { images: [] @@ -498,19 +498,19 @@ class FilmStrip { const o = this.scroll.pos.art[v]; if (images) o.images = images; else { - o.arr = $Bio.isArray(imgBio.art.images) ? imgBio.art.images.slice() : []; + o.arr = $Bio.isArray(bio.img.art.images) ? bio.img.art.images.slice() : []; o.blockSize = this.blockSize; - o.ix = imgBio.art.ix; - o.scroll = art_scroller.scroll; + o.ix = bio.img.art.ix; + o.scroll = bio.art_scroller.scroll; } break; } case false: - if (!panelBio.stnd(panelBio.alb.ix, panelBio.alb.list)) break; - v = imgBio.id.albCyc; + if (!bio.panel.stnd(bio.panel.alb.ix, bio.panel.alb.list)) break; + v = bio.img.id.albCyc; this.scroll.pos.cov[v] = { blockSize: this.blockSize, - scroll: cov_scroller.scroll + scroll: bio.cov_scroller.scroll }; break; } @@ -519,33 +519,33 @@ class FilmStrip { mbtn_up(n) { switch (n) { case 'onOff': - pptBio.toggle('showFilmStrip'); + bioSet.toggle('showFilmStrip'); this.set('clear'); this.paint(); break; case 'showCurrent': - if (this.blocks.length > this.blocks.drawn && (!pptBio.text_only || uiBio.style.isBlur)) { - this.showImage(pptBio.artistView ? imgBio.art.ix : imgBio.cov.ix); + if (this.blocks.length > this.blocks.drawn && (!bioSet.text_only || bio.ui.style.isBlur)) { + this.showImage(bioSet.artistView ? bio.img.art.ix : bio.img.cov.ix); } break; } } move(x, y) { - if (!pptBio.showFilmStrip || pptBio.text_only && !uiBio.style.isBlur) return this.hand = false; + if (!bioSet.showFilmStrip || bioSet.text_only && !bio.ui.style.isBlur) return this.hand = false; this.m_i = this.get_ix(x, y); this.hand = this.m_i != -1; } noIm(gr, box_x, box_y, imgStyle) { if ((!this.style.horizontal && box_y >= this.y && box_y <= this.y + this.h - this.blockSize) || (this.style.horizontal && box_x >= this.x && box_x <= this.x + this.w - this.blockSize)) { - if (imgStyle != 2) gr.FillSolidRect(box_x + this.noimg.xy, box_y + this.noimg.xy, this.noimg.wh, this.noimg.wh, uiBio.col.bg1); - else gr.FillEllipse(box_x + this.noimg.xy, box_y + this.noimg.xy, this.noimg.wh, this.noimg.wh, uiBio.col.bg1); + if (imgStyle != 2) gr.FillSolidRect(box_x + this.noimg.xy, box_y + this.noimg.xy, this.noimg.wh, this.noimg.wh, bio.ui.col.bg1); + else gr.FillEllipse(box_x + this.noimg.xy, box_y + this.noimg.xy, this.noimg.wh, this.noimg.wh, bio.ui.col.bg1); } } on_load_image_done(image, image_path) { - if (!panelBio.style.showFilmStrip) return; + if (!bio.panel.style.showFilmStrip) return; const key = this.getLoadKey(image_path); const o = this.cache[key]; if (o && o.img == 'called') { @@ -565,25 +565,25 @@ class FilmStrip { } paint() { - if (!panelBio.style.showFilmStrip) return; + if (!bio.panel.style.showFilmStrip) return; window.RepaintRect(this.repaint.x, this.repaint.y, this.repaint.w, this.repaint.h); } scrollerType() { - return pptBio.artistView ? art_scroller : cov_scroller; + return bioSet.artistView ? bio.art_scroller : bio.cov_scroller; } setFilmStripSize() { - panelBio.filmStripSize = !panelBio.style.showFilmStrip ? { + bio.panel.filmStripSize = !bio.panel.style.showFilmStrip ? { t: 0, r: 0, b: 0, l: 0 } : { - t: pptBio.filmStripPos == 0 ? this.y + this.h - SCALE(55) : 0, - r: pptBio.filmStripPos == 1 ? panelBio.w - this.x : 0, - b: pptBio.filmStripPos == 2 ? panelBio.h - this.y + SCALE(25) : 0, - l: pptBio.filmStripPos == 3 ? this.x + this.w : 0 + t: bioSet.filmStripPos == 0 ? this.y + this.h - SCALE(55) : 0, + r: bioSet.filmStripPos == 1 ? bio.panel.w - this.x : 0, + b: bioSet.filmStripPos == 2 ? bio.panel.h - this.y + SCALE(25) : 0, + l: bioSet.filmStripPos == 3 ? this.x + this.w : 0 }; } @@ -593,69 +593,69 @@ class FilmStrip { case 1: case 2: case 3: - pptBio.showFilmStrip = true; - pptBio.filmStripPos = i; + bioSet.showFilmStrip = true; + bioSet.filmStripPos = i; break; case 5: { const continue_confirmation = (status, confirmed) => { - if (confirmed) pptBio.filmStripSize = pptBio.filmStripOverlay && !pptBio.text_only && pptBio.style !== 4 ? 0.09 : 0.05; + if (confirmed) bioSet.filmStripSize = bioSet.filmStripOverlay && !bioSet.text_only && bioSet.style !== 4 ? 0.09 : 0.05; }; const caption = 'Reset Filmstrip To Default Size'; const prompt = 'Continue?'; - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'Yes', 'No', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'Yes', 'No', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); break; } } - pptBio.filmStripSize = pptBio.filmStripOverlay && !pptBio.text_only && pptBio.style !== 4 ? 0.09 : 0.05; - filmStrip.logScrollPos(); - imgBio.mask.reset = true; + bioSet.filmStripSize = bioSet.filmStripOverlay && !bioSet.text_only && bioSet.style !== 4 ? 0.09 : 0.05; + bio.filmStrip.logScrollPos(); + bio.img.mask.reset = true; this.clearCache(); this.setSize(); this.check(i); - txt.refresh(0); + bio.txt.refresh(0); this.paint(); } setSize() { - if (!pptBio.showFilmStrip) return; - this.style.auto = pptBio.autoFilm; - this.style.fit = pptBio.filmStripAutofit; - this.style.gap = pptBio.thumbNailGap; - const filmStripOverlay = pptBio.filmStripOverlay && !pptBio.text_only; + if (!bioSet.showFilmStrip) return; + this.style.auto = bioSet.autoFilm; + this.style.fit = bioSet.filmStripAutofit; + this.style.gap = bioSet.thumbNailGap; + const filmStripOverlay = bioSet.filmStripOverlay && !bioSet.text_only; const spacer = Math.round((!this.style.gap ? 2 : this.style.gap < 3 ? 1 : 0) * $Bio.scale); let bor = 0; - if (filmStripOverlay && imgBio.isType('AnyBor') && imgBio.style.crop) { - const isBorder = imgBio.isType('Border'); + if (filmStripOverlay && bio.img.isType('AnyBor') && bio.img.style.crop) { + const isBorder = bio.img.isType('Border'); if (isBorder == 1 || isBorder == 3) { bor = 5 * $Bio.scale; } } - const marginT = uiBio.heading.linePad * (RES_4K ? 1.5 : 0.5); - const marginTopCorr = pref.layout === 'artwork' ? RES_4K ? -12 : -4 : RES_4K ? -7 : -1; - const marginTopCorr2 = pref.layout === 'artwork' ? RES_4K ? -7 : -4 : RES_4K ? -2 : -1; - const filmStripLeftRight = pptBio.filmStripPos === 1 || pptBio.filmStripPos === 3; + const marginT = bio.ui.heading.linePad * (RES._4K ? 1.5 : 0.5); + const marginTopCorr = grSet.layout === 'artwork' ? RES._4K ? -12 : -4 : RES._4K ? -7 : -1; + const marginTopCorr2 = grSet.layout === 'artwork' ? RES._4K ? -7 : -4 : RES._4K ? -2 : -1; + const filmStripLeftRight = bioSet.filmStripPos === 1 || bioSet.filmStripPos === 3; const filmStripCorrY = - pptBio.img_only ? filmStripLeftRight && !filmStripOverlay ? uiBio.y + pptBio.borT : pptBio.filmStripPos === 2 && !filmStripOverlay ? 0 : uiBio.y : - filmStripLeftRight && !pptBio.heading ? uiBio.y + pptBio.borT : - pptBio.showFilmStrip && pptBio.filmStripPos === 2 && !filmStripOverlay ? marginTopCorr : - pptBio.style === 4 && pptBio.filmStripPos === 0 && filmStripOverlay ? uiBio.y - pptBio.gap - marginTopCorr2 : - pptBio.filmStripPos !== 0 && !filmStripOverlay ? 0 : - uiBio.y; - - let max_h = panelBio.h; - let max_w = panelBio.w; - this.max_sz = panelBio.w; - this.text_y = !panelBio.style.fullWidthHeading || pptBio.img_only ? 0 : panelBio.text.t; - switch (pptBio.filmStripPos) { + bioSet.img_only ? filmStripLeftRight && !filmStripOverlay ? bio.ui.y + bioSet.borT : bioSet.filmStripPos === 2 && !filmStripOverlay ? 0 : bio.ui.y : + filmStripLeftRight && !bioSet.heading ? bio.ui.y + bioSet.borT : + bioSet.showFilmStrip && bioSet.filmStripPos === 2 && !filmStripOverlay ? marginTopCorr : + bioSet.style === 4 && bioSet.filmStripPos === 0 && filmStripOverlay ? bio.ui.y - bioSet.gap - marginTopCorr2 : + bioSet.filmStripPos !== 0 && !filmStripOverlay ? 0 : + bio.ui.y; + + let max_h = bio.panel.h; + let max_w = bio.panel.w; + this.max_sz = bio.panel.w; + this.text_y = !bio.panel.style.fullWidthHeading || bioSet.img_only ? 0 : bio.panel.text.t; + switch (bioSet.filmStripPos) { case 0: // top - /** MOD */ this.y = (!filmStripOverlay ? (pptBio.filmStripMargin == 2 ? pptBio.borT : pptBio.filmStripMargin == 4 ? pptBio.textT : spacer) : panelBio.img.t + bor) + filmStripCorrY; - max_h = !filmStripOverlay ? panelBio.h - this.y : pptBio.style == 0 || pptBio.style == 2 ? panelBio.style.imgSize : panelBio.h - panelBio.img.t - panelBio.img.b - bor * 2; - this.x = !filmStripOverlay ? (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borL : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textL : spacer) : panelBio.img.l + bor; - this.w = !filmStripOverlay ? (panelBio.w - this.x - (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borR : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textR : spacer)) : pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3 || pptBio.img_only ? panelBio.w - panelBio.img.l - panelBio.img.r - bor * 2 : panelBio.style.imgSize - bor * 2; + /** MOD */ this.y = (!filmStripOverlay ? (bioSet.filmStripMargin == 2 ? bioSet.borT : bioSet.filmStripMargin == 4 ? bioSet.textT : spacer) : bio.panel.img.t + bor) + filmStripCorrY; + max_h = !filmStripOverlay ? bio.panel.h - this.y : bioSet.style == 0 || bioSet.style == 2 ? bio.panel.style.imgSize : bio.panel.h - bio.panel.img.t - bio.panel.img.b - bor * 2; + this.x = !filmStripOverlay ? (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borL : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textL : spacer) : bio.panel.img.l + bor; + this.w = !filmStripOverlay ? (bio.panel.w - this.x - (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borR : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textR : spacer)) : bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3 || bioSet.img_only ? bio.panel.w - bio.panel.img.l - bio.panel.img.r - bor * 2 : bio.panel.style.imgSize - bor * 2; this.max_sz = Math.min(max_h - 5, this.w); - this.blockSize = Math.round(pptBio.filmStripSize * panelBio.h); + this.blockSize = Math.round(bioSet.filmStripSize * bio.panel.h); if (this.style.fit) { this.blockSize = Math.floor(this.w / Math.max(Math.round(this.w / this.blockSize), 1)); this.h = this.blockSize = Math.min(this.blockSize, this.max_sz); @@ -667,19 +667,19 @@ class FilmStrip { this.style.horizontal = true; this.repaint = { x: 0, - y: uiBio.y, - w: panelBio.w, + y: bio.ui.y, + w: bio.panel.w, h: this.y + this.h + 2 }; break; case 1: { // right - const pad_r = pptBio.filmStripMargin == 2 ? pptBio.borR : pptBio.filmStripMargin == 4 ? pptBio.textR : spacer; - max_w = !filmStripOverlay ? panelBio.w - pad_r : pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3 ? panelBio.w - panelBio.img.l - panelBio.img.r - bor * 2 : panelBio.style.imgSize - bor * 2; - /** MOD */ this.y = (!filmStripOverlay ? (this.text_y + marginT || (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borT : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textT : spacer)) : panelBio.img.t + bor) + filmStripCorrY; - this.h = !filmStripOverlay ? (panelBio.h - this.y - (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borB : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textB : spacer)) : pptBio.style == 0 || pptBio.style == 2 ? panelBio.style.imgSize - bor * 2 : pptBio.style > 3 ? (panelBio.clip ? panelBio.style.imgSize - pptBio.borT : panelBio.h - panelBio.img.t - panelBio.img.b - bor * 2) : panelBio.h - panelBio.img.t - panelBio.img.b - bor * 2; + const pad_r = bioSet.filmStripMargin == 2 ? bioSet.borR : bioSet.filmStripMargin == 4 ? bioSet.textR : spacer; + max_w = !filmStripOverlay ? bio.panel.w - pad_r : bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3 ? bio.panel.w - bio.panel.img.l - bio.panel.img.r - bor * 2 : bio.panel.style.imgSize - bor * 2; + /** MOD */ this.y = (!filmStripOverlay ? (this.text_y + marginT || (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borT : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textT : spacer)) : bio.panel.img.t + bor) + filmStripCorrY; + this.h = !filmStripOverlay ? (bio.panel.h - this.y - (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borB : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textB : spacer)) : bioSet.style == 0 || bioSet.style == 2 ? bio.panel.style.imgSize - bor * 2 : bioSet.style > 3 ? (bio.panel.clip ? bio.panel.style.imgSize - bioSet.borT : bio.panel.h - bio.panel.img.t - bio.panel.img.b - bor * 2) : bio.panel.h - bio.panel.img.t - bio.panel.img.b - bor * 2; this.max_sz = Math.min(max_w - 5, this.h); - /** MOD */ this.blockSize = Math.round(pptBio.filmStripSize * panelBio.h); + /** MOD */ this.blockSize = Math.round(bioSet.filmStripSize * bio.panel.h); if (this.style.fit) { this.blockSize = Math.floor(this.h / Math.max(Math.round(this.h / this.blockSize)), 1); this.w = this.blockSize = Math.min(this.blockSize, this.max_sz); @@ -688,24 +688,24 @@ class FilmStrip { this.blockSize = Math.min(this.blockSize, this.max_sz); this.w = this.blockSize; } - this.x = !filmStripOverlay ? panelBio.w - this.w - pad_r : pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3 || pptBio.img_only ? panelBio.w - this.w - panelBio.img.r - bor : panelBio.img.l + panelBio.style.imgSize - this.w - bor; + this.x = !filmStripOverlay ? bio.panel.w - this.w - pad_r : bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3 || bioSet.img_only ? bio.panel.w - this.w - bio.panel.img.r - bor : bio.panel.img.l + bio.panel.style.imgSize - this.w - bor; this.style.horizontal = false; this.repaint = { x: this.x - 2, - y: uiBio.y, - w: panelBio.w - this.x + 2, - h: panelBio.h + y: bio.ui.y, + w: bio.panel.w - this.x + 2, + h: bio.panel.h }; break; } case 2: { // bottom - const pad_b = pptBio.filmStripMargin == 2 ? pptBio.borB : pptBio.filmStripMargin == 4 ? pptBio.textB : spacer; - max_h = !filmStripOverlay ? panelBio.h - pad_b : pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3 ? panelBio.style.imgSize - bor * 2 : panelBio.h - panelBio.img.t - panelBio.img.b - bor * 2; - this.x = !filmStripOverlay ? (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borL : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textL : spacer) : panelBio.img.l + bor; - this.w = !filmStripOverlay ? (panelBio.w - this.x - (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borR : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textR : spacer)) : pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3 || pptBio.img_only ? panelBio.w - panelBio.img.l - panelBio.img.r - bor * 2 : panelBio.style.imgSize - bor * 2; + const pad_b = bioSet.filmStripMargin == 2 ? bioSet.borB : bioSet.filmStripMargin == 4 ? bioSet.textB : spacer; + max_h = !filmStripOverlay ? bio.panel.h - pad_b : bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3 ? bio.panel.style.imgSize - bor * 2 : bio.panel.h - bio.panel.img.t - bio.panel.img.b - bor * 2; + this.x = !filmStripOverlay ? (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borL : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textL : spacer) : bio.panel.img.l + bor; + this.w = !filmStripOverlay ? (bio.panel.w - this.x - (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borR : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textR : spacer)) : bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3 || bioSet.img_only ? bio.panel.w - bio.panel.img.l - bio.panel.img.r - bor * 2 : bio.panel.style.imgSize - bor * 2; this.max_sz = Math.min(max_h - 5, this.w); - this.blockSize = Math.round(pptBio.filmStripSize * panelBio.h); + this.blockSize = Math.round(bioSet.filmStripSize * bio.panel.h); if (this.style.fit) { this.blockSize = Math.floor(this.w / Math.max(Math.round(this.w / this.blockSize), 1)); this.h = this.blockSize = Math.min(this.blockSize, this.max_sz); @@ -714,24 +714,24 @@ class FilmStrip { this.blockSize = Math.min(this.blockSize, this.max_sz); this.h = this.blockSize; } - /** MOD */ this.y = (!filmStripOverlay ? panelBio.h - this.h - pad_b + SCALE(40) : pptBio.style == 0 || pptBio.style == 2 ? panelBio.img.t + panelBio.style.imgSize - this.h - bor : pptBio.style > 3 ? (panelBio.clip ? panelBio.ibox.t + panelBio.style.imgSize - this.h : panelBio.h - panelBio.img.b - this.h - bor) : panelBio.h - panelBio.img.b - this.h - bor) + filmStripCorrY; + /** MOD */ this.y = (!filmStripOverlay ? bio.panel.h - this.h - pad_b + SCALE(40) : bioSet.style == 0 || bioSet.style == 2 ? bio.panel.img.t + bio.panel.style.imgSize - this.h - bor : bioSet.style > 3 ? (bio.panel.clip ? bio.panel.ibox.t + bio.panel.style.imgSize - this.h : bio.panel.h - bio.panel.img.b - this.h - bor) : bio.panel.h - bio.panel.img.b - this.h - bor) + filmStripCorrY; this.style.horizontal = true; this.repaint = { x: 0, y: this.y - 2, - w: panelBio.w, - h: panelBio.h - this.y + 2 + uiBio.y + w: bio.panel.w, + h: bio.panel.h - this.y + 2 + bio.ui.y }; break; } case 3: // left - this.x = !filmStripOverlay ? (pptBio.filmStripMargin == 2 ? pptBio.borL : pptBio.filmStripMargin == 4 ? pptBio.textL : spacer) : panelBio.img.l + bor; - max_w = !filmStripOverlay ? panelBio.w - this.x : pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3 ? panelBio.w - panelBio.img.l - panelBio.img.r - bor * 2 : panelBio.style.imgSize - bor * 2; - /** MOD */ this.y = (!filmStripOverlay ? (this.text_y + marginT || (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borT : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textT : spacer)) : panelBio.img.t + bor) + filmStripCorrY; - this.h = !filmStripOverlay ? (panelBio.h - this.y - (pptBio.filmStripMargin == 1 || pptBio.filmStripMargin == 2 ? pptBio.borB : pptBio.filmStripMargin == 3 || pptBio.filmStripMargin == 4 ? pptBio.textB : spacer)) : pptBio.style == 0 || pptBio.style == 2 ? panelBio.style.imgSize - bor * 2 : pptBio.style > 3 ? (panelBio.clip ? panelBio.style.imgSize - pptBio.borT : panelBio.h - panelBio.img.t - panelBio.img.b - bor * 2) : panelBio.h - panelBio.img.t - panelBio.img.b - bor * 2; + this.x = !filmStripOverlay ? (bioSet.filmStripMargin == 2 ? bioSet.borL : bioSet.filmStripMargin == 4 ? bioSet.textL : spacer) : bio.panel.img.l + bor; + max_w = !filmStripOverlay ? bio.panel.w - this.x : bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3 ? bio.panel.w - bio.panel.img.l - bio.panel.img.r - bor * 2 : bio.panel.style.imgSize - bor * 2; + /** MOD */ this.y = (!filmStripOverlay ? (this.text_y + marginT || (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borT : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textT : spacer)) : bio.panel.img.t + bor) + filmStripCorrY; + this.h = !filmStripOverlay ? (bio.panel.h - this.y - (bioSet.filmStripMargin == 1 || bioSet.filmStripMargin == 2 ? bioSet.borB : bioSet.filmStripMargin == 3 || bioSet.filmStripMargin == 4 ? bioSet.textB : spacer)) : bioSet.style == 0 || bioSet.style == 2 ? bio.panel.style.imgSize - bor * 2 : bioSet.style > 3 ? (bio.panel.clip ? bio.panel.style.imgSize - bioSet.borT : bio.panel.h - bio.panel.img.t - bio.panel.img.b - bor * 2) : bio.panel.h - bio.panel.img.t - bio.panel.img.b - bor * 2; this.max_sz = Math.min(max_w - 5, this.h); - /** MOD */ this.blockSize = Math.round(pptBio.filmStripSize * panelBio.h); + /** MOD */ this.blockSize = Math.round(bioSet.filmStripSize * bio.panel.h); if (this.style.fit) { this.blockSize = Math.floor(this.h / Math.max(Math.round(this.h / this.blockSize)), 1); this.w = this.blockSize = Math.min(this.blockSize, this.max_sz); @@ -743,9 +743,9 @@ class FilmStrip { this.style.horizontal = false; this.repaint = { x: 0, - y: uiBio.y, + y: bio.ui.y, w: this.x + this.w + 2, - h: panelBio.h + h: bio.panel.h }; break; } @@ -774,7 +774,7 @@ class FilmStrip { } trace(x, y) { - if (!panelBio.style.showFilmStrip) return false; + if (!bio.panel.style.showFilmStrip) return false; return y > this.y && y < this.y + this.h && x > this.x && x < this.x + this.w; } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-helpers.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-helpers.js index e229b42b..2be3e124 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-helpers.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-helpers.js @@ -1,9 +1,10 @@ 'use strict'; -const requiredVersionStr = '1.5.2'; +/** @global @type {string} */ +const bioRequiredVersionStr = '1.5.2'; -function is_compatible(requiredVersionStr) { - const requiredVersion = requiredVersionStr.split('.'); +function lib_is_compatible(bioRequiredVersionStr) { + const requiredVersion = bioRequiredVersionStr.split('.'); const currentVersion = utils.Version.split('.'); if (currentVersion.length > 3) currentVersion.length = 3; for (let i = 0; i < currentVersion.length; ++i) { @@ -11,14 +12,18 @@ function is_compatible(requiredVersionStr) { } return true; } -if (!is_compatible(requiredVersionStr)) fb.ShowPopupMessage(`Biography requires v${requiredVersionStr}. Current component version is v${utils.Version}.`); - -const docBio = new ActiveXObject('htmlfile'); -const fsoBio = new ActiveXObject('Scripting.FileSystemObject'); -const tooltipBio = window.Tooltip; -const WshShellBio = new ActiveXObject('WScript.Shell'); - -class Helpers { +if (!lib_is_compatible(bioRequiredVersionStr)) fb.ShowPopupMessage(`Biography requires v${bioRequiredVersionStr}. Current component version is v${utils.Version}.`); + +/** @global @type {ActiveXObject} */ +const bioDoc = new ActiveXObject('htmlfile'); +/** @global @type {ActiveXObject} */ +const bioFSO = new ActiveXObject('Scripting.FileSystemObject'); +/** @global @type {FbTooltip} */ +const bioTooltip = window.Tooltip; +/** @global @type {ActiveXObject} */ +const bioWshShell = new ActiveXObject('WScript.Shell'); + +class BioHelpers { constructor() { this.diacriticsMap = {}; this.key = ['t', 'r', 'b', 'l']; @@ -76,7 +81,7 @@ class Helpers { create(fo) { try { - if (!this.folder(fo)) fsoBio.CreateFolder(fo); + if (!this.folder(fo)) bioFSO.CreateFolder(fo); } catch (e) {} } @@ -109,17 +114,17 @@ class Helpers { eval(n, focus, ignoreLock) { if (!n) return ''; const tfo = FbTitleFormat(n); - if (panelBio.isRadio(focus)) return tfo.Eval(); + if (bio.panel.isRadio(focus)) return tfo.Eval(); const handle = this.handle(focus, ignoreLock); return handle ? tfo.EvalWithMetadb(handle) : ''; } file(f) { - return typeof f === 'string' && fsoBio.FileExists(f); + return typeof f === 'string' && bioFSO.FileExists(f); } folder(fo) { - return typeof fo === 'string' && fsoBio.FolderExists(fo); + return typeof fo === 'string' && bioFSO.FolderExists(fo); } getClipboardData() { @@ -127,7 +132,7 @@ class Helpers { return utils.GetClipboardText(); } catch (e) { try { - return docBio.parentWindow.clipboardData.getData('Text'); + return bioDoc.parentWindow.clipboardData.getData('Text'); } catch (e) { return null; } @@ -137,7 +142,7 @@ class Helpers { getDpi() { let dpi = 120; try { - dpi = WshShellBio.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI'); + dpi = bioWshShell.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI'); } catch (e) {} return Math.max(dpi / 120, 1); } @@ -162,7 +167,7 @@ class Helpers { } handle(focus, ignoreLock) { - return !panelBio.lock || ignoreLock ? fb.IsPlaying && !focus ? fb.GetNowPlaying() : fb.GetFocusItem() : panelBio.lockHandle; + return !bio.panel.lock || ignoreLock ? fb.IsPlaying && !focus ? fb.GetNowPlaying() : fb.GetFocusItem() : bio.panel.lockHandle; } htmlParse(n, prop, match, func) { @@ -223,13 +228,13 @@ class Helpers { lastAccessed(file) { try { - return Date.parse(fsoBio.GetFile(file).DateLastAccessed); + return Date.parse(bioFSO.GetFile(file).DateLastAccessed); } catch (e) {} } lastModified(file) { try { - return Date.parse(fsoBio.GetFile(file).DateLastModified); + return Date.parse(bioFSO.GetFile(file).DateLastModified); } catch (e) {} } @@ -302,7 +307,7 @@ class Helpers { run(c, w) { try { - w === undefined ? WshShellBio.Run(c) : WshShellBio.Run(c, w); + w === undefined ? bioWshShell.Run(c) : bioWshShell.Run(c, w); return true; } catch (e) { return false; @@ -369,7 +374,7 @@ class Helpers { } throttle(e, i, t) { - let n = !0; let r = !0; if (typeof e != 'function') throw new TypeError('throttle: invalid function'); return this.isObject(t) && (n = 'leading' in t ? !!txt.leading : n, r = 'trailing' in t ? !!txt.trailing : r), this.debounce(e, i, { leading:n, maxWait:i, trailing:r }); + let n = !0; let r = !0; if (typeof e != 'function') throw new TypeError('throttle: invalid function'); return this.isObject(t) && (n = 'leading' in t ? !!bio.txt.leading : n, r = 'trailing' in t ? !!bio.txt.trailing : r), this.debounce(e, i, { leading:n, maxWait:i, trailing:r }); } titlecase(n) { @@ -388,7 +393,7 @@ class Helpers { } trace(message, n) { - if (!cfg.showConsoleMessages) return; + if (!bioCfg.showConsoleMessages) return; console.log(`Biography${n ? ' Server' : ''}: ${message}`); } @@ -410,7 +415,7 @@ class Helpers { wshPopup(prompt, caption) { try { - const ns = WshShellBio.Popup(prompt, 0, caption, 1); + const ns = bioWshShell.Popup(prompt, 0, caption, 1); if (ns == 1) return true; return false; } catch (e) { @@ -419,7 +424,12 @@ class Helpers { } } -const $Bio = new Helpers(); +/** + * The instance of `BioHelpers` class for biography helper operations. + * @typedef {BioHelpers} + * @global + */ +const $Bio = new BioHelpers(); function RGB(r, g, b) { return 0xff000000 | r << 16 | g << 8 | b; @@ -450,14 +460,28 @@ function StringFormat() { } /* eslint-disable */ -function Bezier(){const i=4,c=.001,o=1e-7,v=10,l=11,s=1/(l-1),n=typeof Float32Array==='function';function e(r,n){return 1-3*n+3*r;}function u(r,n){return 3*n-6*r;}function a(r){return 3*r;}function w(r,n,t){return((e(n,t)*r+u(n,t))*r+a(n))*r;}function y(r,n,t){return 3*e(n,t)*r*r+2*u(n,t)*r+a(n);}function h(r,n,t,e,u){let a,f,i=0;do{f=n+(t-n)/2;a=w(f,e,u)-r;if(a>0){t=f;}else{n=f;}}while(Math.abs(a)>o&&++i=c){return A(r,a,i,o);}else if(f===0){return a;}else{return h(r,n,n+s,i,o);}}return function r(n){if(n===0){return 0;}if(n===1){return 1;}return w(u(n),t,e);};} this.scroll = bezier(0.25, 0.1, 0.25, 1); this.full = this.scroll; this.step = this.scroll; this.bar = bezier(0.165,0.84,0.44,1); this.barFast = bezier(0.19, 1, 0.22, 1); this.inertia = bezier(0.23, 1, 0.32, 1);} -const easeBio = new Bezier; +function BioBezier(){const i=4,c=.001,o=1e-7,v=10,l=11,s=1/(l-1),n=typeof Float32Array==='function';function e(r,n){return 1-3*n+3*r;}function u(r,n){return 3*n-6*r;}function a(r){return 3*r;}function w(r,n,t){return((e(n,t)*r+u(n,t))*r+a(n))*r;}function y(r,n,t){return 3*e(n,t)*r*r+2*u(n,t)*r+a(n);}function h(r,n,t,e,u){let a,f,i=0;do{f=n+(t-n)/2;a=w(f,e,u)-r;if(a>0){t=f;}else{n=f;}}while(Math.abs(a)>o&&++i=c){return A(r,a,i,o);}else if(f===0){return a;}else{return h(r,n,n+s,i,o);}}return function r(n){if(n===0){return 0;}if(n===1){return 1;}return w(u(n),t,e);};} this.scroll = bezier(0.25, 0.1, 0.25, 1); this.full = this.scroll; this.step = this.scroll; this.bar = bezier(0.165,0.84,0.44,1); this.barFast = bezier(0.19, 1, 0.22, 1); this.inertia = bezier(0.23, 1, 0.32, 1);} + +/** + * The instance of `BioBezier` function for calculating points on a Bezier curve. + * This can be used for animations, transitions, or any operations that require Bezier curve computations. + * @typedef {BioBezier} + * @global + */ +const bioEase = new BioBezier; + +function BioMD5(){const b=function(l,n){let m=l[0],j=l[1],p=l[2],o=l[3];m+=(j&p|~j&o)+n[0]-680876936|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[1]-389564586|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[2]+606105819|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[3]-1044525330|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[4]-176418897|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[5]+1200080426|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[6]-1473231341|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[7]-45705983|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[8]+1770035416|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[9]-1958414417|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[10]-42063|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[11]-1990404162|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[12]+1804603682|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[13]-40341101|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[14]-1502002290|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[15]+1236535329|0;j=(j<<22|j>>>10)+p|0;m+=(j&o|p&~o)+n[1]-165796510|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[6]-1069501632|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[11]+643717713|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[0]-373897302|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[5]-701558691|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[10]+38016083|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[15]-660478335|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[4]-405537848|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[9]+568446438|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[14]-1019803690|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[3]-187363961|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[8]+1163531501|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[13]-1444681467|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[2]-51403784|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[7]+1735328473|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[12]-1926607734|0;j=(j<<20|j>>>12)+p|0;m+=(j^p^o)+n[5]-378558|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[8]-2022574463|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[11]+1839030562|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[14]-35309556|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[1]-1530992060|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[4]+1272893353|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[7]-155497632|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[10]-1094730640|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[13]+681279174|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[0]-358537222|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[3]-722521979|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[6]+76029189|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[9]-640364487|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[12]-421815835|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[15]+530742520|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[2]-995338651|0;j=(j<<23|j>>>9)+p|0;m+=(p^(j|~o))+n[0]-198630844|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[7]+1126891415|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[14]-1416354905|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[5]-57434055|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[12]+1700485571|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[3]-1894986606|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[10]-1051523|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[1]-2054922799|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[8]+1873313359|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[15]-30611744|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[6]-1560198380|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[13]+1309151649|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[4]-145523070|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[11]-1120210379|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[2]+718787259|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[9]-343485551|0;j=(j<<21|j>>>11)+p|0;l[0]=m+l[0]|0;l[1]=j+l[1]|0;l[2]=p+l[2]|0;l[3]=o+l[3]|0;};const e='0123456789abcdef';const d=[];const c=function(k){const q=e;const o=d;let r,p,l;for(let m=0;m<4;m++){p=m*8;r=k[m];for(l=0;l<8;l+=2){o[p+1+l]=q.charAt(r&15);r>>>=4;o[p+0+l]=q.charAt(r&15);r>>>=4;}}return o.join('');};const i=function(){this._dataLength=0;this._state=new Int32Array(4);this._buffer=new ArrayBuffer(68);this._bufferLength=0;this._buffer8=new Uint8Array(this._buffer,0,68);this._buffer32=new Uint32Array(this._buffer,0,17);this.start();};const a=new Int32Array([1732584193,-271733879,-1732584194,271733878]);const h=new Int32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);i.prototype.appendStr=function(n){const k=this._buffer8;const j=this._buffer32;let o=this._bufferLength;for(let l=0;l>>6)+192;k[o++]=m&63|128;}else{if(m<55296||m>56319){k[o++]=(m>>>12)+224;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}else{m=((m-55296)*1024)+(n.charCodeAt(++l)-56320)+65536;if(m>1114111){throw'Unicode standard supports code points up to U+10FFFF';}k[o++]=(m>>>18)+240;k[o++]=(m>>>12&63)|128;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}}}if(o>=64){this._dataLength+=64;b(this._state,j);o-=64;j[0]=j[16];}}this._bufferLength=o;return this;};i.prototype.appendAsciiStr=function(o){const l=this._buffer8;const k=this._buffer32;let p=this._bufferLength;let n=0,m=0;for(;;){n=Math.min(o.length-m,64-p);while(n--){l[p++]=o.charCodeAt(m++);}if(p<64){break;}this._dataLength+=64;b(this._state,k);p=0;}this._bufferLength=p;return this;};i.prototype.start=function(){this._dataLength=0;this._bufferLength=0;this._state.set(a);return this;};i.prototype.end=function(){const q=this._bufferLength;this._dataLength+=q;const r=this._buffer8;r[q]=128;r[q+1]=r[q+2]=r[q+3]=0;const k=this._buffer32;const m=(q>>2)+1;k.set(h.subarray(m),m);if(q>55){b(this._state,k);k.set(h);}const j=this._dataLength*8;if(j<=4294967295){k[14]=j;}else{const n=j.toString(16).match(/(.*?)(.{0,8})$/);const o=parseInt(n[2],16);const l=parseInt(n[1],16)||0;k[14]=o;k[15]=l;}b(this._state,k);return c(this._state);};const f=new i();i.hashStr=function(k){return f.start().appendStr(k).end();};return i;} // https://github.com/gorhill/yamd5.js -function MD5(){const b=function(l,n){let m=l[0],j=l[1],p=l[2],o=l[3];m+=(j&p|~j&o)+n[0]-680876936|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[1]-389564586|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[2]+606105819|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[3]-1044525330|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[4]-176418897|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[5]+1200080426|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[6]-1473231341|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[7]-45705983|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[8]+1770035416|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[9]-1958414417|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[10]-42063|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[11]-1990404162|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[12]+1804603682|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[13]-40341101|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[14]-1502002290|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[15]+1236535329|0;j=(j<<22|j>>>10)+p|0;m+=(j&o|p&~o)+n[1]-165796510|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[6]-1069501632|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[11]+643717713|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[0]-373897302|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[5]-701558691|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[10]+38016083|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[15]-660478335|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[4]-405537848|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[9]+568446438|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[14]-1019803690|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[3]-187363961|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[8]+1163531501|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[13]-1444681467|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[2]-51403784|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[7]+1735328473|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[12]-1926607734|0;j=(j<<20|j>>>12)+p|0;m+=(j^p^o)+n[5]-378558|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[8]-2022574463|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[11]+1839030562|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[14]-35309556|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[1]-1530992060|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[4]+1272893353|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[7]-155497632|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[10]-1094730640|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[13]+681279174|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[0]-358537222|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[3]-722521979|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[6]+76029189|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[9]-640364487|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[12]-421815835|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[15]+530742520|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[2]-995338651|0;j=(j<<23|j>>>9)+p|0;m+=(p^(j|~o))+n[0]-198630844|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[7]+1126891415|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[14]-1416354905|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[5]-57434055|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[12]+1700485571|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[3]-1894986606|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[10]-1051523|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[1]-2054922799|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[8]+1873313359|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[15]-30611744|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[6]-1560198380|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[13]+1309151649|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[4]-145523070|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[11]-1120210379|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[2]+718787259|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[9]-343485551|0;j=(j<<21|j>>>11)+p|0;l[0]=m+l[0]|0;l[1]=j+l[1]|0;l[2]=p+l[2]|0;l[3]=o+l[3]|0;};const e='0123456789abcdef';const d=[];const c=function(k){const q=e;const o=d;let r,p,l;for(let m=0;m<4;m++){p=m*8;r=k[m];for(l=0;l<8;l+=2){o[p+1+l]=q.charAt(r&15);r>>>=4;o[p+0+l]=q.charAt(r&15);r>>>=4;}}return o.join('');};const i=function(){this._dataLength=0;this._state=new Int32Array(4);this._buffer=new ArrayBuffer(68);this._bufferLength=0;this._buffer8=new Uint8Array(this._buffer,0,68);this._buffer32=new Uint32Array(this._buffer,0,17);this.start();};const a=new Int32Array([1732584193,-271733879,-1732584194,271733878]);const h=new Int32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);i.prototype.appendStr=function(n){const k=this._buffer8;const j=this._buffer32;let o=this._bufferLength;for(let l=0;l>>6)+192;k[o++]=m&63|128;}else{if(m<55296||m>56319){k[o++]=(m>>>12)+224;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}else{m=((m-55296)*1024)+(n.charCodeAt(++l)-56320)+65536;if(m>1114111){throw'Unicode standard supports code points up to U+10FFFF';}k[o++]=(m>>>18)+240;k[o++]=(m>>>12&63)|128;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}}}if(o>=64){this._dataLength+=64;b(this._state,j);o-=64;j[0]=j[16];}}this._bufferLength=o;return this;};i.prototype.appendAsciiStr=function(o){const l=this._buffer8;const k=this._buffer32;let p=this._bufferLength;let n=0,m=0;for(;;){n=Math.min(o.length-m,64-p);while(n--){l[p++]=o.charCodeAt(m++);}if(p<64){break;}this._dataLength+=64;b(this._state,k);p=0;}this._bufferLength=p;return this;};i.prototype.start=function(){this._dataLength=0;this._bufferLength=0;this._state.set(a);return this;};i.prototype.end=function(){const q=this._bufferLength;this._dataLength+=q;const r=this._buffer8;r[q]=128;r[q+1]=r[q+2]=r[q+3]=0;const k=this._buffer32;const m=(q>>2)+1;k.set(h.subarray(m),m);if(q>55){b(this._state,k);k.set(h);}const j=this._dataLength*8;if(j<=4294967295){k[14]=j;}else{const n=j.toString(16).match(/(.*?)(.{0,8})$/);const o=parseInt(n[2],16);const l=parseInt(n[1],16)||0;k[14]=o;k[15]=l;}b(this._state,k);return c(this._state);};const f=new i();i.hashStr=function(k){return f.start().appendStr(k).end();};return i;} // https://github.com/gorhill/yamd5.js -const md5Bio = new MD5; +/** + * The instance of `bioMD5` function for biography MD5 hash generation. + * @typedef {bioMD5} + * @global + */ +const bioMD5 = new BioMD5; -const codeToCountry = { +/** @global @type {object.} */ +const bioCodeToCountry = { US: 'United States', GB: 'United Kingdom', AU: 'Australia', @@ -703,7 +727,8 @@ const codeToCountry = { ZW: 'Zimbabwe' } -const countryToCode = { +/** @global @type {object.} */ +const bioCountryToCode = { unitedstates: 'US', unitedkingdom: 'GB', australia: 'AU', diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-images.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-images.js index d66e8d51..a178e1ad 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-images.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-images.js @@ -1,6 +1,6 @@ 'use strict'; -class ImagesBio { +class BioImages { constructor() { this.artist = ''; this.blur = null; @@ -35,7 +35,7 @@ class ImagesBio { }; this.blackList = { - file: `${cfg.storageFolder}blacklist_image.json`, + file: `${bioCfg.storageFolder}blacklist_image.json`, artist: '', cur: '', item: [], @@ -49,17 +49,17 @@ class ImagesBio { this.cov = { counter: 0, - cycle: pptBio.loadCovAllFb || pptBio.loadCovFolder, + cycle: bioSet.loadCovAllFb || bioSet.loadCovFolder, cycle_ix: 0, done: '', folder: '', - folderSameAsArt: cfg.albCovFolder.toUpperCase() == cfg.pth.foImgArt.toUpperCase(), + folderSameAsArt: bioCfg.albCovFolder.toUpperCase() == bioCfg.pth.foImgArt.toUpperCase(), ix: 0, images: [], list: [], newBlur: false, blur: null, - selection: $Bio.jsonParse(pptBio.loadCovSelFb, [0, 1, 2, 3, 4]) + selection: $Bio.jsonParse(bioSet.loadCovSelFb, [0, 1, 2, 3, 4]) }; this.filter = { @@ -103,15 +103,15 @@ class ImagesBio { reset: false }; - pptBio.reflStrength = $Bio.clamp(pptBio.reflStrength, 0, 100); - pptBio.reflGradient = $Bio.clamp(pptBio.reflGradient, 0, 100); - pptBio.reflSize = $Bio.clamp(pptBio.reflSize, 0, 100); + bioSet.reflStrength = $Bio.clamp(bioSet.reflStrength, 0, 100); + bioSet.reflGradient = $Bio.clamp(bioSet.reflGradient, 0, 100); + bioSet.reflSize = $Bio.clamp(bioSet.reflSize, 0, 100); this.refl = { adjust: false, - gradient: pptBio.reflGradient / 10 - 1, - size: $Bio.clamp(pptBio.reflSize / 100, 0.1, 1), - strength: $Bio.clamp(255 * pptBio.reflStrength / 100, 0, 255) + gradient: bioSet.reflGradient / 10 - 1, + size: $Bio.clamp(bioSet.reflSize / 100, 0.1, 1), + strength: $Bio.clamp(255 * bioSet.reflStrength / 100, 0, 255) }; this.stub = { @@ -140,14 +140,14 @@ class ImagesBio { user: null }, art: { - file: `${cfg.storageFolder}artist_stub_user.png`, - folder: `${cfg.storageFolder}artist_stub_user`, + file: `${bioCfg.storageFolder}artist_stub_user.png`, + folder: `${bioCfg.storageFolder}artist_stub_user`, path: '', user: null }, cov: { - file: `${cfg.storageFolder}front_cover_stub_user.png`, - folder: `${cfg.storageFolder}front_cover_stub_user`, + file: `${bioCfg.storageFolder}front_cover_stub_user.png`, + folder: `${bioCfg.storageFolder}front_cover_stub_user`, path: '', user: null }, @@ -160,7 +160,7 @@ class ImagesBio { circular: false, crop: false, border: 0, - delay: Math.min(pptBio.cycTimePic, 7) * 1000, + delay: Math.min(bioSet.cycTimePic, 7) * 1000, fade: false, horizontal: true, overlay: false, @@ -180,18 +180,18 @@ class ImagesBio { }; this.transition = { - level: $Bio.clamp(100 - pptBio.transLevel, 0.1, 100) + level: $Bio.clamp(100 - bioSet.transLevel, 0.1, 100) }; this.transition.incr = Math.pow(284.2171 / this.transition.level, 0.0625); if (this.transition.level == 100) this.transition.level = 255; this.cycImages = this.cov.folderSameAsArt ? this.artImages : v => { if (!$Bio.file(v)) return false; - return /(?:jpe?g|png|webp|gif|bmp)$/i.test(fsoBio.GetExtensionName(v)); + return /(?:jpe?g|png|webp|gif|bmp)$/i.test(bioFSO.GetExtensionName(v)); }; ['Front', 'Back', 'Disc', 'Icon', 'Art'].forEach((v, i) => { - const f = cfg.expandPath(pptBio[`panel${v}Stub`]); + const f = bioCfg.expandPath(bioSet[`panel${v}Stub`]); if ($Bio.file(f)) { this.stub[i].panel = true; this.stub[i].path = f; @@ -213,17 +213,17 @@ class ImagesBio { } this.setCov = $Bio.debounce(() => { - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); if (this.cov.ix < 0) this.cov.ix = this.cov.images.length - 1; else if (this.cov.ix >= this.cov.images.length) this.cov.ix = 0; this.cov.cycle_ix = this.cov.ix; const key = this.cov.images[this.cov.ix]; - this.loadImg(cov, key, true, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); + this.loadImg(bioCov, key, true, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); this.timeStamp.cov = Date.now(); }, 100); this.setPhoto = $Bio.debounce(() => { - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); if (this.art.ix < 0) this.art.ix = this.art.images.length - 1; else if (this.art.ix >= this.art.images.length) this.art.ix = 0; this.loadArtImage(); @@ -231,7 +231,7 @@ class ImagesBio { }, 100); this.createImages(); - if (pptBio.img_only) this.setCrop(true); + if (bioSet.img_only) this.setCrop(true); this.processSizeFilter(); this.cov.selFiltered = this.cov.selection.filter(v => v != -1); @@ -242,30 +242,30 @@ class ImagesBio { artImages(v) { if (!$Bio.file(v)) return false; const fileSize = utils.GetFileSize(v); - return (name.isLfmImg(fsoBio.GetFileName(v)) || !pptBio.imgFilterLfm && /(?:jpe?g|png|webp|gif|bmp)$/i.test(fsoBio.GetExtensionName(v)) && !/ - /.test(fsoBio.GetBaseName(v))) && !this.exclArr.includes(fileSize) && !this.blackListed(v); + return (bio.name.isLfmImg(bioFSO.GetFileName(v)) || !bioSet.imgFilterLfm && /(?:jpe?g|png|webp|gif|bmp)$/i.test(bioFSO.GetExtensionName(v)) && !/ - /.test(bioFSO.GetBaseName(v))) && !this.exclArr.includes(fileSize) && !this.blackListed(v); } artistReset(force) { - if (panelBio.lock) return; + if (bio.panel.lock) return; this.blurCheck(); this.cur_artist = this.artist; - this.artist = name.artist(panelBio.id.focus); - const new_artist = this.artist && this.artist != this.cur_artist || !this.artist || pptBio.covBlur && uiBio.style.isBlur && this.id.blur != this.id.curBlur || force; + this.artist = bio.name.artist(bio.panel.id.focus); + const new_artist = this.artist && this.artist != this.cur_artist || !this.artist || bioSet.covBlur && bio.ui.style.isBlur && this.id.blur != this.id.curBlur || force; if (new_artist) { this.art.folderSup = ''; let files = []; - if (pptBio.cycPhotoLocation == 1) { - this.art.folder = !panelBio.isRadio(panelBio.id.focus) ? panelBio.cleanPth(cfg.artCusImgFolder, panelBio.id.focus) : panelBio.cleanPth(cfg.remap.foCycPhoto, panelBio.id.focus, 'remap', this.artist, '', 1); + if (bioSet.cycPhotoLocation == 1) { + this.art.folder = !bio.panel.isRadio(bio.panel.id.focus) ? bio.panel.cleanPth(bioCfg.artCusImgFolder, bio.panel.id.focus) : bio.panel.cleanPth(bioCfg.remap.foCycPhoto, bio.panel.id.focus, 'remap', this.artist, '', 1); files = utils.Glob(`${this.art.folder}*`); } - if (files.length && files.some(v => /(?:jpe?g|png|webp|gif|bmp)$/i.test(fsoBio.GetExtensionName(v)))) { + if (files.length && files.some(v => /(?:jpe?g|png|webp|gif|bmp)$/i.test(bioFSO.GetExtensionName(v)))) { this.art.cusPhotoLocation = true; } else { - this.art.folder = !panelBio.isRadio(panelBio.id.focus) ? panelBio.cleanPth(cfg.pth.foImgArt, panelBio.id.focus) : panelBio.cleanPth(cfg.remap.foImgArt, panelBio.id.focus, 'remap', this.artist, '', 1); + this.art.folder = !bio.panel.isRadio(bio.panel.id.focus) ? bio.panel.cleanPth(bioCfg.pth.foImgArt, bio.panel.id.focus) : bio.panel.cleanPth(bioCfg.remap.foImgArt, bio.panel.id.focus, 'remap', this.artist, '', 1); this.art.cusPhotoLocation = false; } this.clearArtCache(true); - if (pptBio.cycPhoto) this.art.done = false; + if (bioSet.cycPhoto) this.art.done = false; if (!this.art.images.length) { this.art.allFilesLength = 0; this.art.ix = 0; @@ -280,7 +280,7 @@ class ImagesBio { if (!image) { this.art.images.splice(this.art.ix, 1); if (this.art.images.length > 1) this.changePhoto(1); - filmStrip.check('imgUpd'); + bio.filmStrip.check('imgUpd'); return; } this.processArtImg(image, image_path); @@ -295,39 +295,39 @@ class ImagesBio { } blackListed(v) { - imgBio.blackList.cur = this.blackList.artist; - this.blackList.artist = this.artist || name.artist(panelBio.id.focus); - if (this.blackList.artist && this.blackList.artist != imgBio.blackList.cur) { - imgBio.blackList.item = this.blacklist($Bio.clean(this.blackList.artist).toLowerCase()); + bio.img.blackList.cur = this.blackList.artist; + this.blackList.artist = this.artist || bio.name.artist(bio.panel.id.focus); + if (this.blackList.artist && this.blackList.artist != bio.img.blackList.cur) { + bio.img.blackList.item = this.blacklist($Bio.clean(this.blackList.artist).toLowerCase()); } - return imgBio.blackList.item.includes(v.slice(v.lastIndexOf('_') + 1)); + return bio.img.blackList.item.includes(v.slice(v.lastIndexOf('_') + 1)); } blurCheck() { - if (!(pptBio.covBlur && uiBio.style.isBlur) && !pptBio.imgSmoothTrans || pptBio.themed) return; + if (!(bioSet.covBlur && bio.ui.style.isBlur) && !bioSet.imgSmoothTrans || bioSet.themed) return; this.id.curBlur = this.id.blur; - this.id.blur = name.albID(panelBio.id.focus, 'stnd'); - this.id.blur += pptBio.covType; + this.id.blur = bio.name.albID(bio.panel.id.focus, 'stnd'); + this.id.blur += bioSet.covType; if (this.id.blur != this.id.curBlur) { this.cov.newBlur = true; - txt.rev.lookUp = false; + bio.txt.rev.lookUp = false; } } blurImage(image, o) { - if (!image || !panelBio.w || !panelBio.h) return; + if (!image || !bio.panel.w || !bio.panel.h) return; if (this.covBlur() && this.cov.newBlur) { let handle = null; this.cov.blur = null; - if (cfg.cusCov && !pptBio.covType) { - this.chkPths(cfg.cusCovPaths, '', 1, true); + if (bioCfg.cusCov && !bioSet.covType) { + this.chkPths(bioCfg.cusCovPaths, '', 1, true); } if (!this.cov.blur) { - handle = $Bio.handle(panelBio.id.focus); - if (handle) this.cov.blur = utils.GetAlbumArtV2(handle, pptBio.covType, !!pptBio.covType); + handle = $Bio.handle(bio.panel.id.focus); + if (handle) this.cov.blur = utils.GetAlbumArtV2(handle, bioSet.covType, !!bioSet.covType); } - if (!this.cov.blur && !pptBio.covType) { - const pth_cov = panelBio.getPth('cov', panelBio.id.focus).pth; + if (!this.cov.blur && !bioSet.covType) { + const pth_cov = bio.panel.getPth('cov', bio.panel.id.focus).pth; this.ext.some(v => { if ($Bio.file(pth_cov + v)) { this.cov.blur = gdi.Image(pth_cov + v); @@ -335,38 +335,38 @@ class ImagesBio { } }); } - if (!this.cov.blur && !pptBio.covType) { - const a = name.albumArtist(panelBio.id.focus); - const l = name.album(panelBio.id.focus); - const pth_cov = [panelBio.getPth('cov', panelBio.id.focus).pth, panelBio.getPth('img', panelBio.id.focus, a, l).pth]; + if (!this.cov.blur && !bioSet.covType) { + const a = bio.name.albumArtist(bio.panel.id.focus); + const l = bio.name.album(bio.panel.id.focus); + const pth_cov = [bio.panel.getPth('cov', bio.panel.id.focus).pth, bio.panel.getPth('img', bio.panel.id.focus, a, l).pth]; this.chkPths(pth_cov, '', 1); } - if (!this.cov.blur && !pptBio.covType && handle) this.cov.blur = utils.GetAlbumArtV2(handle, 0); + if (!this.cov.blur && !bioSet.covType && handle) this.cov.blur = utils.GetAlbumArtV2(handle, 0); if (!this.cov.blur) this.cov.blur = this.stub.default[0].Clone(0, 0, this.stub.default[0].Width, this.stub.default[0].Height); this.cov.newBlur = false; - if (this.cov.blur && !pptBio.blurAutofill) this.cov.blur = this.cov.blur.Resize(panelBio.w, panelBio.h); + if (this.cov.blur && !bioSet.blurAutofill) this.cov.blur = this.cov.blur.Resize(bio.panel.w, bio.panel.h); } if (this.covBlur() && this.cov.blur) image = this.cov.blur.Clone(0, 0, this.cov.blur.Width, this.cov.blur.Height); // clone to stop blurring same img more than once - image = pptBio.blurAutofill ? this.format(image, 1, 'crop', panelBio.w, panelBio.h, 'blurAutofill', o) : this.format(image, 1, 'stretch', panelBio.w, panelBio.h, 'blurStretch', o); - const i = $Bio.gr(panelBio.w, panelBio.h, true, (g, gi) => { + image = bioSet.blurAutofill ? this.format(image, 1, 'crop', bio.panel.w, bio.panel.h, 'blurAutofill', o) : this.format(image, 1, 'stretch', bio.panel.w, bio.panel.h, 'blurStretch', o); + const i = $Bio.gr(bio.panel.w, bio.panel.h, true, (g, gi) => { g.SetInterpolationMode(0); - if (uiBio.blur.blend) { - if (pptBio.blurTemp) { - const iSmall = image.Resize(Math.max(panelBio.w * uiBio.blur.level / 100, 1), Math.max(panelBio.h * uiBio.blur.level / 100, 2, 1), 2); - const iFull = iSmall.Resize(panelBio.w, panelBio.h, 2); - const offset = 90 - uiBio.blur.level; - g.DrawImage(iFull, uiBio.x - offset, uiBio.y - SCALE(40), panelBio.w + offset * 2, panelBio.h + offset * 2, 0, 0, iFull.Width, iFull.Height, 0, uiBio.blur.blendAlpha); - } else g.DrawImage(image, uiBio.x, uiBio.y - SCALE(40), panelBio.w, panelBio.h, 0, 0, image.Width, image.Height, 0, uiBio.blur.blendAlpha); // no blur + if (bio.ui.blur.blend) { + if (bioSet.blurTemp) { + const iSmall = image.Resize(Math.max(bio.panel.w * bio.ui.blur.level / 100, 1), Math.max(bio.panel.h * bio.ui.blur.level / 100, 2, 1), 2); + const iFull = iSmall.Resize(bio.panel.w, bio.panel.h, 2); + const offset = 90 - bio.ui.blur.level; + g.DrawImage(iFull, bio.ui.x - offset, bio.ui.y - SCALE(40), bio.panel.w + offset * 2, bio.panel.h + offset * 2, 0, 0, iFull.Width, iFull.Height, 0, bio.ui.blur.blendAlpha); + } else g.DrawImage(image, bio.ui.x, bio.ui.y - SCALE(40), bio.panel.w, bio.panel.h, 0, 0, image.Width, image.Height, 0, bio.ui.blur.blendAlpha); // no blur } else { - if (pptBio.theme == 1 || pptBio.theme == 3) { - g.DrawImage(image, uiBio.x, uiBio.y - SCALE(40), panelBio.w, panelBio.h, 0, 0, image.Width, image.Height); - if (uiBio.blur.level > 1) gi.StackBlur(uiBio.blur.level); - g.FillSolidRect(uiBio.x, uiBio.y - SCALE(40), panelBio.w, panelBio.h, this.isImageLight(gi) ? uiBio.col.bg_light : uiBio.col.bg_dark); + if (bioSet.theme == 1 || bioSet.theme == 3) { + g.DrawImage(image, bio.ui.x, bio.ui.y - SCALE(40), bio.panel.w, bio.panel.h, 0, 0, image.Width, image.Height); + if (bio.ui.blur.level > 1) gi.StackBlur(bio.ui.blur.level); + g.FillSolidRect(bio.ui.x, bio.ui.y - SCALE(40), bio.panel.w, bio.panel.h, this.isImageLight(gi) ? bio.ui.col.bg_light : bio.ui.col.bg_dark); } - if (pptBio.theme == 4) { - g.FillSolidRect(uiBio.x, uiBio.y - SCALE(40), panelBio.w, panelBio.h, this.getRandomCol()); - g.DrawImage(image, uiBio.x, uiBio.y - SCALE(40), panelBio.w, panelBio.h, 0, 0, image.Width, image.Height, 0, this.getImgAlpha(image)); - if (uiBio.blur.level > 1) gi.StackBlur(uiBio.blur.level); + if (bioSet.theme == 4) { + g.FillSolidRect(bio.ui.x, bio.ui.y - SCALE(40), bio.panel.w, bio.panel.h, this.getRandomCol()); + g.DrawImage(image, bio.ui.x, bio.ui.y - SCALE(40), bio.panel.w, bio.panel.h, 0, 0, image.Width, image.Height, 0, this.getImgAlpha(image)); + if (bio.ui.blur.level > 1) gi.StackBlur(bio.ui.blur.level); } } }); @@ -374,21 +374,21 @@ class ImagesBio { } cache() { - return pptBio.artistView ? art : cov; + return bioSet.artistView ? bioArt : bioCov; } changeCov(incr) { - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); this.cov.cycle_ix += incr; if (this.cov.cycle_ix < 0) this.cov.cycle_ix = this.cov.images.length - 1; else if (this.cov.cycle_ix >= this.cov.images.length) this.cov.cycle_ix = 0; this.cov.ix = this.cov.cycle_ix; const key = this.cov.images[this.cov.ix]; - this.loadImg(cov, key, true, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); + this.loadImg(bioCov, key, true, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); } changePhoto(incr) { - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); this.art.ix += incr; if (this.art.ix < 0) this.art.ix = this.art.images.length - 1; else if (this.art.ix >= this.art.images.length) this.art.ix = 0; @@ -404,24 +404,24 @@ class ImagesBio { } check() { - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); this.id.albCyc = ''; this.id.curAlbCyc = ''; this.clearArtCache(true); - if (panelBio.stndItem()) { + if (bio.panel.stndItem()) { this.art.done = false; if (!this.art.images.length) { this.art.allFilesLength = 0; this.art.ix = 0; } - if (pptBio.artistView && pptBio.cycPhoto) this.getArtImg(); + if (bioSet.artistView && bioSet.cycPhoto) this.getArtImg(); else this.getFbImg(); - } else this.getItem(panelBio.art.ix, panelBio.alb.ix, true); + } else this.getItem(bio.panel.art.ix, bio.panel.alb.ix, true); } checkArr(info) { - if (panelBio.block()) return; - if (this.art.images.length < 2 || !pptBio.artistView || pptBio.text_only || !pptBio.cycPhoto) return; + if (bio.panel.block()) return; + if (this.art.images.length < 2 || !bioSet.artistView || bioSet.text_only || !bioSet.cycPhoto) return; if (!this.art.validate.includes(info[0])) this.art.validate.push(info[0]); this.art.displayedOtherPanel = info[1]; if (!this.id.w1) this.id.w1 = info[0]; @@ -464,14 +464,14 @@ class ImagesBio { let h = false; pths.some(v => { if (h) return true; - const ph = !cusCovPaths ? v + fn : $Bio.eval(v + fn, panelBio.id.focus); + const ph = !cusCovPaths ? v + fn : $Bio.eval(v + fn, bio.panel.id.focus); this.ext.some(w => { const ep = ph + w; if ($Bio.file(ep)) { h = true; switch (type) { case 0: - this.loadImg(cov, ep, true, this.cov.ix); + this.loadImg(bioCov, ep, true, this.cov.ix); return true; case 1: this.cov.blur = gdi.Image(ep); @@ -498,11 +498,11 @@ class ImagesBio { this.art.validate = []; this.art.checkNo = 0; } - art.cache = {}; + bioArt.cache = {}; } clearCovCache() { - cov.cache = {}; + bioCov.cache = {}; } clearCache() { @@ -511,24 +511,24 @@ class ImagesBio { } covBlur() { - return pptBio.covBlur && uiBio.style.isBlur && (pptBio.artistView || this.cov.cycle || pptBio.text_only || panelBio.alb.ix); + return bioSet.covBlur && bio.ui.style.isBlur && (bioSet.artistView || this.cov.cycle || bioSet.text_only || bio.panel.alb.ix); } createImages() { - const bg = this.isType('AnyBorShadow') || !uiBio.blur.dark && !uiBio.blur.light; + const bg = this.isType('AnyBorShadow') || !bio.ui.blur.dark && !bio.ui.blur.light; const cc = StringFormat(1, 1); - const font1 = gdi.Font(fontDefault, 184, 1); - const font2 = gdi.Font(fontDefault, 80, 1); - const font3 = gdi.Font(fontDefault, 200, 1); - const font4 = gdi.Font(fontDefault, 90, 1); - const tcol = !bg ? uiBio.col.text : uiBio.col.noPhotoStubText; + const font1 = gdi.Font(grFont.fontDefault, 184, 1); + const font2 = gdi.Font(grFont.fontDefault, 80, 1); + const font3 = gdi.Font(grFont.fontDefault, 200, 1); + const font4 = gdi.Font(grFont.fontDefault, 90, 1); + const tcol = !bg ? bio.ui.col.text : bio.ui.col.noPhotoStubText; const sz = 600; for (let i = 0; i < 3; i++) { this.stub.default[i] = $Bio.gr(sz, sz, true, g => { g.SetSmoothingMode(2); if (bg) { g.FillSolidRect(0, 0, sz, sz, tcol); - g.FillSolidRect(-1, 0, sz + 5, sz, uiBio.col.noPhotoStubBg); + g.FillSolidRect(-1, 0, sz + 5, sz, bio.ui.col.noPhotoStubBg); } g.SetTextRenderingHint(3); g.DrawString('NO', i == 2 ? font3 : font1, tcol, 0, 0, sz, sz * 275 / 500, cc); @@ -549,59 +549,59 @@ class ImagesBio { } draw(gr) { - if (syncBio.get && syncBio.img) { - syncBio.image(syncBio.img.image, syncBio.img.id); - syncBio.get = false; + if (bioSync.get && bioSync.img) { + bioSync.image(bioSync.img.image, bioSync.img.id); + bioSync.get = false; } - if (pptBio.text_only && !uiBio.style.isBlur) { - if (pptBio.showFilmStrip && this.get) this.getImgFallback(); + if (bioSet.text_only && !bio.ui.style.isBlur) { + if (bioSet.showFilmStrip && this.get) this.getImgFallback(); return; } - if (uiBio.style.isBlur) { + if (bio.ui.style.isBlur) { const bImg = !this.themed ? this.blur : this.themed; - if (bImg) gr.DrawImage(bImg, uiBio.x, uiBio.y, panelBio.w, panelBio.h, 0, 0, bImg.Width, bImg.Height); + if (bImg) gr.DrawImage(bImg, bio.ui.x, bio.ui.y, bio.panel.w, bio.panel.h, 0, 0, bImg.Width, bImg.Height); } if (this.get) return this.getImgFallback(); - if (!pptBio.text_only && this.cur) { - gr.DrawImage(this.cur, this.x - (pptBio.img_only ? SCALE(2) : 0), pptBio.img_only ? geo.topMenuHeight + uiBio.h * 0.5 - this.cur.Height * 0.5 : geo.topMenuHeight + this.y, this.cur.Width + (pptBio.img_only ? SCALE(4) : 0), this.cur.Height, 0, 0, this.cur.Width, this.cur.Height, 0, this.style.alpha); + if (!bioSet.text_only && this.cur) { + gr.DrawImage(this.cur, this.x - (bioSet.img_only ? SCALE(2) : 0), bioSet.img_only ? grm.ui.topMenuHeight + bio.ui.h * 0.5 - this.cur.Height * 0.5 : grm.ui.topMenuHeight + this.y, this.cur.Width + (bioSet.img_only ? SCALE(4) : 0), this.cur.Height, 0, 0, this.cur.Width, this.cur.Height, 0, this.style.alpha); } } fadeMask(image, x, y, w, h) { - const xl = Math.max(0, panelBio.tbox.l - x); - let f = Math.min(w, panelBio.tbox.l - x + panelBio.tbox.w); + const xl = Math.max(0, bio.panel.tbox.l - x); + let f = Math.min(w, bio.panel.tbox.l - x + bio.panel.tbox.w); this.refl.adjust = false; if (xl >= f) return image; - const wl = f - xl; - const yl = Math.max(0, panelBio.tbox.t - y); - f = Math.min(h, panelBio.tbox.t - y + uiBio.y + panelBio.tbox.h); + // const wl = f - xl; + const yl = Math.max(0, bio.panel.tbox.t - y); + f = Math.min(h, bio.panel.tbox.t - y + bio.ui.y + bio.panel.tbox.h); if (yl >= f) return image; const hl = f - yl; if (!this.mask.fade || this.mask.reset) { - const km = uiBio.overlay.gradient != -1 && panelBio.img.t <= panelBio.text.t - uiBio.heading.h ? uiBio.overlay.strength / 500 + uiBio.overlay.gradient / 10 : 0; + const km = bio.ui.overlay.gradient != -1 && bio.panel.img.t <= bio.panel.text.t - bio.ui.heading.h ? bio.ui.overlay.strength / 500 + bio.ui.overlay.gradient / 10 : 0; this.mask.fade = $Bio.gr(500, 500, true, g => { for (let k = 0; k < 500; k++) { - const c = 255 - $Bio.clamp(uiBio.overlay.strength - k * km, 0, 255); + const c = 255 - $Bio.clamp(bio.ui.overlay.strength - k * km, 0, 255); g.FillSolidRect(0, k, 500, 1, RGB(c, c, c)); } }); this.mask.reset = false; - if (pptBio.style == 4 && panelBio.style.showFilmStrip) { - const rotate = [2, 3, 0, 1][pptBio.filmStripPos]; + if (bioSet.style == 4 && bio.panel.style.showFilmStrip) { + const rotate = [2, 3, 0, 1][bioSet.filmStripPos]; this.mask.fade.RotateFlip(rotate); } } - const mask = $Bio.gr(w, h, true, g => g.DrawImage(this.mask.fade, uiBio.x, yl, uiBio.w, hl, 0, 0, this.mask.fade.Width, this.mask.fade.Height)); + const mask = $Bio.gr(w, h, true, g => g.DrawImage(this.mask.fade, bio.ui.x, yl, bio.ui.w, hl, 0, 0, this.mask.fade.Width, this.mask.fade.Height)); image.ApplyMask(mask); } filmOK(newArr) { - return newArr && this.art.list.length && pptBio.showFilmStrip && filmStrip.scroll.pos.art[this.artist] && filmStrip.scroll.pos.art[this.artist].arr && filmStrip.scroll.pos.art[this.artist].arr.length; + return newArr && this.art.list.length && bioSet.showFilmStrip && bio.filmStrip.scroll.pos.art[this.artist] && bio.filmStrip.scroll.pos.art[this.artist].arr && bio.filmStrip.scroll.pos.art[this.artist].arr.length; } forceStnd() { - const n = pptBio.artistView ? 'bio' : 'rev'; - return !pptBio.sourceAll && txt[n].loaded.txt && (txt.reader[n].props || txt.reader[n].lyrics) && (pptBio.artistView && panelBio.art.ix || !pptBio.artistView && panelBio.alb.ix); + const n = bioSet.artistView ? 'bio' : 'rev'; + return !bioSet.sourceAll && bio.txt[n].loaded.txt && (bio.txt.reader[n].props || bio.txt.reader[n].lyrics) && (bioSet.artistView && bio.panel.art.ix || !bioSet.artistView && bio.panel.alb.ix); } format(image, n, type, w, h, caller, o, blur, border, fade, reflection) { @@ -649,8 +649,8 @@ class ImagesBio { if (border) image = this.getBorder(image, this.im.w, this.im.h, this.bor.w1, this.bor.w2); if (fade) this.fadeMask(image, this.im.l, this.im.t, image.Width, image.Height); - o.x = o.counter_x = seeker.counter.x = this.x = this.im.l; - o.y = o.counter_y = seeker.counter.y = this.y = this.im.t; + o.x = o.counter_x = bio.seeker.counter.x = this.x = this.im.l; + o.y = o.counter_y = bio.seeker.counter.y = this.y = this.im.t; o.w = this.im.w; o.h = this.im.h; if (reflection) image = this.reflImage(image, this.im.l, this.im.t, image.Width, image.Height, o); @@ -663,14 +663,14 @@ class ImagesBio { fresh() { this.counter++; - if (this.counter < pptBio.cycTimePic || panelBio.id.lyricsSource && lyricsBio.display() && lyricsBio.scroll) return; + if (this.counter < bioSet.cycTimePic || bio.panel.id.lyricsSource && bio.lyrics.display() && bio.lyrics.scroll) return; this.counter = 0; - if (panelBio.block() || !pptBio.cycPic || pptBio.text_only || seeker.dn || panelBio.zoom()) return; - if (pptBio.artistView) { - if (this.art.images.length < 2 || Date.now() - this.timeStamp.photo < this.style.delay || !pptBio.cycPhoto) return; + if (bio.panel.block() || !bioSet.cycPic || bioSet.text_only || bio.seeker.dn || bio.panel.zoom()) return; + if (bioSet.artistView) { + if (this.art.images.length < 2 || Date.now() - this.timeStamp.photo < this.style.delay || !bioSet.cycPhoto) return; this.changePhoto(1); } else if (this.cov.cycle) { - if (this.cov.images.length < 2 || Date.now() - this.timeStamp.cov < this.style.delay || panelBio.alb.ix) return; + if (this.cov.images.length < 2 || Date.now() - this.timeStamp.cov < this.style.delay || bio.panel.alb.ix) return; this.changeCov(1); } } @@ -699,18 +699,18 @@ class ImagesBio { let newArr = false; if (!this.art.images.length) { newArr = true; - art.cache = {}; + bioArt.cache = {}; } this.art.allFilesLength = allFiles.length; this.removed = 0; this.art.list = allFiles.filter(this.images.bind(this)); if (this.filmOK(newArr)) { - if ($Bio.equal(this.art.list, filmStrip.scroll.pos.art[this.artist].images)) { - this.art.images = filmStrip.scroll.pos.art[this.artist].arr; - this.art.ix = filmStrip.scroll.pos.art[this.artist].ix || 0; + if ($Bio.equal(this.art.list, bio.filmStrip.scroll.pos.art[this.artist].images)) { + this.art.images = bio.filmStrip.scroll.pos.art[this.artist].arr; + this.art.ix = bio.filmStrip.scroll.pos.art[this.artist].ix || 0; return; } - } else filmStrip.logScrollPos(this.art.list); + } else bio.filmStrip.logScrollPos(this.art.list); let arr = this.art.list.slice(); if (this.filter.size) arr = arr.filter(this.sizeFilter.bind(this)); this.art.images = this.art.images.concat(arr); @@ -718,21 +718,21 @@ class ImagesBio { this.art.images = this.uniq(this.art.images); if (newArr) this.art.images = $Bio.shuffle(this.art.images); } - if (!newArr) seeker.upd(); - filmStrip.check(newArr); + if (!newArr) bio.seeker.upd(); + bio.filmStrip.check(newArr); } getArtImg(update, bypass) { - if (!pptBio.artistView || pptBio.text_only && !uiBio.style.isBlur && !pptBio.showFilmStrip) return; - if (!bypass && (panelBio.id.lyricsSource || panelBio.id.nowplayingSource || panelBio.id.propsSource)) { - this.getItem(panelBio.art.ix, panelBio.alb.ix); + if (!bioSet.artistView || bioSet.text_only && !bio.ui.style.isBlur && !bioSet.showFilmStrip) return; + if (!bypass && (bio.panel.id.lyricsSource || bio.panel.id.nowplayingSource || bio.panel.id.propsSource)) { + this.getItem(bio.panel.art.ix, bio.panel.alb.ix); this.init = false; } if (!this.art.done || update) { this.art.done = true; if (this.artist) this.getArtImages(); } - this.setCheckArr(pptBio.cycPhoto ? this.art.images[this.art.ix] : null); + this.setCheckArr(bioSet.cycPhoto ? this.art.images[this.art.ix] : null); this.loadArtImage(); } @@ -750,10 +750,10 @@ class ImagesBio { const bor_img = $Bio.gr(Math.floor(w + bor_w2 + imgb), Math.floor(h + bor_w2 + imgb), true, g => { if (this.style.border > 1 && !this.style.reflection) g.DrawImage(sh_img, 0, 0, Math.floor(w + bor_w2 + imgb), Math.floor(h + bor_w2 + imgb), 0, 0, sh_img.Width, sh_img.Height); if (this.style.border == 1 || this.style.border == 3) { - if (!this.style.circular) g.FillSolidRect(0, 0, w + bor_w2, h + bor_w2, !pptBio.highlightImgBor ? RGB(255, 255, 255) : uiBio.col.text_h); + if (!this.style.circular) g.FillSolidRect(0, 0, w + bor_w2, h + bor_w2, !bioSet.highlightImgBor ? RGB(255, 255, 255) : bio.ui.col.text_h); else { g.SetSmoothingMode(2); - g.FillEllipse(0, 0, w + bor_w2, h + bor_w2, !pptBio.highlightImgBor ? RGB(255, 255, 255) : uiBio.col.text_h); + g.FillEllipse(0, 0, w + bor_w2, h + bor_w2, !bioSet.highlightImgBor ? RGB(255, 255, 255) : bio.ui.col.text_h); } } g.DrawImage(image, bor_w1, bor_w1, w, h, 0, 0, image.Width, image.Height); @@ -763,8 +763,8 @@ class ImagesBio { } getCallerId(key) { - const a = art.cache[key]; - const c = cov.cache[key]; + const a = bioArt.cache[key]; + const c = bioCov.cache[key]; return { art_id: a && a.id, cov_id: c && c.id @@ -772,19 +772,19 @@ class ImagesBio { } getCovImages() { - if (pptBio.artistView || !this.cov.cycle || panelBio.alb.ix) return false; - if (!panelBio.lock) this.setAlbID(); + if (bioSet.artistView || !this.cov.cycle || bio.panel.alb.ix) return false; + if (!bio.panel.lock) this.setAlbID(); const new_album = this.id.albCyc != this.id.curAlbCyc || !this.id.albCyc; - if (pptBio.loadCovFolder && !panelBio.lock) this.cov.folder = panelBio.cleanPth(cfg.albCovFolder, panelBio.id.focus); + if (bioSet.loadCovFolder && !bio.panel.lock) this.cov.folder = bio.panel.cleanPth(bioCfg.albCovFolder, bio.panel.id.focus); if (new_album) { this.clearCovCache(); this.cov.counter = 0; this.cov.list = []; this.cov.images = []; - cov_scroller.reset(); - filmStrip.scroll.pos.cov = {}; + bio.cov_scroller.reset(); + bio.filmStrip.scroll.pos.cov = {}; this.cov.ix = this.cov.cycle_ix = 0; - if (pptBio.loadCovFolder) { + if (bioSet.loadCovFolder) { this.cov.images = this.cov.folder ? utils.Glob(`${this.cov.folder}*`) : []; this.removed = 0; this.cov.images = this.cov.images.filter(this.cycImages.bind(this)); @@ -799,71 +799,71 @@ class ImagesBio { } this.cov.list = this.cov.list.filter(Boolean); this.cov.images = this.cov.list.map(v => v.pth); - filmStrip.check(); + bio.filmStrip.check(); } - if (pptBio.loadCovAllFb) { - const handle = $Bio.handle(panelBio.id.focus); + if (bioSet.loadCovAllFb) { + const handle = $Bio.handle(bio.panel.id.focus); if (handle) this.cov.selFiltered.forEach(v => this.getImg(handle, v, false)); - else if (!pptBio.loadCovFolder || !this.cov.images.length) return false; + else if (!bioSet.loadCovFolder || !this.cov.images.length) return false; } } - if (!new_album || !pptBio.loadCovAllFb) { + if (!new_album || !bioSet.loadCovAllFb) { const key = this.cov.images[this.cov.ix]; if (!key) return false; - this.loadImg(cov, key, true, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); + this.loadImg(bioCov, key, true, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); } return true; } getFbImg() { - if (pptBio.artistView && this.art.images.length && pptBio.cycPhoto) return; + if (bioSet.artistView && this.art.images.length && bioSet.cycPhoto) return; const forceStnd = this.forceStnd(); - this.cov.ix = this.cov.cycle && !panelBio.alb.ix ? this.cov.cycle_ix : panelBio.alb.ix + 1000000; + this.cov.ix = this.cov.cycle && !bio.panel.alb.ix ? this.cov.cycle_ix : bio.panel.alb.ix + 1000000; this.blurCheck(); if (this.getCovImages()) return; - if (!forceStnd && (panelBio.alb.ix && panelBio.alb.ix < panelBio.alb.list.length && !pptBio.artistView)) { // !stndAlb - const a = panelBio.alb.list[panelBio.alb.ix].artist; - const l = panelBio.alb.list[panelBio.alb.ix].album; - const l_handle = libBio.inLibrary(2, a, l); + if (!forceStnd && (bio.panel.alb.ix && bio.panel.alb.ix < bio.panel.alb.list.length && !bioSet.artistView)) { // !stndAlb + const a = bio.panel.alb.list[bio.panel.alb.ix].artist; + const l = bio.panel.alb.list[bio.panel.alb.ix].album; + const l_handle = bio.lib.inLibrary(2, a, l); if (l_handle) { // check local this.getImg(l_handle, 0, false); return; } else { - const pth = panelBio.getPth('img', panelBio.id.focus, a, l, '', cfg.supCache); + const pth = bio.panel.getPth('img', bio.panel.id.focus, a, l, '', bioCfg.supCache); if (this.chkPths(pth.pe, pth.fe, 0)) return; - if (pth.fe != this.cov.done && cfg.dlRevImg) { - const pth_cov = pth.pe[!cfg.supCache ? 0 : 1]; + if (pth.fe != this.cov.done && bioCfg.dlRevImg) { + const pth_cov = pth.pe[!bioCfg.supCache ? 0 : 1]; const fn_cov = pth_cov + pth.fe; - if ($Bio.server) serverBio.getRevImg(a, l, pth_cov, fn_cov, false); + if ($Bio.server) bio.server.getRevImg(a, l, pth_cov, fn_cov, false); else window.NotifyOthers('bio_getRevImg', [a, l, pth_cov, fn_cov]); this.cov.done = pth.fe; } - this.setStub(cov, this.stub[0].path, false, 0, this.stub[0].user); + this.setStub(bioCov, this.stub[0].path, false, 0, this.stub[0].user); return; } } - if ((forceStnd || !panelBio.alb.ix) && cfg.cusCov && !pptBio.artistView && !pptBio.covType) { - if (this.chkPths(cfg.cusCovPaths, '', 0, true)) return; + if ((forceStnd || !bio.panel.alb.ix) && bioCfg.cusCov && !bioSet.artistView && !bioSet.covType) { + if (this.chkPths(bioCfg.cusCovPaths, '', 0, true)) return; } - if (!forceStnd && (panelBio.art.ix && panelBio.art.ix < panelBio.art.list.length && pptBio.artistView)) { // !stndBio - const a_handle = libBio.inLibrary(3, this.artist); + if (!forceStnd && (bio.panel.art.ix && bio.panel.art.ix < bio.panel.art.list.length && bioSet.artistView)) { // !stndBio + const a_handle = bio.lib.inLibrary(3, this.artist); if (a_handle) { this.getImg(a_handle, 4, false); return; } - this.setStub(art, this.stub[4].path, false, 1, this.stub[4].user); + this.setStub(bioArt, this.stub[4].path, false, 1, this.stub[4].user); return; } // stndAlb - const handle = $Bio.handle(panelBio.id.focus); + const handle = $Bio.handle(bio.panel.id.focus); if (handle) { - let id = pptBio.artistView ? 4 : pptBio.covType; - if (!pptBio.loadCovAllFb || forceStnd || pptBio.artistView) this.getImg(handle, id, this.stub[id].panel ? false : !(!pptBio.covType || pptBio.artistView)); + let id = bioSet.artistView ? 4 : bioSet.covType; + if (!bioSet.loadCovAllFb || forceStnd || bioSet.artistView) this.getImg(handle, id, this.stub[id].panel ? false : !(!bioSet.covType || bioSet.artistView)); else { id = this.cov.selFiltered[0]; let image = null; - if (cov.cacheHit(this.stub[id].path)) return; + if (bioCov.cacheHit(this.stub[id].path)) return; if (this.stub[id].user) { image = this.stub[id].user; if (image) this.cache().cacheIt(image, this.stub[id].path); @@ -877,9 +877,9 @@ class ImagesBio { } getImages(force) { - if (pptBio.text_only && !uiBio.style.isBlur && !pptBio.showFilmStrip) return; - if (pptBio.artistView && pptBio.cycPhoto) { - if (!panelBio.art.ix) this.artistReset(force); + if (bioSet.text_only && !bio.ui.style.isBlur && !bioSet.showFilmStrip) return; + if (bioSet.artistView && bioSet.cycPhoto) { + if (!bio.panel.art.ix) this.artistReset(force); this.getArtImg(); } else this.getFbImg(); } @@ -890,8 +890,8 @@ class ImagesBio { } getImgFallback() { - if (txt.scrollbar_type().draw_timer) return; - if (!panelBio.updateNeeded()) { + if (bio.txt.scrollbar_type().draw_timer) return; + if (!bio.panel.updateNeeded()) { this.paint(); this.get = false; return; @@ -906,22 +906,22 @@ class ImagesBio { alb_ix = 0; } switch (true) { - case pptBio.artistView: { - if (pptBio.text_only && !uiBio.style.isBlur && !pptBio.showFilmStrip) return; + case bioSet.artistView: { + if (bioSet.text_only && !bio.ui.style.isBlur && !bioSet.showFilmStrip) return; this.cur_artist = this.artist; - const stndBio = panelBio.stnd(art_ix, panelBio.art.list); - this.artist = !stndBio ? panelBio.art.list[art_ix].name : !panelBio.lock ? name.artist(panelBio.id.focus) : panelBio.art.list.length ? panelBio.art.list[0].name : this.artist; + const stndBio = bio.panel.stnd(art_ix, bio.panel.art.list); + this.artist = !stndBio ? bio.panel.art.list[art_ix].name : !bio.panel.lock ? bio.name.artist(bio.panel.id.focus) : bio.panel.art.list.length ? bio.panel.art.list[0].name : this.artist; const new_artist = this.artist && this.artist != this.cur_artist || !this.artist || force; if (new_artist) { - menBio.counter.bio = 0; - art_scroller.reset(); + bio.men.counter.bio = 0; + bio.art_scroller.reset(); } - if (pptBio.cycPhoto) { + if (bioSet.cycPhoto) { if (new_artist) { this.counter = 0; - this.art.folder = panelBio.lock || panelBio.isRadio(panelBio.id.focus) ? panelBio.cleanPth(cfg.remap.foImgArt, panelBio.id.focus, 'remap', this.artist, '', 1) : stndBio ? panelBio.cleanPth(cfg.pth.foImgArt, panelBio.id.focus) : panelBio.cleanPth(cfg.remap.foImgArt, panelBio.id.focus, 'remap', this.artist, '', 1); + this.art.folder = bio.panel.lock || bio.panel.isRadio(bio.panel.id.focus) ? bio.panel.cleanPth(bioCfg.remap.foImgArt, bio.panel.id.focus, 'remap', this.artist, '', 1) : stndBio ? bio.panel.cleanPth(bioCfg.pth.foImgArt, bio.panel.id.focus) : bio.panel.cleanPth(bioCfg.remap.foImgArt, bio.panel.id.focus, 'remap', this.artist, '', 1); this.art.folderSup = ''; - if (!stndBio && cfg.supCache && !$Bio.folder(this.art.folder)) this.art.folderSup = panelBio.cleanPth(cfg.sup.foImgArt, panelBio.id.focus, 'remap', this.artist, '', 1); + if (!stndBio && bioCfg.supCache && !$Bio.folder(this.art.folder)) this.art.folderSup = bio.panel.cleanPth(bioCfg.sup.foImgArt, bio.panel.id.focus, 'remap', this.artist, '', 1); this.clearArtCache(true); this.art.done = false; if (!this.art.images.length) this.art.allFilesLength = 0; @@ -932,18 +932,18 @@ class ImagesBio { this.get = false; break; } - case !pptBio.artistView: { - const stndAlb = !alb_ix || alb_ix + 1 > panelBio.alb.list.length; + case !bioSet.artistView: { + const stndAlb = !alb_ix || alb_ix + 1 > bio.panel.alb.list.length; if (stndAlb) this.resetCounters(); - else if (!panelBio.lock) { + else if (!bio.panel.lock) { this.id.curAlbum = this.id.album; - this.id.album = (!panelBio.art.ix ? this.artist : panelBio.art.list[0].name) + panelBio.alb.list[alb_ix].name; + this.id.album = (!bio.panel.art.ix ? this.artist : bio.panel.art.list[0].name) + bio.panel.alb.list[alb_ix].name; if (this.id.album != this.id.curAlbum || force) { this.counter = 0; - menBio.counter.rev = 0; + bio.men.counter.rev = 0; } } - txt.rev.lookUp = true; + bio.txt.rev.lookUp = true; this.getFbImg(); this.get = false; break; @@ -952,8 +952,8 @@ class ImagesBio { } getOrientation() { - this.style.horizontal = (pptBio.style == 0 || pptBio.style == 2 || pptBio.style > 3) && !pptBio.img_only; - this.style.vertical = (pptBio.style == 1 || pptBio.style == 3 || pptBio.style > 3 && !pptBio.alignAuto) && !pptBio.img_only; + this.style.horizontal = (bioSet.style == 0 || bioSet.style == 2 || bioSet.style > 3) && !bioSet.img_only; + this.style.vertical = (bioSet.style == 1 || bioSet.style == 3 || bioSet.style > 3 && !bioSet.alignAuto) && !bioSet.img_only; this.style.circular = this.isType('Circ'); this.style.reflection = this.isType('Refl'); } @@ -966,8 +966,8 @@ class ImagesBio { this.im.h = Math.round(image.Height * sc); this.im.t = Math.round((this.nh - this.im.h) / 2 + this.im.t); } else { - this.im.t = panelBio.img.t; - this.nh = Math.max(panelBio.h - panelBio.img.t - panelBio.img.b - this.bor.w2, 10); + this.im.t = bio.panel.img.t; + this.nh = Math.max(bio.panel.h - bio.panel.img.t - bio.panel.img.b - this.bor.w2, 10); } } @@ -975,20 +975,20 @@ class ImagesBio { const rc = () => Math.floor(Math.random() * 256); let c = [rc(), rc(), rc()]; while (!this.isColOk(c)) c = [rc(), rc(), rc()]; - return $Bio.RGBAtoRGB(RGBA(c[0], c[1], c[2], Math.min(80 / uiBio.blur.alpha, 255)), RGB(0, 0, 0)); + return $Bio.RGBAtoRGB(RGBA(c[0], c[1], c[2], Math.min(80 / bio.ui.blur.alpha, 255)), RGB(0, 0, 0)); } grab(force) { - if (panelBio.block()) return this.get = true; + if (bio.panel.block()) return this.get = true; this.getArtImg(true); if (force) this.getFbImg(); } images(v) { if (!$Bio.file(v)) return false; - if (this.art.cusPhotoLocation) return /(?:jpe?g|png|webp|gif|bmp)$/i.test(fsoBio.GetExtensionName(v)); + if (this.art.cusPhotoLocation) return /(?:jpe?g|png|webp|gif|bmp)$/i.test(bioFSO.GetExtensionName(v)); const fileSize = utils.GetFileSize(v); - return (name.isLfmImg(fsoBio.GetFileName(v), this.artist) || !pptBio.imgFilterLfm && /(?:jpe?g|png|webp|gif|bmp)$/i.test(fsoBio.GetExtensionName(v)) && !/ - /.test(fsoBio.GetBaseName(v))) && !this.exclArr.includes(fileSize) && !this.blackListed(v); + return (bio.name.isLfmImg(bioFSO.GetFileName(v), this.artist) || !bioSet.imgFilterLfm && /(?:jpe?g|png|webp|gif|bmp)$/i.test(bioFSO.GetExtensionName(v)) && !/ - /.test(bioFSO.GetBaseName(v))) && !this.exclArr.includes(fileSize) && !this.blackListed(v); } isColOk(c) { @@ -1003,10 +1003,10 @@ class ImagesBio { isEmbedded(type, ix) { // also identifies yt etc switch (type) { case 'stnd': - return pptBio.artistView || !this.cov.list[ix] ? null : this.cov.list[ix].embedded; + return bioSet.artistView || !this.cov.list[ix] ? null : this.cov.list[ix].embedded; case 'thumb': if (this.cache().embedded) return this.cache().embedded; - else if (pptBio.artistView || !this.cov.list[ix]) return null; + else if (bioSet.artistView || !this.cov.list[ix]) return null; else return this.cov.list[ix].embedded; } } @@ -1025,50 +1025,50 @@ class ImagesBio { freqTot += v.freq; }); const avgCol = [$Bio.clamp(Math.round(Math.sqrt(rTot / freqTot)), 0, 255), $Bio.clamp(Math.round(Math.sqrt(gTot / freqTot)), 0, 255), $Bio.clamp(Math.round(Math.sqrt(bTot / freqTot)), 0, 255)]; - return !!uiBio.isLightCol(avgCol, true); + return !!bio.ui.isLightCol(avgCol, true); } isType(n, image) { switch (n) { // init before createImages & this.setCrop case 'AnyBorShadow': - return ['artBorderImgOnly', 'artShadowImgOnly', 'artBorderDual', 'artShadowDual', 'covBorderImgOnly', 'covShadowImgOnly', 'covBorderDual', 'covShadowDual'].some(v => pptBio[v]); + return ['artBorderImgOnly', 'artShadowImgOnly', 'artBorderDual', 'artShadowDual', 'covBorderImgOnly', 'covShadowImgOnly', 'covBorderDual', 'covShadowDual'].some(v => bioSet[v]); case 'Blur': - return uiBio.style.isBlur && !(pptBio.img_only && this.style.crop && this.style.border < 2) ? image.Clone(0, 0, image.Width, image.Height) : null; + return bio.ui.style.isBlur && !(bioSet.img_only && this.style.crop && this.style.border < 2) ? image.Clone(0, 0, image.Width, image.Height) : null; case 'AnyBor': - return ['artBorderImgOnly', 'artBorderDual', 'covBorderImgOnly', 'covBorderDual'].some(v => pptBio[v]); + return ['artBorderImgOnly', 'artBorderDual', 'covBorderImgOnly', 'covBorderDual'].some(v => bioSet[v]); case 'Fade': - return (!pptBio.typeOverlay || pptBio.style == 4 && !pptBio.typeOverlay) && pptBio.style > 3 && !pptBio.img_only; + return (!bioSet.typeOverlay || bioSet.style == 4 && !bioSet.typeOverlay) && bioSet.style > 3 && !bioSet.img_only; case 'Overlay': - return pptBio.style > 3 && pptBio.alignAuto && !pptBio.img_only; + return bioSet.style > 3 && bioSet.alignAuto && !bioSet.img_only; case 'Circ': - if (pptBio.style == 4) return false; - switch (pptBio.artistView) { + if (bioSet.style == 4) return false; + switch (bioSet.artistView) { case true: - return !pptBio.img_only ? pptBio.artStyleDual == 2 : pptBio.artStyleImgOnly == 2; + return !bioSet.img_only ? bioSet.artStyleDual == 2 : bioSet.artStyleImgOnly == 2; case false: - return !pptBio.img_only ? pptBio.covStyleDual == 2 : pptBio.covStyleImgOnly == 2; + return !bioSet.img_only ? bioSet.covStyleDual == 2 : bioSet.covStyleImgOnly == 2; } break; case 'Border': - switch (pptBio.artistView) { + switch (bioSet.artistView) { case true: - return !pptBio.img_only && pptBio.artBorderDual && pptBio.artShadowDual || pptBio.img_only && pptBio.artBorderImgOnly && pptBio.artShadowImgOnly ? 3 : !pptBio.img_only && pptBio.artShadowDual || pptBio.img_only && pptBio.artShadowImgOnly ? 2 : !pptBio.img_only && pptBio.artBorderDual || pptBio.img_only && pptBio.artBorderImgOnly ? 1 : 0; + return !bioSet.img_only && bioSet.artBorderDual && bioSet.artShadowDual || bioSet.img_only && bioSet.artBorderImgOnly && bioSet.artShadowImgOnly ? 3 : !bioSet.img_only && bioSet.artShadowDual || bioSet.img_only && bioSet.artShadowImgOnly ? 2 : !bioSet.img_only && bioSet.artBorderDual || bioSet.img_only && bioSet.artBorderImgOnly ? 1 : 0; case false: - return !pptBio.img_only && pptBio.covBorderDual && pptBio.covShadowDual || pptBio.img_only && pptBio.covBorderImgOnly && pptBio.covShadowImgOnly ? 3 : !pptBio.img_only && pptBio.covShadowDual || pptBio.img_only && pptBio.covShadowImgOnly ? 2 : !pptBio.img_only && pptBio.covBorderDual || pptBio.img_only && pptBio.covBorderImgOnly ? 1 : 0; + return !bioSet.img_only && bioSet.covBorderDual && bioSet.covShadowDual || bioSet.img_only && bioSet.covBorderImgOnly && bioSet.covShadowImgOnly ? 3 : !bioSet.img_only && bioSet.covShadowDual || bioSet.img_only && bioSet.covShadowImgOnly ? 2 : !bioSet.img_only && bioSet.covBorderDual || bioSet.img_only && bioSet.covBorderImgOnly ? 1 : 0; } break; default: - switch (pptBio.artistView) { + switch (bioSet.artistView) { case true: - return !pptBio.img_only ? pptBio[`art${n}Dual`] : pptBio[`art${n}ImgOnly`]; + return !bioSet.img_only ? bioSet[`art${n}Dual`] : bioSet[`art${n}ImgOnly`]; case false: - return !pptBio.img_only ? pptBio[`cov${n}Dual`] : pptBio[`cov${n}ImgOnly`]; + return !bioSet.img_only ? bioSet[`cov${n}Dual`] : bioSet[`cov${n}ImgOnly`]; } } } lbtn_dn(p_x) { - if (!pptBio.touchControl || panelBio.trace.text || this.dn) return; + if (!bioSet.touchControl || bio.panel.trace.text || this.dn) return; this.touch.dn = true; this.touch.start = p_x; } @@ -1088,31 +1088,31 @@ class ImagesBio { let a, l; switch (n) { case 0: // stndAlb !fbImg: chkCov save pths: if !found chk/save stubCovUser - a = name.albumArtist(panelBio.id.focus); - l = name.album(panelBio.id.focus); - if (this.chkPths([panelBio.getPth('cov', panelBio.id.focus).pth, panelBio.getPth('img', panelBio.id.focus, a, l).pth], '', 0)) return true; - if (cov.cacheHit(this.stub.cov.path)) return true; + a = bio.name.albumArtist(bio.panel.id.focus); + l = bio.name.album(bio.panel.id.focus); + if (this.chkPths([bio.panel.getPth('cov', bio.panel.id.focus).pth, bio.panel.getPth('img', bio.panel.id.focus, a, l).pth], '', 0)) return true; + if (bioCov.cacheHit(this.stub.cov.path)) return true; this.checkUserStub('cov', handle); return false; case 1: { // !stndAlb inLib !fbImg: chkCov save pths: if !found getRevImg else load stub || metadb - a = panelBio.alb.list[panelBio.alb.ix].artist; - l = panelBio.alb.list[panelBio.alb.ix].album; - const pth = panelBio.getPth('img', panelBio.id.focus, a, l, '', cfg.supCache); + a = bio.panel.alb.list[bio.panel.alb.ix].artist; + l = bio.panel.alb.list[bio.panel.alb.ix].album; + const pth = bio.panel.getPth('img', bio.panel.id.focus, a, l, '', bioCfg.supCache); if (this.chkPths(pth.pe, pth.fe, 0)) return; - if (pth.fe != this.cov.done && cfg.dlRevImg) { - const pth_cov = pth.pe[!cfg.supCache ? 0 : 1]; + if (pth.fe != this.cov.done && bioCfg.dlRevImg) { + const pth_cov = pth.pe[!bioCfg.supCache ? 0 : 1]; const fn_cov = pth_cov + pth.fe; - if ($Bio.server) serverBio.getRevImg(a, l, pth_cov, fn_cov, false); + if ($Bio.server) bio.server.getRevImg(a, l, pth_cov, fn_cov, false); else window.NotifyOthers('bio_getRevImg', [a, l, pth_cov, fn_cov]); this.cov.done = pth.fe; } - this.setStub(cov, this.stub[0].path, false, 0, this.stub[0].user); + this.setStub(bioCov, this.stub[0].path, false, 0, this.stub[0].user); } } } loadArtImage() { - if (this.art.images.length && pptBio.cycPhoto) this.loadImg(art, this.art.images[this.art.ix], true, this.art.ix); + if (this.art.images.length && bioSet.cycPhoto) this.loadImg(bioArt, this.art.images[this.art.ix], true, this.art.ix); else if (!this.init) this.getFbImg(); } @@ -1123,13 +1123,13 @@ class ImagesBio { const fileSize = utils.GetFileSize(image_path); if (this.exclArr.includes(fileSize)) image_path = ''; } - if (pptBio.loadCovAllFb) { + if (bioSet.loadCovAllFb) { if (this.cov.list.every(v => v.id !== art_id)) { this.cov.counter++; if (!art_id) { - let path = cfg.cusCov ? this.chkPths(cfg.cusCovPaths, '', 3, true) : ''; - if (image_path && !cfg.cusCov || !path) path = image_path; - if (!path) path = this.chkPths([panelBio.getPth('cov', panelBio.id.focus).pth, panelBio.getPth('img', panelBio.id.focus, name.albumArtist(panelBio.id.focus), name.album(panelBio.id.focus)).pth], '', 3); + let path = bioCfg.cusCov ? this.chkPths(bioCfg.cusCovPaths, '', 3, true) : ''; + if (image_path && !bioCfg.cusCov || !path) path = image_path; + if (!path) path = this.chkPths([bio.panel.getPth('cov', bio.panel.id.focus).pth, bio.panel.getPth('img', bio.panel.id.focus, bio.name.albumArtist(bio.panel.id.focus), bio.name.album(bio.panel.id.focus)).pth], '', 3); if (path) { const ln = this.cov.list.length; this.cov.list[ln] = {}; @@ -1152,15 +1152,15 @@ class ImagesBio { $Bio.sort(this.cov.list, 'id', 'num'); this.cov.list = this.uniqPth(this.cov.list); this.cov.images = this.cov.list.map(v => v.pth); - filmStrip.check(); + bio.filmStrip.check(); } } - if (!pptBio.artistView && !panelBio.alb.ix) { + if (!bioSet.artistView && !bio.panel.alb.ix) { const key = this.cov.images[this.cov.ix]; - if (this.cov.counter > (pptBio.loadCovAllFb ? imgBio.cov.selFiltered.length : 0) - 1) { - if (key) this.loadImg(cov, key, false, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); - seeker.metrics(this.style.circular, this.style.crop, this.style.horizontal, this.style.reflection, this.style.vertical); + if (this.cov.counter > (bioSet.loadCovAllFb ? bio.img.cov.selFiltered.length : 0) - 1) { + if (key) this.loadImg(bioCov, key, false, this.cov.ix, this.isEmbedded('stnd', this.cov.ix)); + bio.seeker.metrics(this.style.circular, this.style.crop, this.style.horizontal, this.style.reflection, this.style.vertical); if (!this.cov.list.length) return false; // load stub } return true; @@ -1174,7 +1174,7 @@ class ImagesBio { id }; gdi.LoadImageAsync(0, key); - } else cov.cacheIt(embeddedImg, key); + } else bioCov.cacheIt(embeddedImg, key); } loadStndCov(handle, art_id, image, image_path, embedded) { // stndAlb load fbImg else stub @@ -1183,16 +1183,16 @@ class ImagesBio { image_path = ''; } if (!image) { - if (pptBio.artistView) { - if (art.cacheHit(this.stub[art_id].path)) return; + if (bioSet.artistView) { + if (bioArt.cacheHit(this.stub[art_id].path)) return; this.checkUserStub('art', handle); if (this.stub[art_id].user) { image = this.stub[art_id].user; image_path = this.stub[art_id].path; } } else { - if (pptBio.loadCovAllFb) art_id = this.cov.selFiltered[0]; - if (cov.cacheHit(this.stub[art_id].path)) return; + if (bioSet.loadCovAllFb) art_id = this.cov.selFiltered[0]; + if (bioCov.cacheHit(this.stub[art_id].path)) return; this.checkUserStub('cov', handle); if (this.stub[art_id].user) { image = this.stub[art_id].user; @@ -1201,10 +1201,10 @@ class ImagesBio { } } if (!image) { - this.setStub(this.cache(), `stub${art_id}`, true, pptBio.artistView || art_id == 4 ? 1 : 0); + this.setStub(this.cache(), `stub${art_id}`, true, bioSet.artistView || art_id == 4 ? 1 : 0); return; } - if (!txt.rev.lookUp) this.clearCovCache(); + if (!bio.txt.rev.lookUp) this.clearCovCache(); this.cache().cacheIt(image, image_path + (!embedded ? '' : art_id), embedded); } @@ -1216,19 +1216,19 @@ class ImagesBio { metrics() { this.setAutoDisplayVariables(); this.getOrientation(); - seeker.metrics(this.style.circular, this.style.crop, this.style.horizontal, this.style.reflection, this.style.vertical); + bio.seeker.metrics(this.style.circular, this.style.crop, this.style.horizontal, this.style.reflection, this.style.vertical); } move(p_x, p_y) { if (this.touch.dn) { - if (!panelBio.imgBoxTrace(p_x, p_y)) return; + if (!bio.panel.imgBoxTrace(p_x, p_y)) return; this.touch.end = p_x; const x_delta = this.touch.end - this.touch.start; - if (x_delta > panelBio.style.imgSize / 5) { + if (x_delta > bio.panel.style.imgSize / 5) { this.wheel(1); this.touch.start = this.touch.end; } - if (x_delta < -panelBio.style.imgSize / 5) { + if (x_delta < -bio.panel.style.imgSize / 5) { this.wheel(-1); this.touch.start = this.touch.end; } @@ -1244,10 +1244,10 @@ class ImagesBio { const forceStnd = this.forceStnd(); if (!this.cur_handle || !this.cur_handle.Compare(handle) || image && this.cache().cacheHit(image_path + (!embedded ? '' : art_id))) return; if (!forceStnd && this.loadCycCov(handle, art_id, image, image_path)) return; - if (!forceStnd && panelBio.alb.ix && panelBio.alb.ix < panelBio.alb.list.length && !image && !pptBio.artistView) return this.loadAltCov(handle, 1); - if (!image && !pptBio.artistView && (!art_id || pptBio.loadCovAllFb) && (!panelBio.alb.ix || forceStnd)) { + if (!forceStnd && bio.panel.alb.ix && bio.panel.alb.ix < bio.panel.alb.list.length && !image && !bioSet.artistView) return this.loadAltCov(handle, 1); + if (!image && !bioSet.artistView && (!art_id || bioSet.loadCovAllFb) && (!bio.panel.alb.ix || forceStnd)) { if (this.loadAltCov(handle, 0)) return; - if (pptBio.loadCovAllFb) art_id = this.cov.selFiltered[0]; + if (bioSet.loadCovAllFb) art_id = this.cov.selFiltered[0]; if (this.stub[art_id].user) { image = this.stub[art_id].user; image_path = this.stub[art_id].path; @@ -1268,46 +1268,46 @@ class ImagesBio { this.processArtImg(image, image_path); } if (caller.cov_id === this.cov.ix) { - if (!txt.rev.lookUp && !this.cov.cycle) this.clearCovCache(); + if (!bio.txt.rev.lookUp && !this.cov.cycle) this.clearCovCache(); if (!image) return; - if (this.filter.size && this.cov.folderSameAsArt && this.cov.images.includes(image_path) && (!pptBio.imgFilterBothPx ? image.Width < this.filter.minPx && image.Height < this.filter.minPx : image.Width < this.filter.minPx || image.Height < this.filter.minPx) && this.cov.images.length > this.filter.minNo) { + if (this.filter.size && this.cov.folderSameAsArt && this.cov.images.includes(image_path) && (!bioSet.imgFilterBothPx ? image.Width < this.filter.minPx && image.Height < this.filter.minPx : image.Width < this.filter.minPx || image.Height < this.filter.minPx) && this.cov.images.length > this.filter.minNo) { this.cov.list.splice(this.cov.ix, 1); this.cov.images.splice(this.cov.ix, 1); - seeker.upd(false, false, true); - if (pptBio.showFilmStrip) filmStrip.trimCache(image_path); + bio.seeker.upd(false, false, true); + if (bioSet.showFilmStrip) bio.filmStrip.trimCache(image_path); this.changeCov(1); - filmStrip.check('imgUpd'); + bio.filmStrip.check('imgUpd'); return; } - cov.cacheIt(image, image_path); + bioCov.cacheIt(image, image_path); } } processArtImg(image, image_path) { const caller = this.getCallerId(image_path); if (caller.art_id === this.art.ix) { - if (this.filter.size && (!pptBio.imgFilterBothPx ? image.Width < this.filter.minPx && image.Height < this.filter.minPx : image.Width < this.filter.minPx || image.Height < this.filter.minPx) && this.art.images.length > this.filter.minNo) { + if (this.filter.size && (!bioSet.imgFilterBothPx ? image.Width < this.filter.minPx && image.Height < this.filter.minPx : image.Width < this.filter.minPx || image.Height < this.filter.minPx) && this.art.images.length > this.filter.minNo) { this.art.images.splice(this.art.ix, 1); - seeker.upd(false, false, true); - if (pptBio.showFilmStrip) filmStrip.trimCache(image_path); + bio.seeker.upd(false, false, true); + if (bioSet.showFilmStrip) bio.filmStrip.trimCache(image_path); this.changePhoto(1); - filmStrip.check('imgUpd'); + bio.filmStrip.check('imgUpd'); return; } - art.cacheIt(image, image_path); + bioArt.cacheIt(image, image_path); } } on_playback_new_track(force) { this.resetCounters(); - if (!panelBio.updateNeeded() && !force) return; - if (panelBio.block()) { + if (!bio.panel.updateNeeded() && !force) return; + if (bio.panel.block()) { this.get = true; - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); this.artistReset(); } else { - if (pptBio.artistView && pptBio.cycPhoto) { - filmStrip.logScrollPos(); + if (bioSet.artistView && bioSet.cycPhoto) { + bio.filmStrip.logScrollPos(); this.artistReset(); this.getArtImg(); } else this.getFbImg(); @@ -1316,14 +1316,14 @@ class ImagesBio { } on_size() { - if (pptBio.text_only) { + if (bioSet.text_only) { this.clearCovCache(); this.getFbImg(); } - if (pptBio.text_only && !uiBio.style.isBlur && !pptBio.showFilmStrip) return this.init = false; - filmStrip.logScrollPos(); + if (bioSet.text_only && !bio.ui.style.isBlur && !bioSet.showFilmStrip) return this.init = false; + bio.filmStrip.logScrollPos(); this.clearCache(); - if (pptBio.artistView) { + if (bioSet.artistView) { if (this.init) this.artistReset(); this.getArtImg(); } else { @@ -1334,33 +1334,33 @@ class ImagesBio { } } this.init = false; - if (pptBio.img_only) panelBio.getList(true, true); - butBio.refresh(true); + if (bioSet.img_only) bio.panel.getList(true, true); + bio.but.refresh(true); } paint() { - if (!pptBio.imgSmoothTrans) { + if (!bioSet.imgSmoothTrans) { this.style.alpha = 255; - txt.paint(); + bio.txt.paint(); return; } this.id.curImg = this.id.img; this.id.img = this.cur_pth(); this.style.alpha = this.id.curImg != this.id.img ? this.transition.level : 255; - timerBio.clear(timerBio.transition); - timerBio.transition.id = setInterval(() => { + bio.timer.clear(bio.timer.transition); + bio.timer.transition.id = setInterval(() => { this.style.alpha = Math.min(this.style.alpha *= this.transition.incr, 255); - txt.paint(); - if (this.style.alpha == 255) timerBio.clear(timerBio.transition); + bio.txt.paint(); + if (this.style.alpha == 255) bio.timer.clear(bio.timer.transition); }, 12); } pth() { const cur_pth = this.cur_pth(); return { - imgPth: (pptBio.text_only && !panelBio.style.showFilmStrip) || !$Bio.file(cur_pth) ? '' : cur_pth, - artist: this.artist || name.artist(panelBio.id.focus), - blk: name.isLfmImg(fsoBio.GetFileName(cur_pth)) + imgPth: (bioSet.text_only && !bio.panel.style.showFilmStrip) || !$Bio.file(cur_pth) ? '' : cur_pth, + artist: this.artist || bio.name.artist(bio.panel.id.focus), + blk: bio.name.isLfmImg(bioFSO.GetFileName(cur_pth)) }; } @@ -1372,7 +1372,7 @@ class ImagesBio { const type = this.style.circular ? 'circular' : !this.style.crop ? 'default' : 'crop'; switch (type) { case 'circular': - if (this.style.overlay) this.nh = Math.max(panelBio.h - panelBio.img.t - panelBio.img.b - this.bor.w2, 10); + if (this.style.overlay) this.nh = Math.max(bio.panel.h - bio.panel.img.t - bio.panel.img.b - this.bor.w2, 10); this.im.w = this.im.h = Math.min(this.nw, this.nh); break; case 'default': @@ -1381,25 +1381,25 @@ class ImagesBio { this.im.h = this.nh; break; case 'crop': - if (pptBio.style > 3 && !pptBio.img_only) this.nh = Math.max(panelBio.h - panelBio.img.t - panelBio.img.b - this.bor.w2, 10); + if (bioSet.style > 3 && !bioSet.img_only) this.nh = Math.max(bio.panel.h - bio.panel.img.t - bio.panel.img.b - this.bor.w2, 10); this.im.w = this.nw; this.im.h = this.nh; break; } - return this.format(image, n, type, this.im.w, this.im.h, 'img', o, !pptBio.themed ? this.style.blur : false, this.style.border, this.style.fade, this.style.reflection); + return this.format(image, n, type, this.im.w, this.im.h, 'img', o, !bioSet.themed ? this.style.blur : false, this.style.border, this.style.fade, this.style.reflection); } processSizeFilter() { - pptBio.imgFilterMinNo = Math.round(Math.max($Bio.value(pptBio.imgFilterMinNo, 3, 0), 1)); - pptBio.imgFilterMaxSz = Math.round(Math.max($Bio.value(pptBio.imgFilterMaxSz, 12000, 0), 50)); - pptBio.imgFilterMinSz = Math.round(Math.max($Bio.value(pptBio.imgFilterMinSz, 50, 0), 0)); - pptBio.imgFilterMinPx = Math.round(Math.max($Bio.value(pptBio.imgFilterMinPx, 500, 0), 0)); - this.filter.size = pptBio.imgFilterMaxSzEnabled || pptBio.imgFilterMinSzEnabled || pptBio.imgFilterMinPxEnabled; + bioSet.imgFilterMinNo = Math.round(Math.max($Bio.value(bioSet.imgFilterMinNo, 3, 0), 1)); + bioSet.imgFilterMaxSz = Math.round(Math.max($Bio.value(bioSet.imgFilterMaxSz, 12000, 0), 50)); + bioSet.imgFilterMinSz = Math.round(Math.max($Bio.value(bioSet.imgFilterMinSz, 50, 0), 0)); + bioSet.imgFilterMinPx = Math.round(Math.max($Bio.value(bioSet.imgFilterMinPx, 500, 0), 0)); + this.filter.size = bioSet.imgFilterMaxSzEnabled || bioSet.imgFilterMinSzEnabled || bioSet.imgFilterMinPxEnabled; if (this.filter.size) { - this.filter.minNo = pptBio.imgFilterMinNo; - this.filter.maxSz = pptBio.imgFilterMaxSzEnabled ? pptBio.imgFilterMaxSz * 1024 : Infinity; - this.filter.minSz = pptBio.imgFilterMinSzEnabled ? pptBio.imgFilterMinSz * 1024 : 0; - this.filter.minPx = pptBio.imgFilterMinPxEnabled ? pptBio.imgFilterMinPx : 0; + this.filter.minNo = bioSet.imgFilterMinNo; + this.filter.maxSz = bioSet.imgFilterMaxSzEnabled ? bioSet.imgFilterMaxSz * 1024 : Infinity; + this.filter.minSz = bioSet.imgFilterMinSzEnabled ? bioSet.imgFilterMinSz * 1024 : 0; + this.filter.minPx = bioSet.imgFilterMinPxEnabled ? bioSet.imgFilterMinPx : 0; } } @@ -1414,25 +1414,25 @@ class ImagesBio { }); } let r_mask; let refl; let reflImg; let ref_sz; let sw = 0; - if (!pptBio.imgReflType) { // auto - switch (pptBio.style) { + if (!bioSet.imgReflType) { // auto + switch (bioSet.style) { case 0: case 2: - sw = pptBio.alignH == 1 ? pptBio.style : pptBio.alignH == 0 ? 3 : 1; + sw = bioSet.alignH == 1 ? bioSet.style : bioSet.alignH == 0 ? 3 : 1; break; case 1: case 3: - sw = pptBio.alignV == 1 ? pptBio.style : pptBio.alignV == 0 ? 0 : 2; + sw = bioSet.alignV == 1 ? bioSet.style : bioSet.alignV == 0 ? 0 : 2; break; default: - sw = pptBio.alignH == 1 ? 0 : 3 - pptBio.alignH; + sw = bioSet.alignH == 1 ? 0 : 3 - bioSet.alignH; break; } - } else sw = [2, 3, 0, 1][pptBio.imgReflType - 1]; + } else sw = [2, 3, 0, 1][bioSet.imgReflType - 1]; this.refl.adjust = false; switch (sw) { case 0: // bottom - ref_sz = Math.round(Math.min(panelBio.h - y - h, image.Height * this.refl.size)); + ref_sz = Math.round(Math.min(bio.panel.h - y - h, image.Height * this.refl.size)); if (ref_sz <= 0) return image; refl = image.Clone(0, image.Height - ref_sz, image.Width, ref_sz); r_mask = this.mask.reflection.Clone(0, 0, this.mask.reflection.Width, this.mask.reflection.Height); @@ -1484,7 +1484,7 @@ class ImagesBio { o.h = ref_sz + h; break; case 3: // right - ref_sz = Math.round(Math.min(panelBio.w - x - w, image.Width * this.refl.size)); + ref_sz = Math.round(Math.min(bio.panel.w - x - w, image.Width * this.refl.size)); if (ref_sz <= 0) return image; refl = image.Clone(image.Width - ref_sz, 0, ref_sz, image.Height); r_mask = this.mask.reflection.Clone(0, 0, this.mask.reflection.Width, this.mask.reflection.Height); @@ -1505,66 +1505,66 @@ class ImagesBio { } resetCounters() { - if (panelBio.lock) return; + if (bio.panel.lock) return; this.id.curAlbCounter = this.id.albCounter; - this.id.albCounter = name.albID(panelBio.id.focus, 'full'); + this.id.albCounter = bio.name.albID(bio.panel.id.focus, 'full'); if (this.id.albCounter != this.id.curAlbCounter || !this.id.albCounter) { this.counter = 0; - menBio.counter.rev = 0; + bio.men.counter.rev = 0; } this.id.curArtCounter = this.id.artCounter; - this.id.artCounter = name.artist(panelBio.id.focus); + this.id.artCounter = bio.name.artist(bio.panel.id.focus); if (this.id.artCounter && this.id.artCounter != this.id.curArtCounter || !this.id.artCounter) { this.counter = 0; - menBio.counter.bio = 0; + bio.men.counter.bio = 0; } } resetTimestamps() { - if (!pptBio.cycPic) return; - pptBio.artistView ? this.timeStamp.photo = Date.now() : this.timeStamp.cov = Date.now(); + if (!bioSet.cycPic) return; + bioSet.artistView ? this.timeStamp.photo = Date.now() : this.timeStamp.cov = Date.now(); } setAlbID() { this.id.curAlbCyc = this.id.albCyc; - this.id.albCyc = name.albID(panelBio.id.focus, 'full'); + this.id.albCyc = bio.name.albID(bio.panel.id.focus, 'full'); } setAlignment() { - if (this.style.horizontal && pptBio.alignH != 1) { - if (pptBio.alignH == 2) this.im.l = Math.round(panelBio.w - panelBio.img.r - this.im.w - this.bor.w2); + if (this.style.horizontal && bioSet.alignH != 1) { + if (bioSet.alignH == 2) this.im.l = Math.round(bio.panel.w - bio.panel.img.r - this.im.w - this.bor.w2); } else this.im.l = Math.round((this.nw - this.im.w) / 2 + this.im.l); - if (this.style.vertical && pptBio.alignV != 1) { - if (pptBio.alignV == 2) this.im.t = Math.round(panelBio.h - panelBio.img.b - this.im.h - this.bor.w2); - } else if (pptBio.style < 4 || !pptBio.alignAuto || pptBio.img_only) this.im.t = Math.round((this.nh - this.im.h) / 2 + this.im.t); + if (this.style.vertical && bioSet.alignV != 1) { + if (bioSet.alignV == 2) this.im.t = Math.round(bio.panel.h - bio.panel.img.b - this.im.h - this.bor.w2); + } else if (bioSet.style < 4 || !bioSet.alignAuto || bioSet.img_only) this.im.t = Math.round((this.nh - this.im.h) / 2 + this.im.t); } setAutoDisplayVariables() { - if (!pptBio.img_only && pptBio.textAlign && pptBio.style < 4) { - if (pptBio.style == 0) { - panelBio.img.l = panelBio.heading.x; - panelBio.img.r = panelBio.w - panelBio.heading.x - panelBio.heading.w; + if (!bioSet.img_only && bioSet.textAlign && bioSet.style < 4) { + if (bioSet.style == 0) { + bio.panel.img.l = bio.panel.heading.x; + bio.panel.img.r = bio.panel.w - bio.panel.heading.x - bio.panel.heading.w; } - if (pptBio.style == 2) { - panelBio.img.l = !panelBio.style.fullWidthHeading ? panelBio.heading.x : panelBio.heading.x + (!pptBio.filmStripOverlay ? panelBio.filmStripSize.l : 0); - panelBio.img.r = !panelBio.style.fullWidthHeading ? panelBio.w - panelBio.heading.x - panelBio.heading.w : panelBio.w - panelBio.img.l - panelBio.heading.w + (!pptBio.filmStripOverlay ? panelBio.filmStripSize.r : 0); + if (bioSet.style == 2) { + bio.panel.img.l = !bio.panel.style.fullWidthHeading ? bio.panel.heading.x : bio.panel.heading.x + (!bioSet.filmStripOverlay ? bio.panel.filmStripSize.l : 0); + bio.panel.img.r = !bio.panel.style.fullWidthHeading ? bio.panel.w - bio.panel.heading.x - bio.panel.heading.w : bio.panel.w - bio.panel.img.l - bio.panel.heading.w + (!bioSet.filmStripOverlay ? bio.panel.filmStripSize.r : 0); } - if ((pptBio.style == 1 || pptBio.style == 3)) { - panelBio.img.t = !panelBio.style.fullWidthHeading ? pptBio.textT + (!pptBio.filmStripOverlay ? panelBio.filmStripSize.t : 0) : panelBio.text.t; - panelBio.img.b = !panelBio.style.fullWidthHeading ? pptBio.textB + (!pptBio.filmStripOverlay ? panelBio.filmStripSize.b : 0) : panelBio.h - panelBio.text.t - panelBio.text.h; + if ((bioSet.style == 1 || bioSet.style == 3)) { + bio.panel.img.t = !bio.panel.style.fullWidthHeading ? bioSet.textT + (!bioSet.filmStripOverlay ? bio.panel.filmStripSize.t : 0) : bio.panel.text.t; + bio.panel.img.b = !bio.panel.style.fullWidthHeading ? bioSet.textB + (!bioSet.filmStripOverlay ? bio.panel.filmStripSize.b : 0) : bio.panel.h - bio.panel.text.t - bio.panel.text.h; } } - if (!pptBio.img_only && pptBio.style == 0 && panelBio.style.fullWidthHeading) { - if (panelBio.filmStripSize.l && !pptBio.filmStripOverlay) panelBio.img.l = panelBio.bor.l; - if (panelBio.filmStripSize.r && !pptBio.filmStripOverlay) panelBio.img.r = panelBio.bor.r; + if (!bioSet.img_only && bioSet.style == 0 && bio.panel.style.fullWidthHeading) { + if (bio.panel.filmStripSize.l && !bioSet.filmStripOverlay) bio.panel.img.l = bio.panel.bor.l; + if (bio.panel.filmStripSize.r && !bioSet.filmStripOverlay) bio.panel.img.r = bio.panel.bor.r; } $Bio.key.forEach(v => { - this.im[v] = pptBio.img_only ? panelBio.bor[v] + (!this.style.crop ? (!pptBio.filmStripOverlay ? panelBio.filmStripSize[v] : 0) : 0) : panelBio.img[v]; + this.im[v] = bioSet.img_only ? bio.panel.bor[v] + (!this.style.crop ? (!bioSet.filmStripOverlay ? bio.panel.filmStripSize[v] : 0) : 0) : bio.panel.img[v]; }); - this.nw = !pptBio.img_only && (!pptBio.style || pptBio.style == 2 || pptBio.style > 3) ? panelBio.w - panelBio.img.l - panelBio.img.r : !pptBio.img_only ? panelBio.style.imgSize : panelBio.w - this.im.l - this.im.r; - this.nh = !pptBio.img_only && (pptBio.style == 1 || pptBio.style == 3 || pptBio.style > 3 && !pptBio.alignAuto) ? panelBio.h - panelBio.img.t - panelBio.img.b : !pptBio.img_only ? panelBio.style.imgSize : panelBio.h - this.im.t - this.im.b; + this.nw = !bioSet.img_only && (!bioSet.style || bioSet.style == 2 || bioSet.style > 3) ? bio.panel.w - bio.panel.img.l - bio.panel.img.r : !bioSet.img_only ? bio.panel.style.imgSize : bio.panel.w - this.im.l - this.im.r; + this.nh = !bioSet.img_only && (bioSet.style == 1 || bioSet.style == 3 || bioSet.style > 3 && !bioSet.alignAuto) ? bio.panel.h - bio.panel.img.t - bio.panel.img.b : !bioSet.img_only ? bio.panel.style.imgSize : bio.panel.h - this.im.t - this.im.b; this.style.border = this.isType('Border'); if (this.style.border == 1 || this.style.border == 3) { @@ -1582,25 +1582,25 @@ class ImagesBio { } setCrop(sz) { - const imgRefresh = pptBio.img_only; + const imgRefresh = bioSet.img_only; this.style.crop = this.isType('Circ') ? false : - pptBio.artistView && (pptBio.artStyleImgOnly == 1 && imgRefresh || (pptBio.artStyleDual == 1 || pptBio.style == 4) && !pptBio.img_only) || !pptBio.artistView && (pptBio.covStyleImgOnly == 1 && imgRefresh || (pptBio.covStyleDual == 1 || pptBio.style == 4) && !pptBio.img_only); - panelBio.setBorder(imgRefresh && this.style.crop, this.isType('Border'), this.isType('Refl')); + bioSet.artistView && (bioSet.artStyleImgOnly == 1 && imgRefresh || (bioSet.artStyleDual == 1 || bioSet.style == 4) && !bioSet.img_only) || !bioSet.artistView && (bioSet.covStyleImgOnly == 1 && imgRefresh || (bioSet.covStyleDual == 1 || bioSet.style == 4) && !bioSet.img_only); + bio.panel.setBorder(imgRefresh && this.style.crop, this.isType('Border'), this.isType('Refl')); if (sz) { - panelBio.setStyle(); - if (pptBio.heading && !pptBio.img_only) butBio.check(); + bio.panel.setStyle(); + if (bioSet.heading && !bioSet.img_only) bio.but.check(); } } setReflStrength(n) { this.refl.strength += n; this.refl.strength = $Bio.clamp(this.refl.strength, 0, 255); - pptBio.reflStrength = Math.round(this.refl.strength / 2.55); + bioSet.reflStrength = Math.round(this.refl.strength / 2.55); this.mask.reflection = false; this.refl.adjust = true; - if (pptBio.artistView && pptBio.cycPhoto) this.clearArtCache(); - if (panelBio.stndItem()) this.getImages(); - else this.getItem(panelBio.art.ix, panelBio.alb.ix); + if (bioSet.artistView && bioSet.cycPhoto) this.clearArtCache(); + if (bio.panel.stndItem()) this.getImages(); + else this.getItem(bio.panel.art.ix, bio.panel.alb.ix); } setStub(ca, key, def, n, userStub) { @@ -1629,26 +1629,26 @@ class ImagesBio { } toggle(n) { - pptBio.toggle(n); - this.cov.cycle = pptBio.loadCovAllFb || pptBio.loadCovFolder; + bioSet.toggle(n); + this.cov.cycle = bioSet.loadCovAllFb || bioSet.loadCovFolder; this.cov.cycle_ix = 0; - if (n == 'loadCovFolder' && pptBio.loadCovFolder && !pptBio.get('Panel Biography - System: Cover Folder Checked', false)) { + if (n == 'loadCovFolder' && bioSet.loadCovFolder && !bioSet.get('Panel Biography - System: Cover Folder Checked', false)) { fb.ShowPopupMessage("Enter folder in options: \"Server Settings\"\\Cover\\Covers: cycle folder.\n\nDefault: artist photo folder.\n\nImages are updated when the album changes. Any images arriving after choosing the current album aren't included.", 'Biography: load folder for cover cycling'); - pptBio.set('Panel Biography - System: Cover Folder Checked', true); + bioSet.set('Panel Biography - System: Cover Folder Checked', true); } this.id.albCyc = ''; this.id.curAlbCyc = ''; - if (pptBio.artistView) { - seeker.upd(false, !this.cov.cycle); + if (bioSet.artistView) { + bio.seeker.upd(false, !this.cov.cycle); return; } if (this.cov.cycle) this.getCovImages(); else this.getImages(); - seeker.upd(false, !this.cov.cycle); + bio.seeker.upd(false, !this.cov.cycle); } trace(x, y) { - if (!pptBio.autoEnlarge) return true; + if (!bioSet.autoEnlarge) return true; const o = this.cache().cache[this.cur_pth()]; if (!o) return false; return x > o.x && x < o.x + o.w && y > o.y && y < o.y + o.h; @@ -1675,33 +1675,33 @@ class ImagesBio { this.id.curAlbCyc = ''; this.clearArtCache(true); this.clearCovCache(); - filmStrip.scroll.pos.art = {}; - if (panelBio.stndItem()) this.getImages(true); - else this.getItem(panelBio.art.ix, panelBio.alb.ix, true); + bio.filmStrip.scroll.pos.art = {}; + if (bio.panel.stndItem()) this.getImages(true); + else this.getItem(bio.panel.art.ix, bio.panel.alb.ix, true); } wheel(step) { - switch (vkBio.k('shift')) { + switch (bio.vk.k('shift')) { case false: - if (this.art.images.length > 1 && pptBio.artistView && !pptBio.text_only && pptBio.cycPhoto) { + if (this.art.images.length > 1 && bioSet.artistView && !bioSet.text_only && bioSet.cycPhoto) { this.changePhoto(-step); - if (pptBio.cycPic) this.timeStamp.photo = Date.now(); + if (bioSet.cycPic) this.timeStamp.photo = Date.now(); } - if (this.cov.cycle && this.cov.images.length > 1 && !pptBio.artistView && !pptBio.text_only && !panelBio.alb.ix) { + if (this.cov.cycle && this.cov.images.length > 1 && !bioSet.artistView && !bioSet.text_only && !bio.panel.alb.ix) { this.changeCov(-step); if (this.cov.cycle) this.timeStamp.cov = Date.now(); } - seeker.debounce(); + bio.seeker.debounce(); break; case true: - if (!panelBio || butBio.trace('lookUp', panelBio.m.x, panelBio.m.y) || panelBio.trace.text || panelBio.trace.film || !this.isType('Refl')) break; + if (!bio.panel || bio.but.trace('lookUp', bio.panel.m.x, bio.panel.m.y) || bio.panel.trace.text || bio.panel.trace.film || !this.isType('Refl')) break; this.setReflStrength(-step * 5); break; } } } -class ImageCache { +class BioImageCache { constructor(type) { this.cache = {}; this.pth = ''; @@ -1715,7 +1715,7 @@ class ImageCache { const cacheLength = keys.length; const minCacheSize = 5; if (cacheLength > minCacheSize) { - this.cache = imgBio.sortCache(this.cache, 'time'); + this.cache = bio.img.sortCache(this.cache, 'time'); keys = Object.keys(this.cache); const numToRemove = Math.round((cacheLength - minCacheSize) / 5); if (numToRemove > 0) { @@ -1726,44 +1726,44 @@ class ImageCache { cacheIt(image, key, embedded) { try { - if (!image || this.type != pptBio.artistView) return imgBio.paint(); - if (imgBio.memoryLimit()) this.checkCache(); - if (!pptBio.text_only || uiBio.style.isBlur) { + if (!image || this.type != bioSet.artistView) return bio.img.paint(); + if (bio.img.memoryLimit()) this.checkCache(); + if (!bioSet.text_only || bio.ui.style.isBlur) { const start = Date.now(); const o = this.cache[key] = {}; - o.img = imgBio.cur = imgBio.process(image, this.type, o); - o.filmID = filmStrip.id(); + o.img = bio.img.cur = bio.img.process(image, this.type, o); + o.filmID = bio.filmStrip.id(); o.time = Date.now() - start; } this.pth = key; - this.embedded = embedded && pptBio.showFilmStrip && !filmStrip.style.auto ? image : null; - filmStrip.check(); - imgBio.paint(); + this.embedded = embedded && bioSet.showFilmStrip && !bio.filmStrip.style.auto ? image : null; + bio.filmStrip.check(); + bio.img.paint(); } catch (e) { this.pth = ''; - imgBio.paint(); + bio.img.paint(); $Bio.trace(`unable to load image: ${key}`); } } cacheHit(key) { - if (pptBio.text_only && !uiBio.style.isBlur) { + if (bioSet.text_only && !bio.ui.style.isBlur) { this.pth = key; return; } const o = this.cache[key]; - const id = filmStrip.id(); - if (!o || !o.img || o.filmID.id != id.id || o.filmID.bor != id.bor || imgBio.refl.adjust) return false; - imgBio.x = o.x; - seeker.counter.x = o.counter_x; - imgBio.y = o.y; - seeker.counter.y = o.counter_y; - if (uiBio.style.isBlur && o.blur && !(pptBio.img_only && imgBio.style.crop)) imgBio.blur = o.blur; - imgBio.cur = o.img; + const id = bio.filmStrip.id(); + if (!o || !o.img || o.filmID.id != id.id || o.filmID.bor != id.bor || bio.img.refl.adjust) return false; + bio.img.x = o.x; + bio.seeker.counter.x = o.counter_x; + bio.img.y = o.y; + bio.seeker.counter.y = o.counter_y; + if (bio.ui.style.isBlur && o.blur && !(bioSet.img_only && bio.img.style.crop)) bio.img.blur = o.blur; + bio.img.cur = o.img; this.pth = key; - if (this.type && !imgBio.art.images.length || !this.type && !imgBio.cov.images.length) filmStrip.check(); - else filmStrip.paint(); - imgBio.paint(); + if (this.type && !bio.img.art.images.length || !this.type && !bio.img.cov.images.length) bio.filmStrip.check(); + else bio.filmStrip.paint(); + bio.img.paint(); return true; } @@ -1772,17 +1772,28 @@ class ImageCache { } } -const art = new ImageCache(true); -const cov = new ImageCache(false); - -class Seeker { +/** + * The instance of `BioImageCache` class for biography artist image cache operations. + * @typedef {BioImageCache} + * @global + */ +const bioArt = new BioImageCache(true); + +/** + * The instance of `BioImageCache` class for biography cover image cache operations. + * @typedef {BioImageCache} + * @global + */ +const bioCov = new BioImageCache(false); + +class BioSeeker { constructor() { this.dn = false; this.hand = false; this.l_dn = false; this.imgNo = 0; - pptBio.imgSeekerShow = $Bio.clamp(pptBio.imgSeekerShow, 0, 2); - this.imgSeeker = (!pptBio.imgSeeker && !pptBio.imgCounter) ? 0 : pptBio.imgSeekerShow; + bioSet.imgSeekerShow = $Bio.clamp(bioSet.imgSeekerShow, 0, 2); + this.imgSeeker = (!bioSet.imgSeeker && !bioSet.imgCounter) ? 0 : bioSet.imgSeekerShow; this.nh = 10; this.nw = 10; this.overlap = false; @@ -1821,89 +1832,89 @@ class Seeker { }; this.debounce = $Bio.debounce(() => { - if (panelBio.imgBoxTrace(panelBio.m.x, panelBio.m.y) || this.imgSeeker == 2) return; + if (bio.panel.imgBoxTrace(bio.panel.m.x, bio.panel.m.y) || this.imgSeeker == 2) return; this.show = false; - imgBio.paint(); - filmStrip.paint(); + bio.img.paint(); + bio.filmStrip.paint(); }, 3000); } draw(gr) { if (this.imgNo < 2 || !this.show) return; let prog = 0; - if (this.seekerBelowImg && imgBio.cur) { - this.bar.y2 = Math.round(Math.min(imgBio.y + imgBio.cur.Height / (1 + this.bar.reflCorr) + this.bar.offset, (panelBio.h - panelBio.filmStripSize.b) * 0.9) - this.bar.h / 2); - this.bar.y3 = this.bar.y2 + Math.ceil(uiBio.style.l_w / 2); - this.bar.y5 = (panelBio.h - panelBio.filmStripSize.b) * 0.97; + if (this.seekerBelowImg && bio.img.cur) { + this.bar.y2 = Math.round(Math.min(bio.img.y + bio.img.cur.Height / (1 + this.bar.reflCorr) + this.bar.offset, (bio.panel.h - bio.panel.filmStripSize.b) * 0.9) - this.bar.h / 2); + this.bar.y3 = this.bar.y2 + Math.ceil(bio.ui.style.l_w / 2); + this.bar.y5 = (bio.panel.h - bio.panel.filmStripSize.b) * 0.97; } - if (pptBio.imgSeeker && pptBio.imgSeekerDots == 1) { // dots + if (bioSet.imgSeeker && bioSet.imgSeekerDots == 1) { // dots gr.SetSmoothingMode(2); - prog = this.dn ? $Bio.clamp(panelBio.m.x - this.bar.x2 - this.bar.grip_h / 2, this.prog.min, this.prog.max) : (pptBio.artistView ? imgBio.art.ix + 0.5 : imgBio.cov.ix + 0.5) * this.bar.w1 / this.imgNo - (this.bar.grip_h - this.bar.dot_w) / 2; + prog = this.dn ? $Bio.clamp(bio.panel.m.x - this.bar.x2 - this.bar.grip_h / 2, this.prog.min, this.prog.max) : (bioSet.artistView ? bio.img.art.ix + 0.5 : bio.img.cov.ix + 0.5) * this.bar.w1 / this.imgNo - (this.bar.grip_h - this.bar.dot_w) / 2; for (let i = 0; i < this.imgNo; i++) { gr.FillEllipse(this.bar.x2 + ((i + 0.5) / this.imgNo) * this.bar.w1, this.bar.y2, this.bar.dot_w, this.bar.h, RGB(245, 245, 245)); - gr.DrawEllipse(this.bar.x2 + ((i + 0.5) / this.imgNo) * this.bar.w1, this.bar.y2, this.bar.dot_w, this.bar.h, uiBio.style.l_w, RGB(128, 128, 128)); + gr.DrawEllipse(this.bar.x2 + ((i + 0.5) / this.imgNo) * this.bar.w1, this.bar.y2, this.bar.dot_w, this.bar.h, bio.ui.style.l_w, RGB(128, 128, 128)); } gr.FillEllipse(this.bar.x2 + prog, this.bar.y3 - this.bar.gripOffset, this.bar.grip_h, this.bar.grip_h, RGB(245, 245, 245)); - gr.DrawEllipse(this.bar.x2 + prog, this.bar.y3 - this.bar.gripOffset, this.bar.grip_h, this.bar.grip_h, uiBio.style.l_w, RGB(128, 128, 128)); + gr.DrawEllipse(this.bar.x2 + prog, this.bar.y3 - this.bar.gripOffset, this.bar.grip_h, this.bar.grip_h, bio.ui.style.l_w, RGB(128, 128, 128)); gr.SetSmoothingMode(0); } - if (pptBio.imgSeeker && pptBio.imgSeekerDots == 0) { // bar - prog = this.dn ? $Bio.clamp(panelBio.m.x - this.bar.x1, 0, this.bar.w1) : (pptBio.artistView ? imgBio.art.ix + 0.5 : imgBio.cov.ix + 0.5) * this.bar.w1 / this.imgNo; - gr.DrawRect(this.bar.x1, this.bar.y2, this.bar.w1, this.bar.h, uiBio.style.l_w, RGB(128, 128, 128)); - gr.FillSolidRect(this.bar.x2, this.bar.y3, this.bar.w1 - uiBio.style.l_w, this.bar.h - uiBio.style.l_w, RGBA(0, 0, 0, 75)); - gr.FillSolidRect(this.bar.x2, this.bar.y3, prog - uiBio.style.l_w, this.bar.h - uiBio.style.l_w, RGB(245, 245, 245)); + if (bioSet.imgSeeker && bioSet.imgSeekerDots == 0) { // bar + prog = this.dn ? $Bio.clamp(bio.panel.m.x - this.bar.x1, 0, this.bar.w1) : (bioSet.artistView ? bio.img.art.ix + 0.5 : bio.img.cov.ix + 0.5) * this.bar.w1 / this.imgNo; + gr.DrawRect(this.bar.x1, this.bar.y2, this.bar.w1, this.bar.h, bio.ui.style.l_w, RGB(128, 128, 128)); + gr.FillSolidRect(this.bar.x2, this.bar.y3, this.bar.w1 - bio.ui.style.l_w, this.bar.h - bio.ui.style.l_w, RGBA(0, 0, 0, 75)); + gr.FillSolidRect(this.bar.x2, this.bar.y3, prog - bio.ui.style.l_w, this.bar.h - bio.ui.style.l_w, RGB(245, 245, 245)); gr.SetSmoothingMode(2); gr.FillEllipse(this.bar.x2 + prog - Math.round((this.bar.grip_h) / 2), this.bar.y3 - this.bar.gripOffset, this.bar.grip_h, this.bar.grip_h, RGB(245, 245, 245)); - gr.DrawEllipse(this.bar.x2 + prog - Math.round((this.bar.grip_h) / 2), this.bar.y3 - this.bar.gripOffset, this.bar.grip_h, this.bar.grip_h, uiBio.style.l_w, RGB(128, 128, 128)); + gr.DrawEllipse(this.bar.x2 + prog - Math.round((this.bar.grip_h) / 2), this.bar.y3 - this.bar.gripOffset, this.bar.grip_h, this.bar.grip_h, bio.ui.style.l_w, RGB(128, 128, 128)); gr.SetSmoothingMode(0); } - if (pptBio.imgCounter) { // counter - if (pptBio.imgSeekerDots == 1) prog += Math.round(this.bar.grip_h / 2 - this.bar.dot_w / 2); - const count = `${pptBio.artistView ? imgBio.art.ix + 1 : imgBio.cov.ix + 1} / ${this.imgNo}`; + if (bioSet.imgCounter) { // counter + if (bioSet.imgSeekerDots == 1) prog += Math.round(this.bar.grip_h / 2 - this.bar.dot_w / 2); + const count = `${bioSet.artistView ? bio.img.art.ix + 1 : bio.img.cov.ix + 1} / ${this.imgNo}`; const count_m = `${this.imgNo} / ${this.imgNo} `; if (count) { - const count_w = Math.max(gr.CalcTextWidth(count_m, uiBio.font.small), 8); - const count_x = pptBio.imgSeeker ? Math.round($Bio.clamp(this.bar.x1 - count_w / 2 + prog, this.bar.l + 2, this.bar.l + this.nw - count_w - 4)) : this.counter.x + uiBio.style.l_w * 2 + imgBio.bor.w1; - const count_y = pptBio.imgSeeker ? Math.round(this.bar.y2 - this.bar.gripOffset - uiBio.font.small_h * 1.5) : this.counter.y + uiBio.style.l_w * 2 + imgBio.bor.w1; - gr.FillRoundRect(count_x, count_y, count_w + 2, uiBio.font.small_h + 2, 3, 3, RGBA(0, 0, 0, 210)); - gr.DrawRoundRect(count_x + 1, count_y + 1, count_w, uiBio.font.small_h, 1, 1, 1, RGBA(255, 255, 255, 60)); - gr.DrawRoundRect(count_x, count_y, count_w + 2, uiBio.font.small_h + 2, 1, 1, 1, RGBA(0, 0, 0, 200)); - gr.GdiDrawText(count, uiBio.font.small, RGB(250, 250, 250), count_x + 1, count_y, count_w, uiBio.font.small_h + 2, txt.cc); + const count_w = Math.max(gr.CalcTextWidth(count_m, bio.ui.font.small), 8); + const count_x = bioSet.imgSeeker ? Math.round($Bio.clamp(this.bar.x1 - count_w / 2 + prog, this.bar.l + 2, this.bar.l + this.nw - count_w - 4)) : this.counter.x + bio.ui.style.l_w * 2 + bio.img.bor.w1; + const count_y = bioSet.imgSeeker ? Math.round(this.bar.y2 - this.bar.gripOffset - bio.ui.font.small_h * 1.5) : this.counter.y + bio.ui.style.l_w * 2 + bio.img.bor.w1; + gr.FillRoundRect(count_x, count_y, count_w + 2, bio.ui.font.small_h + 2, 3, 3, RGBA(0, 0, 0, 210)); + gr.DrawRoundRect(count_x + 1, count_y + 1, count_w, bio.ui.font.small_h, 1, 1, 1, RGBA(255, 255, 255, 60)); + gr.DrawRoundRect(count_x, count_y, count_w + 2, bio.ui.font.small_h + 2, 1, 1, 1, RGBA(0, 0, 0, 200)); + gr.GdiDrawText(count, bio.ui.font.small, RGB(250, 250, 250), count_x + 1, count_y, count_w, bio.ui.font.small_h + 2, bio.txt.cc); } } } intersectRect() { - return !(panelBio.tbox.l > panelBio.ibox.l + panelBio.ibox.w || panelBio.tbox.l + panelBio.tbox.w < panelBio.ibox.l || panelBio.tbox.t > panelBio.ibox.t + panelBio.ibox.h || panelBio.tbox.t + panelBio.tbox.h < panelBio.ibox.t); + return !(bio.panel.tbox.l > bio.panel.ibox.l + bio.panel.ibox.w || bio.panel.tbox.l + bio.panel.tbox.w < bio.panel.ibox.l || bio.panel.tbox.t > bio.panel.ibox.t + bio.panel.ibox.h || bio.panel.tbox.t + bio.panel.tbox.h < bio.panel.ibox.t); } lbtn_dn(p_x, p_y) { this.dn = false; this.l_dn = true; - if (pptBio.touchControl) { - uiBio.id.touch_dn = filmStrip.get_ix(p_x, p_y); + if (bioSet.touchControl) { + bio.ui.id.touch_dn = bio.filmStrip.get_ix(p_x, p_y); } if (this.imgSeeker) { if (this.imgNo > 1) this.dn = this.hand; if (this.dn) { const prog = $Bio.clamp(p_x - this.bar.x1, 0, this.bar.w1); - if (pptBio.artistView) { - const new_ix = Math.min(Math.floor(prog / this.bar.w1 * imgBio.art.images.length), imgBio.art.images.length - 1); - if (new_ix != imgBio.art.ix) { - imgBio.art.ix = new_ix; - imgBio.setPhoto(); + if (bioSet.artistView) { + const new_ix = Math.min(Math.floor(prog / this.bar.w1 * bio.img.art.images.length), bio.img.art.images.length - 1); + if (new_ix != bio.img.art.ix) { + bio.img.art.ix = new_ix; + bio.img.setPhoto(); } } else { - const new_i_x = Math.min(Math.floor(prog / this.bar.w1 * imgBio.cov.images.length), imgBio.cov.images.length - 1); - if (new_i_x != imgBio.cov.ix) { - imgBio.cov.ix = new_i_x; - imgBio.setCov(); + const new_i_x = Math.min(Math.floor(prog / this.bar.w1 * bio.img.cov.images.length), bio.img.cov.images.length - 1); + if (new_i_x != bio.img.cov.ix) { + bio.img.cov.ix = new_i_x; + bio.img.setCov(); } } - imgBio.paint(); - filmStrip.paint(); + bio.img.paint(); + bio.filmStrip.paint(); } } } @@ -1912,77 +1923,77 @@ class Seeker { this.upd(); this.dn = false; this.l_dn = false; - if (this.imgSeeker) imgBio.paint(); - filmStrip.paint(); + if (this.imgSeeker) bio.img.paint(); + bio.filmStrip.paint(); } metrics(circular, crop, horizontal, reflection, vertical) { - pptBio.imgSeekerDisabled = pptBio.style > 3 && !pptBio.img_only && !panelBio.clip && this.intersectRect() || (pptBio.filmStripOverlay && !pptBio.text_only); - this.imgSeeker = !pptBio.imgSeekerDisabled ? ((!pptBio.imgSeeker && !pptBio.imgCounter) ? 0 : pptBio.imgSeekerShow) : 0; + bioSet.imgSeekerDisabled = bioSet.style > 3 && !bioSet.img_only && !bio.panel.clip && this.intersectRect() || (bioSet.filmStripOverlay && !bioSet.text_only); + this.imgSeeker = !bioSet.imgSeekerDisabled ? ((!bioSet.imgSeeker && !bioSet.imgCounter) ? 0 : bioSet.imgSeekerShow) : 0; if (!this.imgSeeker) { this.show = false; - imgBio.paint(); - filmStrip.paint(); + bio.img.paint(); + bio.filmStrip.paint(); return; } else if (this.imgSeeker == 2) this.show = true; - this.imgNo = pptBio.text_only ? 0 : pptBio.cycPhoto && pptBio.artistView ? imgBio.art.images.length : imgBio.cov.cycle && !pptBio.artistView && !panelBio.alb.ix ? imgBio.cov.images.length : 0; + this.imgNo = bioSet.text_only ? 0 : bioSet.cycPhoto && bioSet.artistView ? bio.img.art.images.length : bio.img.cov.cycle && !bioSet.artistView && !bio.panel.alb.ix ? bio.img.cov.images.length : 0; if (this.imgNo < 2) return; - this.overlap = pptBio.style > 3 && !pptBio.img_only && panelBio.clip; - this.bar.overlapCorr = this.overlap ? panelBio.bor.t : 0; + this.overlap = bioSet.style > 3 && !bioSet.img_only && bio.panel.clip; + this.bar.overlapCorr = this.overlap ? bio.panel.bor.t : 0; - const alignBottom = vertical && !crop && pptBio.alignV == 2 && !this.overlap; - const alignCenter = vertical && !crop && pptBio.alignV == 1 && !this.overlap; - const alignLeft = horizontal && !crop && pptBio.alignH == 0; - const alignRight = horizontal && !crop && pptBio.alignH == 2; + const alignBottom = vertical && !crop && bioSet.alignV == 2 && !this.overlap; + const alignCenter = vertical && !crop && bioSet.alignV == 1 && !this.overlap; + const alignLeft = horizontal && !crop && bioSet.alignH == 0; + const alignRight = horizontal && !crop && bioSet.alignH == 2; - this.nw = pptBio.img_only && crop ? imgBio.nw - panelBio.filmStripSize.l - panelBio.filmStripSize.r : imgBio.nw; - this.nh = pptBio.img_only ? imgBio.nh - (!crop ? 0 : panelBio.filmStripSize.b) : pptBio.style < 4 ? Math.min(!crop ? this.nw : imgBio.nh, imgBio.nh) : this.overlap ? panelBio.style.imgSize : Math.min(!crop ? (panelBio.ibox.w - panelBio.bor.l - panelBio.bor.r) : panelBio.ibox.h - panelBio.bor.t - panelBio.bor.b, panelBio.ibox.h - panelBio.bor.t - panelBio.bor.b); - const seeker_img_t = !alignBottom || this.nw >= imgBio.nh ? uiBio.y : imgBio.nh - this.nw; + this.nw = bioSet.img_only && crop ? bio.img.nw - bio.panel.filmStripSize.l - bio.panel.filmStripSize.r : bio.img.nw; + this.nh = bioSet.img_only ? bio.img.nh - (!crop ? 0 : bio.panel.filmStripSize.b) : bioSet.style < 4 ? Math.min(!crop ? this.nw : bio.img.nh, bio.img.nh) : this.overlap ? bio.panel.style.imgSize : Math.min(!crop ? (bio.panel.ibox.w - bio.panel.bor.l - bio.panel.bor.r) : bio.panel.ibox.h - bio.panel.bor.t - bio.panel.bor.b, bio.panel.ibox.h - bio.panel.bor.t - bio.panel.bor.b); + const seeker_img_t = !alignBottom || this.nw >= bio.img.nh ? bio.ui.y : bio.img.nh - this.nw; - this.bar.h = (pptBio.imgSeekerDots == 1 ? 6 : 5) * $Bio.scale; - this.bar.grip_h = (pptBio.imgSeekerDots == 1 ? 10 : 9) * $Bio.scale; - this.bar.gripOffset = Math.round((this.bar.grip_h - this.bar.h) / 2) + Math.ceil(uiBio.style.l_w / 2); - const circ_sz = pptBio.style < 4 ? Math.min(this.nw, this.nh) : Math.min(panelBio.ibox.w - panelBio.bor.l - panelBio.bor.r, panelBio.ibox.h - panelBio.bor.t - panelBio.bor.b); - this.bar.w1 = circular && (alignLeft || alignRight) ? Math.min(this.imgNo * 30 * $Bio.scale, circ_sz) : Math.min(this.imgNo * 30 * $Bio.scale, (!imgBio.style.crop ? Math.min(this.nw, this.nh) : this.nw) - 30 * $Bio.scale); + this.bar.h = (bioSet.imgSeekerDots == 1 ? 6 : 5) * $Bio.scale; + this.bar.grip_h = (bioSet.imgSeekerDots == 1 ? 10 : 9) * $Bio.scale; + this.bar.gripOffset = Math.round((this.bar.grip_h - this.bar.h) / 2) + Math.ceil(bio.ui.style.l_w / 2); + const circ_sz = bioSet.style < 4 ? Math.min(this.nw, this.nh) : Math.min(bio.panel.ibox.w - bio.panel.bor.l - bio.panel.bor.r, bio.panel.ibox.h - bio.panel.bor.t - bio.panel.bor.b); + this.bar.w1 = circular && (alignLeft || alignRight) ? Math.min(this.imgNo * 30 * $Bio.scale, circ_sz) : Math.min(this.imgNo * 30 * $Bio.scale, (!bio.img.style.crop ? Math.min(this.nw, this.nh) : this.nw) - 30 * $Bio.scale); this.bar.w2 = this.bar.w1 + Math.round(this.bar.grip_h); - this.bar.l = pptBio.img_only ? panelBio.bor.l + panelBio.filmStripSize.l : panelBio.img.l; - this.bar.x1 = circular ? (alignLeft ? this.bar.l + (circ_sz - this.bar.w1) / 2 : alignRight ? this.bar.l + this.nw - circ_sz + (circ_sz - this.bar.w1) / 2 : Math.round(this.bar.l + (this.nw - this.bar.w1) / 2)) : (alignLeft ? Math.round(this.bar.l + 15 * $Bio.scale) : alignRight ? Math.round(panelBio.w - (pptBio.img_only ? panelBio.bor.r : panelBio.img.r) - 15 * $Bio.scale - this.bar.w1) : Math.round(this.bar.l + (this.nw - this.bar.w1) / 2)); + this.bar.l = bioSet.img_only ? bio.panel.bor.l + bio.panel.filmStripSize.l : bio.panel.img.l; + this.bar.x1 = circular ? (alignLeft ? this.bar.l + (circ_sz - this.bar.w1) / 2 : alignRight ? this.bar.l + this.nw - circ_sz + (circ_sz - this.bar.w1) / 2 : Math.round(this.bar.l + (this.nw - this.bar.w1) / 2)) : (alignLeft ? Math.round(this.bar.l + 15 * $Bio.scale) : alignRight ? Math.round(bio.panel.w - (bioSet.img_only ? bio.panel.bor.r : bio.panel.img.r) - 15 * $Bio.scale - this.bar.w1) : Math.round(this.bar.l + (this.nw - this.bar.w1) / 2)); this.bar.x3 = this.bar.x1 - Math.round(this.bar.grip_h / 2); - this.bar.y1 = pptBio.img_only ? panelBio.bor.t + seeker_img_t + panelBio.filmStripSize.t : panelBio.img.t + seeker_img_t; + this.bar.y1 = bioSet.img_only ? bio.panel.bor.t + seeker_img_t + bio.panel.filmStripSize.t : bio.panel.img.t + seeker_img_t; this.bar.y2 = Math.round(this.bar.y1 + this.nh * 0.9 - this.bar.h / 2) - this.bar.overlapCorr; - this.bar.y3 = this.bar.y2 + Math.ceil(uiBio.style.l_w / 2); + this.bar.y3 = this.bar.y2 + Math.ceil(bio.ui.style.l_w / 2); this.bar.y4 = this.nh * 0.8 - this.bar.overlapCorr; this.bar.y5 = this.nh - this.bar.overlapCorr; - this.seekerBelowImg = pptBio.imgSeeker && this.nh < 0.8 * panelBio.h - panelBio.filmStripSize.b && (vertical && !crop && pptBio.alignV == 0 && !this.overlap || alignCenter); - this.bar.reflCorr = this.seekerBelowImg && reflection ? pptBio.reflSize / 100 : 0; - this.bar.offset = pptBio.imgCounter ? uiBio.font.small_h * 2 + this.bar.gripOffset + 2 : uiBio.font.small_h * 1.5 + this.bar.gripOffset; + this.seekerBelowImg = bioSet.imgSeeker && this.nh < 0.8 * bio.panel.h - bio.panel.filmStripSize.b && (vertical && !crop && bioSet.alignV == 0 && !this.overlap || alignCenter); + this.bar.reflCorr = this.seekerBelowImg && reflection ? bioSet.reflSize / 100 : 0; + this.bar.offset = bioSet.imgCounter ? bio.ui.font.small_h * 2 + this.bar.gripOffset + 2 : bio.ui.font.small_h * 1.5 + this.bar.gripOffset; - if (pptBio.imgSeekerDots == 1) { + if (bioSet.imgSeekerDots == 1) { this.bar.dot_w = Math.floor($Bio.clamp(this.bar.w1 / this.imgNo, 2, this.bar.h)); this.bar.x2 = this.bar.x1 - Math.round(this.bar.dot_w / 2); this.prog.min = 0.5 / this.imgNo * this.bar.w1 - (this.bar.grip_h - this.bar.dot_w) / 2; this.prog.max = ((this.imgNo - 0.5) / this.imgNo) * this.bar.w1 - (this.bar.grip_h - this.bar.dot_w) / 2; } else { - this.bar.x2 = this.bar.x1 + Math.ceil(uiBio.style.l_w / 2); + this.bar.x2 = this.bar.x1 + Math.ceil(bio.ui.style.l_w / 2); } } move(p_x, p_y) { this.hand = false; if (this.imgSeeker) { - const trace = panelBio.imgBoxTrace(p_x, p_y); - const show = !pptBio.text_only && (pptBio.artistView || !panelBio.alb.ix) && (this.imgSeeker == 2 || trace); + const trace = bio.panel.imgBoxTrace(p_x, p_y); + const show = !bioSet.text_only && (bioSet.artistView || !bio.panel.alb.ix) && (this.imgSeeker == 2 || trace); if (this.imgNo > 1 && (!this.l_dn || this.dn)) { this.hand = !this.seekerBelowImg ? p_x > this.bar.x3 && p_x < this.bar.x3 + this.bar.w2 && p_y > this.bar.y1 + this.bar.y4 && p_y < this.bar.y1 + this.bar.y5 : - p_x > this.bar.x3 && p_x < this.bar.x3 + this.bar.w2 && p_y > this.bar.y2 - uiBio.font.small_h && p_y < this.bar.y5; + p_x > this.bar.x3 && p_x < this.bar.x3 + this.bar.w2 && p_y > this.bar.y2 - bio.ui.font.small_h && p_y < this.bar.y5; } - if (show != this.show && !pptBio.text_only && trace) { - imgBio.paint(); - filmStrip.paint(); + if (show != this.show && !bioSet.text_only && trace) { + bio.img.paint(); + bio.filmStrip.paint(); } if (show) this.show = true; this.debounce(); @@ -1990,29 +2001,29 @@ class Seeker { if (this.dn) { const prog = $Bio.clamp(p_x - this.bar.x1, 0, this.bar.w1); - if (pptBio.artistView) { - const new_ix = Math.min(Math.floor(prog / this.bar.w1 * imgBio.art.images.length), imgBio.art.images.length - 1); - if (new_ix != imgBio.art.ix) { - imgBio.art.ix = new_ix; - imgBio.setPhoto(); + if (bioSet.artistView) { + const new_ix = Math.min(Math.floor(prog / this.bar.w1 * bio.img.art.images.length), bio.img.art.images.length - 1); + if (new_ix != bio.img.art.ix) { + bio.img.art.ix = new_ix; + bio.img.setPhoto(); } } else { - const new_i_x = Math.min(Math.floor(prog / this.bar.w1 * imgBio.cov.images.length), imgBio.cov.images.length - 1); - if (new_i_x != imgBio.cov.ix) { - imgBio.cov.ix = new_i_x; - imgBio.setCov(); + const new_i_x = Math.min(Math.floor(prog / this.bar.w1 * bio.img.cov.images.length), bio.img.cov.images.length - 1); + if (new_i_x != bio.img.cov.ix) { + bio.img.cov.ix = new_i_x; + bio.img.setCov(); } } - imgBio.paint(); - filmStrip.paint(); + bio.img.paint(); + bio.filmStrip.paint(); } } upd(force, clearCov, repaint) { - if ((pptBio.imgSeeker || pptBio.imgCounter) && pptBio.imgSeekerShow && (!this.dn && (!pptBio.text_only || uiBio.style.isBlur || force))) { - if (clearCov) imgBio.cov.images = []; - imgBio.metrics(); - if (repaint) txt.paint(); + if ((bioSet.imgSeeker || bioSet.imgCounter) && bioSet.imgSeekerShow && (!this.dn && (!bioSet.text_only || bio.ui.style.isBlur || force))) { + if (clearCov) bio.img.cov.images = []; + bio.img.metrics(); + if (repaint) bio.txt.paint(); } } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-initialise.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-initialise.js index 86ec9f71..1205bb1c 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-initialise.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-initialise.js @@ -1,64 +1,76 @@ 'use strict'; -/** @type {UserInterfaceBio} */ -let uiBio = new UserInterfaceBio(); -/** @type {VkeysBio} */ -let vkBio = new VkeysBio(); -/** @type {PanelBio} */ -let panelBio = new PanelBio(); -/** @type {Names} */ -let name = new Names(); -/** @type {ScrollbarBio} */ -let alb_scrollbar = new ScrollbarBio(); -/** @type {ScrollbarBio} */ -let art_scrollbar = new ScrollbarBio(); -/** @type {ScrollbarBio} */ -let art_scroller = new ScrollbarBio(); -/** @type {ScrollbarBio} */ -let cov_scroller = new ScrollbarBio(); -/** @type {ButtonsBio} */ -let butBio = new ButtonsBio(); -/** @type {PopUpBoxBio} */ -let popUpBoxBio = new PopUpBoxBio(); -/** @type {Text} */ -let txt = new Text(); -/** @type {TaggerBio} */ -let tagBio = new TaggerBio(); -/** @type {ResizeHandler} */ -let resize = new ResizeHandler(); -/** @type {LibraryBio} */ -let libBio = new LibraryBio(); -/** @type {ImagesBio} */ -let imgBio = new ImagesBio(); -/** @type {Seeker} */ -let seeker = new Seeker(); -/** @type {FilmStrip} */ -let filmStrip = new FilmStrip(); -/** @type {TimersBio} */ -let timerBio = new TimersBio(); -/** @type {MenuItemsBio} */ -let menBio = new MenuItemsBio(); -/** @type {ServerBio} */ -let serverBio = new ServerBio(); -/** @type {InfoboxBio} */ -let infoboxBio = new InfoboxBio(); -/** @type {LyricsBio} */ -let lyricsBio; if (panelBio.id.lyricsSource) lyricsBio = new LyricsBio(); +/** + * A collection of all biography class instances. + * @typedef {object} bio - The biography main object. + * @property {BioUserInterface} ui - The instance of `BioUserInterface` class for ui operations. + * @property {BioVkeys} vk - The instance of `BioVkeys` class for vkeys operations. + * @property {BioPanel} panel - The instance of `BioPanel` class for panel operations. + * @property {BioNames} name - The instance of `BioNames` class for name operations. + * @property {BioScrollbar} alb_scrollbar - The instance of `BioScrollbar` class for scrollbar operations. + * @property {BioScrollbar} art_scrollbar - The instance of `BioScrollbar` class for scrollbar operations. + * @property {BioScrollbar} art_scroller - The instance of `BioScrollbar` class for scrollbar operations. + * @property {BioScrollbar} cov_scroller - The instance of `BioScrollbar` class for scrollbar operations. + * @property {BioButtons} but - The instance of `BioButtons` class for button operations. + * @property {BioPopUpBox} popUpBox - The instance of `BioPopUpBox` class for popup operations. + * @property {BioText} txt - The instance of `BioText` class for text operations. + * @property {BioTagger} tag - The instance of `BioTagger` class for tagger operations. + * @property {BioResizeHandler} resize - The instance of `BioResizeHandler` class for resize operations. + * @property {BioLibrary} lib - The instance of `BioLibrary` class for lib operations. + * @property {BioImages} img - The instance of `BioImages` class for image operations. + * @property {BioSeeker} seeker - The instance of `BioSeeker` class for seeker operations. + * @property {BioFilmStrip} filmStrip - The instance of `BioFilmStrip` class for filmstrip operations. + * @property {BioTimers} timer - The instance of `BioTimers` class for timer operations. + * @property {BioMenuItems} men - The instance of `BioMenuItems` class for menu operations. + * @property {BioServer} server - The instance of `BioServer` class for server operations. + * @property {BioInfobox} infobox - The instance of `BioInfobox` class for info operations. + * @property {BioLyrics} lyrics - The instance of `BioLyrics` class for lyric operations. + * @property {BioCallbacks} call - The instance of `BioCallbacks` class for callback operations. + */ +/** @global @type {bio} */ +const bio = {}; -alb_scrollbar.type = 'alb'; -art_scrollbar.type = 'art'; -art_scroller.type = 'film'; -cov_scroller.type = 'film'; +bio.initialized = false; -timerBio.image(); +bio.ui = new BioUserInterface(); +bio.vk = new BioVkeys(); +bio.panel = new BioPanel(); +bio.name = new BioNames(); +bio.alb_scrollbar = new BioScrollbar(); +bio.art_scrollbar = new BioScrollbar(); +bio.art_scroller = new BioScrollbar(); +bio.cov_scroller = new BioScrollbar(); +bio.but = new BioButtons(); +bio.popUpBox = new BioPopUpBox(); +bio.txt = new BioText(); +bio.tag = new BioTagger(); +bio.resize = new BioResizeHandler(); +bio.lib = new BioLibrary(); +bio.img = new BioImages(); +bio.seeker = new BioSeeker(); +bio.filmStrip = new BioFilmStrip(); +bio.timer = new BioTimers(); +bio.men = new BioMenuItems(); +bio.server = new BioServer(); +bio.infobox = new BioInfobox(); +if (bio.panel.id.lyricsSource) { + bio.lyrics = new BioLyrics(); +} -timerBio.clear(timerBio.zSearch); -timerBio.zSearch.id = setTimeout(() => { - if ($Bio.server && pptBio.panelActive) { - serverBio.download(false, { ix: 0, focus: false }, { ix: 0, focus: false }); serverBio.download(false, { ix: 0, focus: true }, { ix: 0, focus: true }); +bio.alb_scrollbar.type = 'alb'; +bio.art_scrollbar.type = 'art'; +bio.art_scroller.type = 'film'; +bio.cov_scroller.type = 'film'; + +bio.timer.image(); +bio.timer.clear(bio.timer.zSearch); +bio.timer.zSearch.id = setTimeout(() => { + if ($Bio.server && bioSet.panelActive) { + bio.server.download(false, { ix: 0, focus: false }, { ix: 0, focus: false }); + bio.server.download(false, { ix: 0, focus: true }, { ix: 0, focus: true }); } - timerBio.zSearch.id = null; + bio.timer.zSearch.id = null; }, 3000); -// if (!pptBio.get('Panel Biography - System: Software Notice Checked', false)) fb.ShowPopupMessage('License\r\n\r\nCopyright (c) 2021-2023 WilB\r\n\r\nThe above copyright notice shall be included in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.', 'Biography'); -// pptBio.set('Panel Biography - System: Software Notice Checked', true); +if (!bioSet.get('Panel Biography - System: Software Notice Checked', true)) fb.ShowPopupMessage('License\r\n\r\nCopyright (c) 2021-2023 WilB\r\n\r\nThe above copyright notice shall be included in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.', 'Biography'); + bioSet.set('Panel Biography - System: Software Notice Checked', true); diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-interface.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-interface.js index de5375fb..28eb8a1a 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-interface.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-interface.js @@ -2,12 +2,12 @@ window.DlgCode = 0x004; -class UserInterfaceBio { +class BioUserInterface { constructor() { this.dui = window.InstanceType; - if (pptBio.typeOverlay > 4 || pptBio.typeOverlay < 0) pptBio.typeOverlay = 0; - pptBio.sbarCol = $Bio.clamp(pptBio.sbarCol, 0, 1); + if (bioSet.typeOverlay > 4 || bioSet.typeOverlay < 0) bioSet.typeOverlay = 0; + bioSet.sbarCol = $Bio.clamp(bioSet.sbarCol, 0, 1); this.blur = { level: 100 @@ -25,13 +25,13 @@ class UserInterfaceBio { this.font = { boldAdjust: 1, - heading: gdi.Font(fontDefault, 16, 0), + heading: gdi.Font(grFont.fontDefault, 16, 0), heading_h: 21, headingBaseSize: 16, headingCustom: false, headingStyle: 1, items: [['lyricsFontStyle', 'lyrics'], ['sourceStyle', 'subHeadSource'], ['summaryStyle', 'summary'], ['trackStyle', 'subHeadTrack'], ['wikiStyle', 'subHeadWiki']], - lyrics: gdi.Font(fontDefault, 16, 3), + lyrics: gdi.Font(grFont.fontDefault, 16, 3), main_h: 21, small_h: 8, zoomSize: 16 @@ -40,8 +40,8 @@ class UserInterfaceBio { this.heading = { h: 30, line_y: 0, - linePad: pptBio.hdLinePad, - pad: pptBio.hdPad + linePad: bioSet.hdLinePad, + pad: bioSet.hdPad }; this.id = { @@ -49,14 +49,14 @@ class UserInterfaceBio { touch_dn: -1 }; - pptBio.overlayStrength = $Bio.clamp(pptBio.overlayStrength, 0, 100); - pptBio.overlayGradient = $Bio.clamp(pptBio.overlayGradient, 0, 100); - pptBio.overlayBorderWidth = $Bio.clamp(pptBio.overlayBorderWidth, 1, 20); + bioSet.overlayStrength = $Bio.clamp(bioSet.overlayStrength, 0, 100); + bioSet.overlayGradient = $Bio.clamp(bioSet.overlayGradient, 0, 100); + bioSet.overlayBorderWidth = $Bio.clamp(bioSet.overlayBorderWidth, 1, 20); this.overlay = { - gradient: pptBio.overlayGradient / 10 - 1, - borderWidth: pptBio.typeOverlay != 2 && pptBio.typeOverlay != 4 ? 0 : pptBio.overlayBorderWidth, - strength: $Bio.clamp(255 * (100 - pptBio.overlayStrength) / 100, 0, 255) + gradient: bioSet.overlayGradient / 10 - 1, + borderWidth: bioSet.typeOverlay != 2 && bioSet.typeOverlay != 4 ? 0 : bioSet.overlayBorderWidth, + strength: $Bio.clamp(255 * (100 - bioSet.overlayStrength) / 100, 0, 255) }; this.pss = { @@ -65,23 +65,23 @@ class UserInterfaceBio { }; this.sbar = { - arrowPad: pptBio.sbarPad, + arrowPad: bioSet.sbarPad, but_h: 11, but_w: 11, - col: pptBio.sbarCol, + col: bioSet.sbarCol, narrowWidth: 2, sp: 12, type: 0, w: 11 }; - if (!pptBio.butCustIconFont.length) pptBio.butCustIconFont = 'Segoe UI Symbol'; + if (!bioSet.butCustIconFont.length) bioSet.butCustIconFont = 'Segoe UI Symbol'; this.show = { - btnBg: pptBio.hdShowBtnBg, - btnLabel: pptBio.hdShowBtnLabel, - btnRedLastfm: pptBio.hdShowRedLfm, - headingText: pptBio.hdShowTitle + btnBg: bioSet.hdShowBtnBg, + btnLabel: bioSet.hdShowBtnLabel, + btnRedLastfm: bioSet.hdShowRedLfm, + headingText: bioSet.hdShowTitle }; if (this.show.btnRedLastfm) this.show.btnBg = 1; @@ -95,16 +95,16 @@ class UserInterfaceBio { this.themeColour = {} - if (pptBio.narrowSbarWidth != 0) pptBio.narrowSbarWidth = $Bio.clamp(pptBio.narrowSbarWidth, 2, 10); - pptBio.hdLine = $Bio.value(pptBio.hdLine, 1, 2); - if (pptBio.headFontStyle < 0 || pptBio.headFontStyle > 5) pptBio.headFontStyle = 2; + if (bioSet.narrowSbarWidth != 0) bioSet.narrowSbarWidth = $Bio.clamp(bioSet.narrowSbarWidth, 2, 10); + bioSet.hdLine = $Bio.value(bioSet.hdLine, 1, 2); + if (bioSet.headFontStyle < 0 || bioSet.headFontStyle > 5) bioSet.headFontStyle = 2; this.id.c_c = this.id.local && typeof opt_c_c !== 'undefined'; this.getColours(); this.getFont(true); this.refresh = $Bio.debounce(() => { - txt.refresh(3); + bio.txt.refresh(3); }, 100); this.setSbar(); @@ -149,23 +149,23 @@ class UserInterfaceBio { }; prop.forEach((v, i) => { - this.col[v] = set(pptBio[`${v}Use`] ? pptBio[v] : '', i < 8 ? 0 : 1); + this.col[v] = set(bioSet[`${v}Use`] ? bioSet[v] : '', i < 8 ? 0 : 1); }); } calcText() { - pptBio.textPad = Math.max(pptBio.textPad, -Math.round(this.font.main.Size / 2)); + bioSet.textPad = Math.max(bioSet.textPad, -Math.round(this.font.main.Size / 2)); $Bio.gr(1, 1, false, g => { - this.font.main_h = Math.round(g.CalcTextHeight('String', this.font.main) + pptBio.textPad); - this.font.lyrics_h = Math.round(g.CalcTextHeight('STRING', this.font.lyrics) + pptBio.textPad); + this.font.main_h = Math.round(g.CalcTextHeight('String', this.font.main) + bioSet.textPad); + this.font.lyrics_h = Math.round(g.CalcTextHeight('STRING', this.font.lyrics) + bioSet.textPad); this.font.heading_h = g.CalcTextHeight('String', this.font.heading); this.font.small_h = Math.max(g.CalcTextHeight('0', this.font.small), 8); }); const min_line_y = this.font.heading_h; - const max_line_y = Math.round(this.font.heading_h * (pptBio.hdLine == 1 ? 1.25 : 1.1) + (pptBio.hdLine == 1 ? this.heading.linePad : 0)); - this.heading.line_y = pptBio.heading ? Math.max(min_line_y, max_line_y) : 0; - const min_h = pptBio.hdLine == 1 ? this.heading.line_y : this.font.heading_h + (pptBio.hdLine == 1 ? this.heading.linePad : 0); - this.heading.h = pptBio.heading ? Math.max(Math.round(this.heading.line_y + (pptBio.gap * (pptBio.hdLine == 1 ? 0.75 : 0.25)) + this.heading.pad), min_h) : 0; + const max_line_y = Math.round(this.font.heading_h * (bioSet.hdLine == 1 ? 1.25 : 1.1) + (bioSet.hdLine == 1 ? this.heading.linePad : 0)); + this.heading.line_y = bioSet.heading ? Math.max(min_line_y, max_line_y) : 0; + const min_h = bioSet.hdLine == 1 ? this.heading.line_y : this.font.heading_h + (bioSet.hdLine == 1 ? this.heading.linePad : 0); + this.heading.h = bioSet.heading ? Math.max(Math.round(this.heading.line_y + (bioSet.gap * (bioSet.hdLine == 1 ? 0.75 : 0.25)) + this.heading.pad), min_h) : 0; } dim(c, bg, alpha) { @@ -188,27 +188,27 @@ class UserInterfaceBio { } draw(gr) { - if (this.style.bg) gr.FillSolidRect(this.x - pptBio.borL, this.y, panelBio.w + pptBio.borR, panelBio.h, this.col.bg); - if (pref.styleBlend && albumArt && blendedImg) { - gr.FillSolidRect(0, 0, pref.layout === 'artwork' || pref.biographyLayout === 'full' ? ww : pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : ww * 0.5, geo.topMenuHeight, col.bg); // Hide alpha overlapping at the top - if (pref.layout === 'artwork') gr.FillSolidRect(0, this.y + this.h, ww, geo.lowerBarHeight, col.bg); // Hide alpha overlapping at the bottom - if (UIHacks.Aero.Effect === 2) gr.DrawLine(0, 0, pref.layout === 'artwork' || pref.biographyLayout === 'full' ? ww : pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : ww * 0.5 - 1, 0, 1, col.bg); // UIHacks aero glass shadow frame fix - needed for style Blend - if (pref.layout === 'default' && pref.biographyLayout === 'full') { - gr.DrawImage(blendedImg, 0, 0, ww, wh, 0, 0, blendedImg.Width, blendedImg.Height); + if (this.style.bg) gr.FillSolidRect(this.x - bioSet.borL, this.y, bio.panel.w + bioSet.borR, bio.panel.h, this.col.bg); + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) { + gr.FillSolidRect(0, 0, grSet.layout === 'artwork' || grSet.biographyLayout === 'full' ? grm.ui.ww : grSet.panelWidthAuto ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grm.ui.ww * 0.5, grm.ui.topMenuHeight, grCol.bg); // Hide alpha overlapping at the top + if (grSet.layout === 'artwork') gr.FillSolidRect(0, this.y + this.h, grm.ui.ww, grm.ui.lowerBarHeight, grCol.bg); // Hide alpha overlapping at the bottom + if (UIHacks.Aero.Effect === 2) gr.DrawLine(0, 0, grSet.layout === 'artwork' || grSet.biographyLayout === 'full' ? grm.ui.ww : grSet.panelWidthAuto ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grm.ui.ww * 0.5 - 1, 0, 1, grCol.bg); // UIHacks aero glass shadow frame fix - needed for style Blend + if (grSet.layout === 'default' && grSet.biographyLayout === 'full') { + gr.DrawImage(grCol.imgBlended, 0, 0, grm.ui.ww, grm.ui.wh, 0, 0, grCol.imgBlended.Width, grCol.imgBlended.Height); } else { - gr.DrawImage(blendedImg, pref.panelWidthAuto || pref.layout === 'artwork' ? 0 : this.x - this.w, pref.layout === 'artwork' ? this.h - panelBio.h : this.h - panelBio.h - geo.lowerBarHeight, pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : ww, wh, - pref.panelWidthAuto || pref.layout === 'artwork' ? 0 : this.x - this.w, pref.layout === 'artwork' ? this.h - panelBio.h : this.h - panelBio.h - geo.lowerBarHeight, pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : blendedImg.Width, blendedImg.Height); + gr.DrawImage(grCol.imgBlended, grSet.panelWidthAuto || grSet.layout === 'artwork' ? 0 : this.x - this.w, grSet.layout === 'artwork' ? this.h - bio.panel.h : this.h - bio.panel.h - grm.ui.lowerBarHeight, grSet.panelWidthAuto ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grm.ui.ww, grm.ui.wh, + grSet.panelWidthAuto || grSet.layout === 'artwork' ? 0 : this.x - this.w, grSet.layout === 'artwork' ? this.h - bio.panel.h : this.h - bio.panel.h - grm.ui.lowerBarHeight, grSet.panelWidthAuto ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grCol.imgBlended.Width, grCol.imgBlended.Height); } } } getAccentColour() { let valid = false; - if (this.blur.dark && pptBio.text_hUse) { - const c = pptBio.text_h.replace(/[^0-9.,-]/g, '').split(/[,-]/); + if (this.blur.dark && bioSet.text_hUse) { + const c = bioSet.text_h.replace(/[^0-9.,-]/g, '').split(/[,-]/); if (c.length == 3 || c.length == 4) valid = true; } - this.col.accent = !this.blur.dark || pptBio.text_hUse && valid ? this.col.text_h : pptBio.themed && pptBio.theme == 9 ? RGB(104, 225, 255) : RGB(128, 228, 27); + this.col.accent = !this.blur.dark || bioSet.text_hUse && valid ? this.col.text_h : bioSet.themed && bioSet.theme == 9 ? RGB(104, 225, 255) : RGB(128, 228, 27); } getBlend(c1, c2, f) { @@ -224,33 +224,33 @@ class UserInterfaceBio { getBlurColours() { switch (true) { - case !pptBio.themed: // has to be able to create image: uses original themes - if (pptBio.theme > 4) pptBio.theme = 0; // reset if coming from themed & out of bounds - this.style.isBlur = pptBio.theme > 0; + case !bioSet.themed: // has to be able to create image: uses original themes + if (bioSet.theme > 4) bioSet.theme = 0; // reset if coming from themed & out of bounds + this.style.isBlur = bioSet.theme > 0; this.blur = { - alpha: $Bio.clamp(pptBio.blurAlpha, 0, 100) / 30, - blend: pptBio.theme == 2, - blendAlpha: $Bio.clamp($Bio.clamp(pptBio.blurAlpha, 0, 100) * 105 / 30, 0, 255), - dark: pptBio.theme == 1 || pptBio.theme == 4, - level: pptBio.theme == 2 ? 91.05 - $Bio.clamp(pptBio.blurTemp, 1.05, 90) : $Bio.clamp(pptBio.blurTemp * 2, 0, 254), - light: pptBio.theme == 3 + alpha: $Bio.clamp(bioSet.blurAlpha, 0, 100) / 30, + blend: bioSet.theme == 2, + blendAlpha: $Bio.clamp($Bio.clamp(bioSet.blurAlpha, 0, 100) * 105 / 30, 0, 255), + dark: bioSet.theme == 1 || bioSet.theme == 4, + level: bioSet.theme == 2 ? 91.05 - $Bio.clamp(bioSet.blurTemp, 1.05, 90) : $Bio.clamp(bioSet.blurTemp * 2, 0, 254), + light: bioSet.theme == 3 } if (this.blur.dark) { this.col.bg_light = RGBA(0, 0, 0, Math.min(160 / this.blur.alpha, 255)); this.col.bg_dark = RGBA(0, 0, 0, Math.min(80 / this.blur.alpha, 255)); - if (pptBio.typeOverlay && !pptBio.rectOvUse) this.col.rectOv = RGBA(0, 0, 0, 255 - this.overlay.strength); + if (bioSet.typeOverlay && !bioSet.rectOvUse) this.col.rectOv = RGBA(0, 0, 0, 255 - this.overlay.strength); } if (this.blur.light) { this.col.bg_light = RGBA(255, 255, 255, Math.min(160 / this.blur.alpha, 255)); this.col.bg_dark = RGBA(255, 255, 255, Math.min(205 / this.blur.alpha, 255)); - if (pptBio.typeOverlay && !pptBio.rectOvUse) this.col.rectOv = RGBA(255, 255, 255, 255 - this.overlay.strength); + if (bioSet.typeOverlay && !bioSet.rectOvUse) this.col.rectOv = RGBA(255, 255, 255, 255 - this.overlay.strength); } break; - case pptBio.themed: // sent image - this.style.isBlur = pptBio.theme || pptBio.themeBgImage; - this.blur.blend = pptBio.theme == 6 || pptBio.theme == 7; - this.blur.dark = pptBio.theme == 1 && !pptBio.themeLight || pptBio.theme == 2 && !pptBio.themeLight || pptBio.theme == 3 || pptBio.theme == 4 || pptBio.theme == 5 || pptBio.theme == 9; - this.blur.light = pptBio.theme == 1 && pptBio.themeLight || pptBio.theme == 2 && pptBio.themeLight || pptBio.theme == 8; + case bioSet.themed: // sent image + this.style.isBlur = bioSet.theme || bioSet.themeBgImage; + this.blur.blend = bioSet.theme == 6 || bioSet.theme == 7; + this.blur.dark = bioSet.theme == 1 && !bioSet.themeLight || bioSet.theme == 2 && !bioSet.themeLight || bioSet.theme == 3 || bioSet.theme == 4 || bioSet.theme == 5 || bioSet.theme == 9; + this.blur.light = bioSet.theme == 1 && bioSet.themeLight || bioSet.theme == 2 && bioSet.themeLight || bioSet.theme == 8; break; } } @@ -265,8 +265,8 @@ class UserInterfaceBio { this.setStarType(); this.getUIColours(); this.getItemColours(); - if (pptBio.themed) { - if ((pptBio.theme == 0 || pptBio.theme == 6 || pptBio.theme == 7) && this.themeColour && pptBio.themeColour) { + if (bioSet.themed) { + if ((bioSet.theme == 0 || bioSet.theme == 6 || bioSet.theme == 7) && this.themeColour && bioSet.themeColour) { // nothing to do } else { this.themeColour = { @@ -297,59 +297,59 @@ class UserInterfaceBio { } catch (e) { return false; } }); } - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`] || 14; + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`] || 14; - if (pref.customThemeFonts) this.font.main = ft.biography; - else if (pptBio.custFontUse && pptBio.custFont.length) { - const custFont = $Bio.split(pptBio.custFont, 1); + if (grSet.customThemeFonts) this.font.main = grFont.biography; + else if (bioSet.custFontUse && bioSet.custFont.length) { + const custFont = $Bio.split(bioSet.custFont, 1); this.font.main = gdi.Font(custFont[0], Math.max(Math.round($Bio.value(custFont[1], 16, 0)), 1), Math.round($Bio.value(custFont[2], 0, 0))); } else if (this.dui) this.font.main = window.GetFontDUI(3); else this.font.main = window.GetFontCUI(0); - if (!this.font.main || !pref.customThemeFonts && DetectWine() && /tahoma/i.test(this.font.main.Name)) { // Windows: check still needed (test MS Serif or Modern, neither can be used); Wine: tahoma is default system font, but bold and some unicode characters don't work: if Wine + tahoma detected changed to Segoe UI (if that's not installed, tahoma is still used) - this.font.main = gdi.Font(fontDefault, 16, 0); + if (!this.font.main || !grSet.customThemeFonts && DetectWine() && /tahoma/i.test(this.font.main.Name)) { // Windows: check still needed (test MS Serif or Modern, neither can be used); Wine: tahoma is default system font, but bold and some unicode characters don't work: if Wine + tahoma detected changed to Segoe UI (if that's not installed, tahoma is still used) + this.font.main = gdi.Font(grFont.fontDefault, 16, 0); $Bio.trace('Spider Monkey Panel is unable to use your default font. Using Segoe UI at default size & style instead'); } - if (this.font.main.Size != biographyFontSize) pptBio.zoomFont = 100; - // pref.layout === 'artwork' ? pptBio.baseFontSizeBio_artwork : pptBio.baseFontSizeBio_default = this.font.headingBaseSize = this.font.main.Size; + if (this.font.main.Size != biographyFontSize) bioSet.zoomFont = 100; + // pref.layout === 'artwork' ? bioSet.baseFontSizeBio_artwork : bioSet.baseFontSizeBio_default = this.font.headingBaseSize = this.font.main.Size; this.font.headingBaseSize = biographyFontSize; - this.font.zoomSize = Math.max(Math.round(biographyFontSize * pptBio.zoomFont / 100), 1); + this.font.zoomSize = Math.max(Math.round(biographyFontSize * bioSet.zoomFont / 100), 1); - if (pptBio.custHeadFontUse && pptBio.custHeadFont.length) { - const custHeadFont = $Bio.split(pptBio.custHeadFont, 1); + if (bioSet.custHeadFontUse && bioSet.custHeadFont.length) { + const custHeadFont = $Bio.split(bioSet.custHeadFont, 1); this.font.headingBaseSize = Math.max(Math.round($Bio.value(custHeadFont[1], 16, 0)), 1); this.font.heading = gdi.Font(custHeadFont[0], this.font.headingBaseSize, this.font.headingStyle); this.font.headingStyle = Math.round($Bio.value(custHeadFont[2], 3, 0)); this.font.headingCustom = true; } else { - this.font.headingStyle = pptBio.headFontStyle < 4 ? pptBio.headFontStyle : (pptBio.headFontStyle - 4) * 2; - this.font.heading = gdi.Font(pptBio.headFontStyle < 4 ? this.font.main.Name : 'Segoe UI Semibold', this.font.main.Size, this.font.headingStyle); + this.font.headingStyle = bioSet.headFontStyle < 4 ? bioSet.headFontStyle : (bioSet.headFontStyle - 4) * 2; + this.font.heading = gdi.Font(bioSet.headFontStyle < 4 ? this.font.main.Name : 'Segoe UI Semibold', this.font.main.Size, this.font.headingStyle); } this.font.boldAdjust = this.font.headingStyle != 1 && this.font.headingStyle != 4 && this.font.headingStyle != 5 ? 1 : 1.5; this.font.main = gdi.Font(this.font.main.Name, this.font.zoomSize, this.font.main.Style); this.font.lyrics = gdi.Font(this.font.main.Name, this.font.zoomSize, this.font.lyrics.Style); - this.font.heading = gdi.Font(this.font.heading.Name, Math.max(Math.round(this.font.headingBaseSize * pptBio.zoomFont / 100 * (100 + ((pptBio.zoomHead - 100) / this.font.boldAdjust)) / 100), 6), this.font.headingStyle); - this.heading.pad = $Bio.clamp(this.heading.pad, -pptBio.gap * 2, this.font.main.Size * 5); - this.heading.linePad = $Bio.clamp(this.heading.linePad, -pptBio.gap, this.font.main.Size * 5); + this.font.heading = gdi.Font(this.font.heading.Name, Math.max(Math.round(this.font.headingBaseSize * bioSet.zoomFont / 100 * (100 + ((bioSet.zoomHead - 100) / this.font.boldAdjust)) / 100), 6), this.font.headingStyle); + this.heading.pad = $Bio.clamp(this.heading.pad, -bioSet.gap * 2, this.font.main.Size * 5); + this.heading.linePad = $Bio.clamp(this.heading.linePad, -bioSet.gap, this.font.main.Size * 5); - pptBio.zoomFont = Math.round(this.font.zoomSize / biographyFontSize * 100); + bioSet.zoomFont = Math.round(this.font.zoomSize / biographyFontSize * 100); this.font.items.forEach(v => { - const style = pptBio[v[0]] < 4 ? pptBio[v[0]] : (pptBio[v[0]] - 4) * 2; - this.font[v[1]] = gdi.Font(pptBio[v[0]] < 4 ? this.font.main.Name : 'Segoe UI Semibold', this.font.main.Size, style); + const style = bioSet[v[0]] < 4 ? bioSet[v[0]] : (bioSet[v[0]] - 4) * 2; + this.font[v[1]] = gdi.Font(bioSet[v[0]] < 4 ? this.font.main.Name : 'Segoe UI Semibold', this.font.main.Size, style); }); this.font.message = gdi.Font(this.font.main.Name, this.font.main.Size * 1.5, 1); this.font.small = gdi.Font(this.font.main.Name, Math.round(this.font.main.Size * 12 / 14), this.font.main.Style); - this.narrowSbarWidth = pptBio.narrowSbarWidth == 0 ? $Bio.clamp(Math.floor(this.font.main.Size / 7), 2, 10) : pptBio.narrowSbarWidth; + this.narrowSbarWidth = bioSet.narrowSbarWidth == 0 ? $Bio.clamp(Math.floor(this.font.main.Size / 7), 2, 10) : bioSet.narrowSbarWidth; if (this.id.local) { this.font.main = c_font; - this.font.items.forEach(v => this.font[v[1]] = gdi.Font(this.font.main.Name, this.font.main.Size, pptBio[v[0]])); + this.font.items.forEach(v => this.font[v[1]] = gdi.Font(this.font.main.Name, this.font.main.Size, bioSet[v[0]])); this.font.message = gdi.Font(this.font.main.Name, this.font.main.Size * 1.5, 1); - if (pptBio.sbarShow) { + if (bioSet.sbarShow) { this.sbar.type = 0; this.sbar.w = c_scr_w; this.sbar.but_w = this.sbar.w + 1; @@ -372,7 +372,7 @@ class UserInterfaceBio { this.col.txt = this.col.text; customColText = true; } - if (this.col.text_h === '') this.col.txt_h = pptBio.themed && pptBio.theme == 9 ? RGB(104, 225, 255) : this.blur.blend ? this.setBrightness(this.col.txt_h, lightBg ? -10 : 10) : this.blur.dark ? RGB(255, 255, 255) : this.blur.light ? (pptBio.themed && (pptBio.theme == 1 || pptBio.themed == 2) ? RGB(25, 25, 25) : RGB(71, 129, 183)) : this.col.txt_h; + if (this.col.text_h === '') this.col.txt_h = bioSet.themed && bioSet.theme == 9 ? RGB(104, 225, 255) : this.blur.blend ? this.setBrightness(this.col.txt_h, lightBg ? -10 : 10) : this.blur.dark ? RGB(255, 255, 255) : this.blur.light ? (bioSet.themed && (bioSet.theme == 1 || bioSet.themed == 2) ? RGB(25, 25, 25) : RGB(71, 129, 183)) : this.col.txt_h; else { this.col.txt_h = this.col.text_h; customColText_h = true; @@ -394,50 +394,50 @@ class UserInterfaceBio { this.col.bg2 = lightBg ? 0x04000000 : 0x04ffffff; this.col.bg3 = lightBg ? 0x04ffffff : 0x04000000; - if (pptBio.swapCol) { + if (bioSet.swapCol) { const colH = this.col.txt_h; this.col.txt_h = this.col.txt; this.col.txt = colH; } - if (!customColText || pptBio.swapCol) this.col.text = !pptBio.highlightText ? this.col.txt : this.col.txt_h; - if (!customColText_h || pptBio.swapCol) this.col.text_h = !pptBio.highlightText ? this.col.txt_h : this.col.txt; + if (!customColText || bioSet.swapCol) this.col.text = !bioSet.highlightText ? this.col.txt : this.col.txt_h; + if (!customColText_h || bioSet.swapCol) this.col.text_h = !bioSet.highlightText ? this.col.txt_h : this.col.txt; this.col.shadow = this.getSelTextCol(this.col.text_h); - if (this.col.summary === '') this.col.summary = !pptBio.highlightSummary ? this.col.txt : this.col.txt_h; + if (this.col.summary === '') this.col.summary = !bioSet.highlightSummary ? this.col.txt : this.col.txt_h; this.col.t = this.style.bg ? this.getButCol(this.col.bg) : 200; if (this.stars) { this.col.stars = RGB(255, 190, 0); ['starOn', 'starOff', 'starBor'].forEach((v, i) => { - this.col[v] = i < 2 ? (this.stars == 2 ? $Bio.RGBtoRGBA(this.col.stars === '' ? pptBio.highlightStars ? this.col.txt : this.col.txt_h : this.col.stars, !i ? 232 : 60) : - this.style.bg || !this.style.bg && !this.style.trans || this.blur.dark || this.blur.light ? $Bio.RGBtoRGBA(this.col.stars === '' ? pptBio.highlightStars ? this.col.txt_h : this.col.txt : this.col.stars, !i ? 232 : 60) : (this.col.stars === '' ? RGBA(255, 255, 255, !i ? 232 : 60) : $Bio.RGBtoRGBA(this.col.stars, !i ? 232 : 60))) : RGBA(0, 0, 0, 0); + this.col[v] = i < 2 ? (this.stars == 2 ? $Bio.RGBtoRGBA(this.col.stars === '' ? bioSet.highlightStars ? this.col.txt : this.col.txt_h : this.col.stars, !i ? 232 : 60) : + this.style.bg || !this.style.bg && !this.style.trans || this.blur.dark || this.blur.light ? $Bio.RGBtoRGBA(this.col.stars === '' ? bioSet.highlightStars ? this.col.txt_h : this.col.txt : this.col.stars, !i ? 232 : 60) : (this.col.stars === '' ? RGBA(255, 255, 255, !i ? 232 : 60) : $Bio.RGBtoRGBA(this.col.stars, !i ? 232 : 60))) : RGBA(0, 0, 0, 0); }); } this.col.bottomLine = this.getLineCol('bottom'); this.col.centerLine = this.getLineCol('center'); - this.col.sectionLine = this.col.line === '' && !pptBio.colLineDark ? (pptBio.highlightHdLine ? this.col.text_h : this.col.text) & 0x40ffffff : $Bio.RGBAtoRGB(this.col.bottomLine, this.col.bg == 0 ? 0xff000000 : this.col.bg) & 0x56ffffff; + this.col.sectionLine = this.col.line === '' && !bioSet.colLineDark ? (bioSet.highlightHdLine ? this.col.text_h : this.col.text) & 0x40ffffff : $Bio.RGBAtoRGB(this.col.bottomLine, this.col.bg == 0 ? 0xff000000 : this.col.bg) & 0x56ffffff; this.col.edBg = (this.blur.dark ? RGB(0, 0, 0) : this.blur.light ? RGB(255, 255, 255) : this.col.bg) & 0x99ffffff; this.col.imgBor = this.col.text & 0x25ffffff; const col_txt = this.col.txt == -1 ? RGB(240, 240, 240) : this.col.txt; const col_txt_h = this.col.txt_h == -1 ? RGB(240, 240, 240) : this.col.txt_h; this.col.dropShadow = RGB(18, 26, 46); - this.col.source = this.blur.dark ? (pptBio.highlightSubHd ? col_txt_h : col_txt) : !this.blur.light && (pptBio.sourceStyle == 1 || pptBio.sourceStyle == 3) && (pptBio.headFontStyle != 1 && pptBio.headFontStyle != 3) ? this.dim(pptBio.highlightSubHd ? this.col.txt_h : this.col.txt, !window.IsTransparent ? this.col.bg : 0xff000000, 240) : pptBio.highlightSubHd ? this.col.txt_h : this.col.txt; - this.col.track = this.blur.dark ? (pptBio.highlightSubHd ? col_txt_h : col_txt) : !this.blur.light && (pptBio.trackStyle == 1 || pptBio.trackStyle == 3) && (pptBio.headFontStyle != 1 && pptBio.headFontStyle != 3) ? this.dim(pptBio.highlightSubHd ? this.col.txt_h : this.col.txt, !window.IsTransparent ? this.col.bg : 0xff000000, 240) : pptBio.highlightSubHd ? this.col.txt_h : this.col.txt; + this.col.source = this.blur.dark ? (bioSet.highlightSubHd ? col_txt_h : col_txt) : !this.blur.light && (bioSet.sourceStyle == 1 || bioSet.sourceStyle == 3) && (bioSet.headFontStyle != 1 && bioSet.headFontStyle != 3) ? this.dim(bioSet.highlightSubHd ? this.col.txt_h : this.col.txt, !window.IsTransparent ? this.col.bg : 0xff000000, 240) : bioSet.highlightSubHd ? this.col.txt_h : this.col.txt; + this.col.track = this.blur.dark ? (bioSet.highlightSubHd ? col_txt_h : col_txt) : !this.blur.light && (bioSet.trackStyle == 1 || bioSet.trackStyle == 3) && (bioSet.headFontStyle != 1 && bioSet.headFontStyle != 3) ? this.dim(bioSet.highlightSubHd ? this.col.txt_h : this.col.txt, !window.IsTransparent ? this.col.bg : 0xff000000, 240) : bioSet.highlightSubHd ? this.col.txt_h : this.col.txt; - this.sbar.col = this.blur.dark || this.blur.light ? 1 : pptBio.sbarCol; + this.sbar.col = this.blur.dark || this.blur.light ? 1 : bioSet.sbarCol; if (this.col.frame === '') this.col.frame = (this.blur.dark ? 0xff808080 : this.blur.light ? 0xA330AFED : this.col.bgSel) & 0xd0ffffff; if (this.col.rectOv === '') this.col.rectOv = this.col.bg; this.col.rectOv = $Bio.RGBtoRGBA(this.col.rectOv, 255 - this.overlay.strength); if (this.col.rectOvBor === '') { - this.col.rectOvBor = pptBio.highlightOvBor ? this.col.txt_h : this.col.txt; + this.col.rectOvBor = bioSet.highlightOvBor ? this.col.txt_h : this.col.txt; this.col.rectOvBor = $Bio.RGBtoRGBA(this.col.rectOvBor, 228); } - if (!pptBio.heading) return; - this.col.headBtn = this.col.headingBtn === '' ? !pptBio.highlightHdBtn ? this.col.txt : this.col.txt_h : this.col.headingBtn; - if (this.col.headingText === '') this.col.headingText = !pptBio.highlightHdText ? this.col.txt : this.col.txt_h; + if (!bioSet.heading) return; + this.col.headBtn = this.col.headingBtn === '' ? !bioSet.highlightHdBtn ? this.col.txt : this.col.txt_h : this.col.headingBtn; + if (this.col.headingText === '') this.col.headingText = !bioSet.highlightHdText ? this.col.txt : this.col.txt_h; ['blend1', 'blend2', 'blend3'].forEach((v, i) => { this.col[v] = this.blur.blend ? this.col.headBtn & RGBA(255, 255, 255, i == 2 ? 40 : 12) : @@ -449,9 +449,9 @@ class UserInterfaceBio { } getLineCol(type) { - if (!pptBio.colLineDark) return this.col.line === '' ? this.getBlend(this.blur.dark ? RGB(0, 0, 0) : this.blur.light ? RGB(255, 255, 255) : this.col.bg == 0 ? 0xff000000 : this.col.bg, pptBio.highlightHdLine ? this.col.txt_h : this.col.txt, type == 'bottom' || this.style.isBlur ? 0.25 : 0.5) : this.col.line; + if (!bioSet.colLineDark) return this.col.line === '' ? this.getBlend(this.blur.dark ? RGB(0, 0, 0) : this.blur.light ? RGB(255, 255, 255) : this.col.bg == 0 ? 0xff000000 : this.col.bg, bioSet.highlightHdLine ? this.col.txt_h : this.col.txt, type == 'bottom' || this.style.isBlur ? 0.25 : 0.5) : this.col.line; const lightBg = this.isLightBackground(); - const nearBlack = ((pptBio.theme == 1 || pptBio.theme == 2) && !this.col.themeLight || (pptBio.theme == 0 || pptBio.theme == 6 || pptBio.theme == 7) && !lightBg) && this.getColSat(this.col.bg) < 45; + const nearBlack = ((bioSet.theme == 1 || bioSet.theme == 2) && !this.col.themeLight || (bioSet.theme == 0 || bioSet.theme == 6 || bioSet.theme == 7) && !lightBg) && this.getColSat(this.col.bg) < 45; const alpha = !lightBg ? nearBlack ? 0x20ffffff : 0x50000000 : 0x30000000; return this.col.text & alpha; } @@ -467,12 +467,12 @@ class UserInterfaceBio { getParams() { this.calcText(); - panelBio.setStyle(); - butBio.createStars(); - butBio.setTooltipFont(); - txt.getSubHeadWidths(); - txt.artCalc(); - txt.albCalc(); + bio.panel.setStyle(); + bio.but.createStars(); + bio.but.setTooltipFont(); + bio.txt.getSubHeadWidths(); + bio.txt.artCalc(); + bio.txt.albCalc(); } getSelTextCol(c, bypass) { @@ -480,9 +480,9 @@ class UserInterfaceBio { } getUIColours() { - const colours = Object.keys(colourSelectorBio); - this.themeColour = pptBio.themeColour && colours.length ? colourSelectorBio[colours[pptBio.themeColour]] : null; - if (pptBio.themed && (pptBio.theme == 0 || pptBio.theme == 6 || pptBio.theme == 7) && this.themeColour && pptBio.themeColour) { + const colours = Object.keys(bioColourSelector); + this.themeColour = bioSet.themeColour && colours.length ? bioColourSelector[colours[bioSet.themeColour]] : null; + if (bioSet.themed && (bioSet.theme == 0 || bioSet.theme == 6 || bioSet.theme == 7) && this.themeColour && bioSet.themeColour) { this.col.txt = this.themeColour.text; if (this.col.bg === '') this.col.bg = this.themeColour.background; if (this.col.bgSel === '') this.col.bgSel = this.img.blurDark ? RGBA(255, 255, 255, 36) : this.img.blurLight ? RGBA(50, 50, 50, 36) : this.themeColour.selection; @@ -506,7 +506,7 @@ class UserInterfaceBio { } isLightBackground() { - if (pptBio.themed && (pptBio.theme == 0 || pptBio.theme == 6 || pptBio.theme == 7) && this.themeColour && pptBio.themeColour) { + if (bioSet.themed && (bioSet.theme == 0 || bioSet.theme == 6 || bioSet.theme == 7) && this.themeColour && bioSet.themeColour) { // do nothing } else if (this.blur.light) return true; else if (this.blur.dark) return false; @@ -519,94 +519,94 @@ class UserInterfaceBio { lines(gr) { if (!this.id.c_c) return; - if (pptBio.artistView && !pptBio.img_only || !pptBio.artistView && !pptBio.img_only) { - gr.DrawRect(0, 0, panelBio.w - 1, panelBio.h - 1, 1, RGB(155, 155, 155)); - gr.DrawRect(1, 1, panelBio.w - 3, panelBio.h - 3, 1, RGB(0, 0, 0)); + if (bioSet.artistView && !bioSet.img_only || !bioSet.artistView && !bioSet.img_only) { + gr.DrawRect(0, 0, bio.panel.w - 1, bio.panel.h - 1, 1, RGB(155, 155, 155)); + gr.DrawRect(1, 1, bio.panel.w - 3, bio.panel.h - 3, 1, RGB(0, 0, 0)); } } refreshProp() { - if (panelBio.style.inclTrackRev == 1) txt.logScrollPos(); - - this.heading.pad = pptBio.hdPad; - this.heading.linePad = pptBio.hdLinePad; - panelBio.style.fullWidthHeading = pptBio.heading && pptBio.fullWidthHeading; - panelBio.id.focus = pptBio.focus; - panelBio.id.lyricsSource = false; - panelBio.id.nowplayingSource = false; - panelBio.id.propsSource = false; + if (bio.panel.style.inclTrackRev == 1) bio.txt.logScrollPos(); + + this.heading.pad = bioSet.hdPad; + this.heading.linePad = bioSet.hdLinePad; + bio.panel.style.fullWidthHeading = bioSet.heading && bioSet.fullWidthHeading; + bio.panel.id.focus = bioSet.focus; + bio.panel.id.lyricsSource = false; + bio.panel.id.nowplayingSource = false; + bio.panel.id.propsSource = false; for (let i = 0; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && pptBio[`pthTxtReader${i}`] && pptBio[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1]) && !/nowplaying/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1])) { - panelBio.id.lyricsSource = true; - panelBio.id.focus = false; + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && bioSet[`pthTxtReader${i}`] && bioSet[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1]) && !/nowplaying/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1])) { + bio.panel.id.lyricsSource = true; + bio.panel.id.focus = false; break; } } for (let i = 0; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && /nowplaying/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1])) { - panelBio.id.nowplayingSource = true; - panelBio.id.focus = false; + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && /nowplaying/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1])) { + bio.panel.id.nowplayingSource = true; + bio.panel.id.focus = false; break; } } for (let i = 0; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && /item_properties/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1])) { - panelBio.id.propsSource = true; + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && /item_properties/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1])) { + bio.panel.id.propsSource = true; break; } } - if (!lyricsBio && panelBio.id.lyricsSource) lyricsBio = new LyricsBio(); - panelBio.id.lookUp = pptBio.lookUp; + if (!bio.lyrics && bio.panel.id.lyricsSource) bio.lyrics = new BioLyrics(); + bio.panel.id.lookUp = bioSet.lookUp; this.show = { - btnBg: pptBio.hdShowBtnBg, - btnLabel: pptBio.hdShowBtnLabel, - btnRedLastfm: pptBio.hdShowRedLfm, - headingText: pptBio.hdShowTitle + btnBg: bioSet.hdShowBtnBg, + btnLabel: bioSet.hdShowBtnLabel, + btnRedLastfm: bioSet.hdShowRedLfm, + headingText: bioSet.hdShowTitle }; if (this.show.btnRedLastfm) this.show.btnBg = 1; - panelBio.checkRefreshRates(); - panelBio.setSummary(); - if (pptBio.typeOverlay > 4 || pptBio.typeOverlay < 0) pptBio.typeOverlay = 0; + bio.panel.checkRefreshRates(); + bio.panel.setSummary(); + if (bioSet.typeOverlay > 4 || bioSet.typeOverlay < 0) bioSet.typeOverlay = 0; - pptBio.overlayStrength = $Bio.clamp(pptBio.overlayStrength, 0, 100); - pptBio.overlayGradient = $Bio.clamp(pptBio.overlayGradient, 0, 100); - pptBio.overlayBorderWidth = $Bio.clamp(pptBio.overlayBorderWidth, 1, 20); + bioSet.overlayStrength = $Bio.clamp(bioSet.overlayStrength, 0, 100); + bioSet.overlayGradient = $Bio.clamp(bioSet.overlayGradient, 0, 100); + bioSet.overlayBorderWidth = $Bio.clamp(bioSet.overlayBorderWidth, 1, 20); this.overlay = { - gradient: pptBio.overlayGradient / 10 - 1, - borderWidth: pptBio.typeOverlay != 2 && pptBio.typeOverlay != 4 ? 0 : pptBio.overlayBorderWidth, - strength: $Bio.clamp(255 * (100 - pptBio.overlayStrength) / 100, 0, 255) + gradient: bioSet.overlayGradient / 10 - 1, + borderWidth: bioSet.typeOverlay != 2 && bioSet.typeOverlay != 4 ? 0 : bioSet.overlayBorderWidth, + strength: $Bio.clamp(255 * (100 - bioSet.overlayStrength) / 100, 0, 255) }; - pptBio.reflStrength = $Bio.clamp(pptBio.reflStrength, 0, 100); - pptBio.reflGradient = $Bio.clamp(pptBio.reflGradient, 0, 100); - pptBio.reflSize = $Bio.clamp(pptBio.reflSize, 0, 100); - imgBio.refl = { + bioSet.reflStrength = $Bio.clamp(bioSet.reflStrength, 0, 100); + bioSet.reflGradient = $Bio.clamp(bioSet.reflGradient, 0, 100); + bioSet.reflSize = $Bio.clamp(bioSet.reflSize, 0, 100); + bio.img.refl = { adjust: false, - gradient: pptBio.reflGradient / 10 - 1, - size: $Bio.clamp(pptBio.reflSize / 100, 0.1, 1), - strength: $Bio.clamp(255 * pptBio.reflStrength / 100, 0, 255) + gradient: bioSet.reflGradient / 10 - 1, + size: $Bio.clamp(bioSet.reflSize / 100, 0.1, 1), + strength: $Bio.clamp(255 * bioSet.reflStrength / 100, 0, 255) }; - imgBio.mask.reflection = false; - if (!pptBio.butCustIconFont.length) pptBio.butCustIconFont = 'Segoe UI Symbol'; + bio.img.mask.reflection = false; + if (!bioSet.butCustIconFont.length) bioSet.butCustIconFont = 'Segoe UI Symbol'; this.getColours(); - this.blur.level = pptBio.theme == 2 ? 91.05 - $Bio.clamp(pptBio.blurTemp, 1.05, 90) : $Bio.clamp(pptBio.blurTemp * 2, 0, 254); + this.blur.level = bioSet.theme == 2 ? 91.05 - $Bio.clamp(bioSet.blurTemp, 1.05, 90) : $Bio.clamp(bioSet.blurTemp * 2, 0, 254); - setBiographySize(); - initBiographyColors(); - txt.artCalc(); txt.albCalc(); // Refresh text color + grm.ui.setBiographySize(); + grm.theme.initBiographyColors(); + bio.txt.artCalc(); bio.txt.albCalc(); // Refresh text color - imgBio.mask.reset = true; + bio.img.mask.reset = true; this.setSbar(); - butBio.setSbarIcon(); - alb_scrollbar.active = true; - art_scrollbar.active = true; - [alb_scrollbar, art_scrollbar].forEach(v => { + bio.but.setSbarIcon(); + bio.alb_scrollbar.active = true; + bio.art_scrollbar.active = true; + [bio.alb_scrollbar, bio.art_scrollbar].forEach(v => { v.duration = { drag: 200, - inertia: pptBio.durationTouchFlick, - full: pptBio.durationScroll + inertia: bioSet.durationTouchFlick, + full: bioSet.durationScroll }; v.duration.scroll = Math.round(v.duration.full * 0.8); v.duration.step = Math.round(v.duration.full * 2 / 3); @@ -615,36 +615,36 @@ class UserInterfaceBio { v.setCol(); v.resetAuto(); }); - butBio.createImages('all'); + bio.but.createImages('all'); this.getFont(); this.calcText(); - pptBio.thumbNailGap = Math.max(pptBio.thumbNailGap, 0); - imgBio.createImages(); - filmStrip.set('clear'); - filmStrip.style.image = [pptBio.filmCoverStyle, pptBio.filmPhotoStyle]; - filmStrip.createBorder(); - imgBio.setCrop(true); - panelBio.alb.ix = 0; - panelBio.art.ix = 0; - imgBio.id.albCyc = imgBio.id.curAlbCyc = txt.id.curAlb = txt.id.alb = ''; - - butBio.createStars(true); - - txt.artistReset(true); - txt.albumReset(true); - txt.albumFlush(); - txt.artistFlush(); - txt.rev.cur = ''; - txt.bio.cur = ''; - - txt.bio.loaded = { + bioSet.thumbNailGap = Math.max(bioSet.thumbNailGap, 0); + bio.img.createImages(); + bio.filmStrip.set('clear'); + bio.filmStrip.style.image = [bioSet.filmCoverStyle, bioSet.filmPhotoStyle]; + bio.filmStrip.createBorder(); + bio.img.setCrop(true); + bio.panel.alb.ix = 0; + bio.panel.art.ix = 0; + bio.img.id.albCyc = bio.img.id.curAlbCyc = bio.txt.id.curAlb = bio.txt.id.alb = ''; + + bio.but.createStars(true); + + bio.txt.artistReset(true); + bio.txt.albumReset(true); + bio.txt.albumFlush(); + bio.txt.artistFlush(); + bio.txt.rev.cur = ''; + bio.txt.bio.cur = ''; + + bio.txt.bio.loaded = { am: false, lfm: false, wiki: false, txt: false, ix: -1 }; - txt.rev.loaded = { + bio.txt.rev.loaded = { am: false, lfm: false, wiki: false, @@ -652,31 +652,31 @@ class UserInterfaceBio { ix: -1 }; - txt.bio.fallback = pptBio.bioFallbackText.split('|'); - txt.rev.fallback = pptBio.revFallbackText.split('|'); - txt.loadReader(); - txt.getText(true); - butBio.refresh(true); - imgBio.processSizeFilter(); - imgBio.art.done = false; - imgBio.art.allFilesLength = 0; - imgBio.updImages(); - seeker.upd(); - - const origLock = panelBio.lock; - if (txt.bio.reader || txt.rev.reader) { - panelBio.lock = 0; - if (origLock != panelBio.lock) panelBio.mbtn_up(0, 0, false, true); + bio.txt.bio.fallback = bioSet.bioFallbackText.split('|'); + bio.txt.rev.fallback = bioSet.revFallbackText.split('|'); + bio.txt.loadReader(); + bio.txt.getText(true); + bio.but.refresh(true); + bio.img.processSizeFilter(); + bio.img.art.done = false; + bio.img.art.allFilesLength = 0; + bio.img.updImages(); + bio.seeker.upd(); + + const origLock = bio.panel.lock; + if (bio.txt.bio.reader || bio.txt.rev.reader) { + bio.panel.lock = 0; + if (origLock != bio.panel.lock) bio.panel.mbtn_up(0, 0, false, true); } - if (!panelBio.lock) panelBio.getList(true, true); + if (!bio.panel.lock) bio.panel.getList(true, true); - menBio.playlists_changed(); - panelBio.checkNumServers(); + bio.men.playlists_changed(); + bio.panel.checkNumServers(); - if (pptBio.showFilmStrip && pptBio.autoFilm) txt.getScrollPos(); - if (pptBio.filmStripOverlay && pptBio.showFilmStrip) filmStrip.set(pptBio.filmStripPos); - if (pptBio.text_only && !this.style.isBlur) txt.paint(); + if (bioSet.showFilmStrip && bioSet.autoFilm) bio.txt.getScrollPos(); + if (bioSet.filmStripOverlay && bioSet.showFilmStrip) bio.filmStrip.set(bioSet.filmStripPos); + if (bioSet.text_only && !this.style.isBlur) bio.txt.paint(); } setBrightness(c, percent) { @@ -685,13 +685,13 @@ class UserInterfaceBio { } setSbar() { - pptBio.durationTouchFlick = $Bio.clamp($Bio.value(pptBio.durationTouchFlick, 3000, 0), 0, 5000); - pptBio.durationScroll = $Bio.clamp($Bio.value(pptBio.durationScroll, 500, 0), 0, 5000); - pptBio.flickDistance = $Bio.clamp(pptBio.flickDistance, 0, 10); - pptBio.touchStep = $Bio.clamp(pptBio.touchStep, 1, 10); - pptBio.sbarType = $Bio.value(pptBio.sbarType, 0, 0); - this.sbar.type = Math.min(pptBio.sbarType, 2); - if (pptBio.sbarType == 2) { // light mode only + bioSet.durationTouchFlick = $Bio.clamp($Bio.value(bioSet.durationTouchFlick, 3000, 0), 0, 5000); + bioSet.durationScroll = $Bio.clamp($Bio.value(bioSet.durationScroll, 500, 0), 0, 5000); + bioSet.flickDistance = $Bio.clamp(bioSet.flickDistance, 0, 10); + bioSet.touchStep = $Bio.clamp(bioSet.touchStep, 1, 10); + bioSet.sbarType = $Bio.value(bioSet.sbarType, 0, 0); + this.sbar.type = Math.min(bioSet.sbarType, 2); + if (bioSet.sbarType == 2) { // light mode only this.theme = window.CreateThemeManager('scrollbar'); $Bio.gr(21, 21, false, g => { try { @@ -707,40 +707,40 @@ class UserInterfaceBio { } } catch (e) { this.sbar.type = 1; - pptBio.sbarType = 1; + bioSet.sbarType = 1; } }); } - this.sbar.arrowPad = pptBio.sbarPad; - pptBio.sbarWidth = $Bio.clamp(pptBio.sbarWidth, 0, 400); - pptBio.sbarBase_w = $Bio.clamp(pptBio.sbarBase_w, 0, 400); + this.sbar.arrowPad = bioSet.sbarPad; + bioSet.sbarWidth = $Bio.clamp(bioSet.sbarWidth, 0, 400); + bioSet.sbarBase_w = $Bio.clamp(bioSet.sbarBase_w, 0, 400); - if (pptBio.sbarWidth != pptBio.sbarBase_w) { - pptBio.sbarArrowWidth = Math.min(pptBio.sbarArrowWidth, pptBio.sbarWidth, 400); + if (bioSet.sbarWidth != bioSet.sbarBase_w) { + bioSet.sbarArrowWidth = Math.min(bioSet.sbarArrowWidth, bioSet.sbarWidth, 400); } else { - pptBio.sbarArrowWidth = $Bio.clamp(pptBio.sbarArrowWidth, 0, 400); - pptBio.sbarWidth = $Bio.clamp(pptBio.sbarWidth, pptBio.sbarArrowWidth, 400); + bioSet.sbarArrowWidth = $Bio.clamp(bioSet.sbarArrowWidth, 0, 400); + bioSet.sbarWidth = $Bio.clamp(bioSet.sbarWidth, bioSet.sbarArrowWidth, 400); } - pptBio.sbarBase_w = pptBio.sbarWidth; - this.sbar.w = pptBio.sbarBase_w; - this.sbar.but_w = pptBio.sbarArrowWidth; + bioSet.sbarBase_w = bioSet.sbarWidth; + this.sbar.w = bioSet.sbarBase_w; + this.sbar.but_w = bioSet.sbarArrowWidth; let themed_w = 21; try { themed_w = utils.GetSystemMetrics(2); } catch (e) {} - if (pptBio.sbarWinMetrics) { + if (bioSet.sbarWinMetrics) { this.sbar.w = themed_w; - this.sbar.but_w = pptBio.sbarType != 3 ? this.sbar.w : this.sbar.w * 10 / 18; + this.sbar.but_w = bioSet.sbarType != 3 ? this.sbar.w : this.sbar.w * 10 / 18; } - else if (pptBio.sbarWidth) { - this.sbar.w = RES_4K ? 26 : 12; - this.sbar.but_w = RES_4K ? 26 : 12; + else if (bioSet.sbarWidth) { + this.sbar.w = RES._4K ? 26 : 12; + this.sbar.but_w = RES._4K ? 26 : 12; } - if (!pptBio.sbarWinMetrics && this.sbar.type == 2) this.sbar.w = Math.max(this.sbar.w, 12); - if (!pptBio.sbarShow) this.sbar.w = 0; - this.sbar.but_h = this.sbar.w + (pptBio.sbarType != 2 ? 1 : 0); - if (pptBio.sbarType != 2) { - if (pptBio.sbarButType || !this.sbar.type && this.sbar.but_w < Math.round(15 * $Bio.scale)) this.sbar.but_w += 1; + if (!bioSet.sbarWinMetrics && this.sbar.type == 2) this.sbar.w = Math.max(this.sbar.w, 12); + if (!bioSet.sbarShow) this.sbar.w = 0; + this.sbar.but_h = this.sbar.w + (bioSet.sbarType != 2 ? 1 : 0); + if (bioSet.sbarType != 2) { + if (bioSet.sbarButType || !this.sbar.type && this.sbar.but_w < Math.round(15 * $Bio.scale)) this.sbar.but_w += 1; else if (this.sbar.type == 1 && this.sbar.but_w < Math.round(14 * $Bio.scale)) this.sbar.arrowPad += 1; } const sp = this.sbar.type == 2 || this.sbar.w - this.sbar.but_w > 4 ? 0 : Math.round(1 * $Bio.scale); @@ -749,37 +749,37 @@ class UserInterfaceBio { } setStarType() { - this.stars = $Bio.value(pptBio.star, 1, 1) + 1; - if ((!pptBio.heading || !pptBio.hdBtnShow || pptBio.hdPos == 2) && this.stars == 1) this.stars = 2; - if (!pptBio.amRating && !pptBio.lfmRating) this.stars = 0; + this.stars = $Bio.value(bioSet.star, 1, 1) + 1; + if ((!bioSet.heading || !bioSet.hdBtnShow || bioSet.hdPos == 2) && this.stars == 1) this.stars = 2; + if (!bioSet.amRating && !bioSet.lfmRating) this.stars = 0; } updateProp(prop, value) { - const serverName = pptBio.serverName; + const serverName = bioSet.serverName; Object.entries(prop).forEach(v => { - pptBio[v[0].replace('_internal', '')] = v[1][value]; + bioSet[v[0].replace('_internal', '')] = v[1][value]; }); this.refreshProp(); - if (serverName != pptBio.serverName) { + if (serverName != bioSet.serverName) { window.Reload(); window.NotifyOthers('bio_refresh', 'bio_refresh'); } } wheel(step) { - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`] || 14; - if (!panelBio || butBio.trace('lookUp', panelBio.m.x, panelBio.m.y)) return; - if (vkBio.k('ctrl')) { - if (butBio.trace('heading', panelBio.m.x, panelBio.m.y)) { - if (!butBio.trace_src(panelBio.m.x, panelBio.m.y)) { - pptBio.zoomHead = $Bio.clamp(pptBio.zoomHead += step * 5, 25, 400); - this.font.heading = gdi.Font(this.font.heading.Name, Math.max(Math.round(this.font.headingBaseSize * this.font.zoomSize / (biographyFontSize) * (100 + ((pptBio.zoomHead - 100) / this.font.boldAdjust)) / 100), 6), this.font.headingStyle); - } else butBio.setSrcFontSize(step); - } else if (panelBio.trace.text) { + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`] || 14; + if (!bio.panel || bio.but.trace('lookUp', bio.panel.m.x, bio.panel.m.y)) return; + if (bio.vk.k('ctrl')) { + if (bio.but.trace('heading', bio.panel.m.x, bio.panel.m.y)) { + if (!bio.but.trace_src(bio.panel.m.x, bio.panel.m.y)) { + bioSet.zoomHead = $Bio.clamp(bioSet.zoomHead += step * 5, 25, 400); + this.font.heading = gdi.Font(this.font.heading.Name, Math.max(Math.round(this.font.headingBaseSize * this.font.zoomSize / (biographyFontSize) * (100 + ((bioSet.zoomHead - 100) / this.font.boldAdjust)) / 100), 6), this.font.headingStyle); + } else bio.but.setSrcFontSize(step); + } else if (bio.panel.trace.text) { this.font.zoomSize += step; this.font.zoomSize = Math.max(this.font.zoomSize, 1); this.font.main = gdi.Font(this.font.main.Name, this.font.zoomSize, this.font.main.Style); - this.font.heading = gdi.Font(this.font.heading.Name, Math.max(Math.round(this.font.headingBaseSize * this.font.zoomSize / (biographyFontSize) * (100 + ((pptBio.zoomHead - 100) / this.font.boldAdjust)) / 100), 6), this.font.headingStyle); + this.font.heading = gdi.Font(this.font.heading.Name, Math.max(Math.round(this.font.headingBaseSize * this.font.zoomSize / (biographyFontSize) * (100 + ((bioSet.zoomHead - 100) / this.font.boldAdjust)) / 100), 6), this.font.headingStyle); ['lyrics', 'subHeadSource', 'summary', 'subHeadTrack', 'subHeadWiki'].forEach(v => { this.font[v] = gdi.Font(this.font[v].Name, this.font.zoomSize, this.font[v].Style); @@ -787,32 +787,32 @@ class UserInterfaceBio { this.font.message = gdi.Font(this.font.main.Name, this.font.zoomSize * 1.5, 1); this.font.small = gdi.Font(this.font.main.Name, Math.round(this.font.zoomSize * 12 / 14), this.font.main.Style); - this.narrowSbarWidth = pptBio.narrowSbarWidth == 0 ? $Bio.clamp(Math.floor(this.font.zoomSize / 7), 2, 10) : pptBio.narrowSbarWidth; + this.narrowSbarWidth = bioSet.narrowSbarWidth == 0 ? $Bio.clamp(Math.floor(this.font.zoomSize / 7), 2, 10) : bioSet.narrowSbarWidth; } this.calcText(); - butBio.createStars(); - txt.getSubHeadWidths(); + bio.but.createStars(); + bio.txt.getSubHeadWidths(); window.Repaint(); - pptBio.zoomFont = Math.round(this.font.zoomSize / (biographyFontSize) * 100); + bioSet.zoomFont = Math.round(this.font.zoomSize / (biographyFontSize) * 100); this.refresh(); } - if (vkBio.k('shift') && pptBio.style > 3 && panelBio.trace.text) { + if (bio.vk.k('shift') && bioSet.style > 3 && bio.panel.trace.text) { this.overlay.strength += (-step * 5); this.overlay.strength = $Bio.clamp(this.overlay.strength, 0, 255); - pptBio.overlayStrength = Math.round((255 - this.overlay.strength) / 2.55); + bioSet.overlayStrength = Math.round((255 - this.overlay.strength) / 2.55); this.getColours(); - imgBio.mask.reset = true; - if (!pptBio.typeOverlay) { - imgBio.refl.adjust = true; - if (pptBio.artistView && pptBio.cycPhoto) imgBio.clearArtCache(); - if (panelBio.stndItem()) imgBio.getImages(); - else imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); - } else txt.paint(); + bio.img.mask.reset = true; + if (!bioSet.typeOverlay) { + bio.img.refl.adjust = true; + if (bioSet.artistView && bioSet.cycPhoto) bio.img.clearArtCache(); + if (bio.panel.stndItem()) bio.img.getImages(); + else bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); + } else bio.txt.paint(); } } } -class VkeysBio { +class BioVkeys { k(n) { switch (n) { case 'shift': @@ -825,7 +825,10 @@ class VkeysBio { } } -let colourSelectorBio = {} -let syncBio = { image: () => {} } -const syncerBio = pref.customBiographyDir ? `${globals.customBiographyDir}cache\\biography\\themed\\bioSyncTheme.js` : `${fb.ProfilePath}cache\\biography\\themed\\bioSyncTheme.js`; -if (pptBio.themed && $Bio.file(syncerBio)) include(syncerBio); +/** @global @type {object} */ +let bioColourSelector = {} +/** @global @type {{image: Function}} */ +let bioSync = { image: () => {} } +/** @global @type {string} */ +const bioSyncer = grSet.customBiographyDir ? `${grCfg.customBiographyDir}cache\\biography\\themed\\bioSyncTheme.js` : `${fb.ProfilePath}cache\\biography\\themed\\bioSyncTheme.js`; +if (bioSet.themed && $Bio.file(bioSyncer)) include(bioSyncer); diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-language.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-language.js index 01826c16..829ac92b 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-language.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-language.js @@ -4,7 +4,8 @@ 'use strict'; -const English = { +/** @global @type {object.} */ +const bioEnglish = { ' - Remove from black list (click name): ': ' - Remove from black list (click name): ', ' biography': ' biography', ' first': ' first', @@ -163,7 +164,8 @@ const English = { 'Write existing file info to tags: ': 'Write existing file info to tags: ' } -const simplifiedChinese = { +/** @global @type {object.} */ +const bioSimplifiedChinese = { ' - Remove from black list (click name): ': ' - 从黑名单中删除(点击名称):', ' biography': ' 简介', ' first': ' 首选', @@ -322,7 +324,8 @@ const simplifiedChinese = { 'Write existing file info to tags: ': '将现有文件信息写入标签:' } -const traditionalChinese = { +/** @global @type {object.} */ +const bioTraditionalChinese = { ' - Remove from black list (click name): ': ' - 從黑名單中刪除(點擊名稱): ', ' biography': ' 傳記', ' first': ' 第一的', @@ -481,4 +484,5 @@ const traditionalChinese = { 'Write existing file info to tags: ': '將現有文件信息寫入標籤:' } -const lg = [English, simplifiedChinese, traditionalChinese][pptBio.menuLanguage]; +/** @global @type @type {Array>} */ +const bioLg = [bioEnglish, bioSimplifiedChinese, bioTraditionalChinese][bioSet.menuLanguage]; diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-lastfm.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-lastfm.js index 0b57e93b..9f0137e2 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-lastfm.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-lastfm.js @@ -1,6 +1,6 @@ 'use strict'; -class DldLastfmBio { +class BioDldLastfm { constructor(state_callback) { this.artist; this.con = ''; @@ -44,9 +44,9 @@ class DldLastfmBio { this.pth_bio = p_pth_bio; this.func = null; this.xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); - const URL = this.searchBio == 3 ? `https://${serverBio.lfm.server}/music/${encodeURIComponent(this.artist)}/${encodeURIComponent(this.itemValue[0])}` : this.searchBio == 2 ? `https://www.last.fm/music/${encodeURIComponent(this.artist)}/+albums` : `https://${!this.retry ? serverBio.lfm.server : 'www.last.fm'}/music/${encodeURIComponent(this.artist)}${this.searchBio ? '/+wiki' : ''}`; + const URL = this.searchBio == 3 ? `https://${bio.server.lfm.server}/music/${encodeURIComponent(this.artist)}/${encodeURIComponent(this.itemValue[0])}` : this.searchBio == 2 ? `https://www.last.fm/music/${encodeURIComponent(this.artist)}/+albums` : `https://${!this.retry ? bio.server.lfm.server : 'www.last.fm'}/music/${encodeURIComponent(this.artist)}${this.searchBio ? '/+wiki' : ''}`; this.func = this.analyse; - if (pptBio.multiServer && !force && serverBio.urlDone(md5Bio.hashStr(this.artist + this.pth_bio + URL))) return; + if (bioSet.multiServer && !force && bio.server.urlDone(bioMD5.hashStr(this.artist + this.pth_bio + URL))) return; this.xmlhttp.open('GET', URL); this.xmlhttp.onreadystatechange = this.ready_callback; if (force) this.xmlhttp.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT'); @@ -63,8 +63,8 @@ class DldLastfmBio { analyse(saveOnly) { const noWiki = n => /wiki|vikimiz|\u0412\u0438\u043A\u0438|\u7EF4\u57FA/i.test(n); if (!saveOnly) { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = this.xmlhttp.responseText; const r1 = ['Popular this week', 'Beliebt diese Woche', 'Popular esta semana', 'Populaire cette semaine', 'Popolare questa settimana', '\u4eca\u9031\u306e\u4eba\u6c17\u97f3\u697d', 'Popularne w tym tygodniu', 'Mais ouvida na semana', '\u041f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e \u043d\u0430 \u044d\u0442\u043e\u0439 \u043d\u0435\u0434\u0435\u043b\u0435', 'Popul\u00e4rt denna vecka', 'Bu hafta pop\u00fcler olanlar', '\u672c\u5468\u70ed\u95e8']; const r2 = ['Popular Now', 'Beliebt Jetzt', 'Popular Ahora', 'Populaire Maintenant', 'Popolare Ora', '\u4eca\u4eba\u6c17', 'Popularne Teraz', 'Popular Agora', '\u041f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0441\u0435\u0439\u0447\u0430\u0441', 'Popul\u00e4r Nu', '\u015eimdi Pop\u00fcler', '\u70ed\u95e8 \u73b0\u5728']; @@ -109,7 +109,7 @@ class DldLastfmBio { this.topTracks.push($Bio.titlecase(v.innerText.trim())); i++; }); - this.topTracks = this.topTracks.length ? topTracks[cfg.lang.ix] + this.topTracks.join('\u200b, ') : ''; + this.topTracks = this.topTracks.length ? topTracks[bioCfg.lang.ix] + this.topTracks.join('\u200b, ') : ''; this.tags = this.tags.length ? `Top Tags: ${this.tags.join('\u200b, ')}` : ''; if (this.itemValue[1].length) { r1.forEach((v, i) => itemName[1] = itemName[1].replace(RegExp(v, 'i'), r2[i])); @@ -121,8 +121,8 @@ class DldLastfmBio { } this.pop += `${(this.itemValue[1].length ? '; ' : '\r\n\r\n') + $Bio.titlecase(itemName[0])}: ${this.itemValue[0]}`; } - this.simArtists = this.simArtists.length ? serverBio.similar[cfg.lang.ix] + this.simArtists.join('\u200b, ') : ''; - docBio.close(); + this.simArtists = this.simArtists.length ? bio.server.similar[bioCfg.lang.ix] + this.simArtists.join('\u200b, ') : ''; + bioDoc.close(); this.searchBio = 1; return this.search(this.artist, this.fo_bio, this.pth_bio); } @@ -130,15 +130,15 @@ class DldLastfmBio { let factbox = ''; this.con = ''; $Bio.htmlParse(div.getElementsByTagName('div'), 'className', 'wiki-content', v => { - this.con = serverBio.format(v.innerHTML); + this.con = bio.server.format(v.innerHTML); return true; }); $Bio.htmlParse(div.getElementsByTagName('li'), 'className', 'factbox-item', v => { factbox = ''; - factbox = serverBio.format(v.innerHTML.replace(/<\/H4>/gi, ': ').replace(/\s*<\/LI>\s*/gi, ', ').replace(/\s*Show all members\u2026\s*/gi, '')).replace(/\s+/g, ' ').replace(/,$/, ''); - this.con = txt.add([factbox], this.con); + factbox = bio.server.format(v.innerHTML.replace(/<\/H4>/gi, ': ').replace(/\s*<\/LI>\s*/gi, ', ').replace(/\s*Show all members\u2026\s*/gi, '')).replace(/\s+/g, ' ').replace(/,$/, ''); + this.con = bio.txt.add([factbox], this.con); }); - docBio.close(); + bioDoc.close(); if (!this.retry) { this.searchBio = 2; return this.search(this.artist, this.fo_bio, this.pth_bio); @@ -154,7 +154,7 @@ class DldLastfmBio { i++; if (i == 10) return true; }); - docBio.close(); + bioDoc.close(); if (popAlbums.length) { const mapAlbums = this.topAlbums.map(v => $Bio.cut(v)); const match = mapAlbums.includes($Bio.cut(popAlbums[0])); @@ -163,7 +163,7 @@ class DldLastfmBio { } this.topAlbums = [...new Set(this.topAlbums)]; this.topAlbums.length = Math.min(6, this.topAlbums.length); - this.topAlbums = this.topAlbums.length ? topAlb[cfg.lang.ix] + this.topAlbums.join('\u200b, ') : ''; + this.topAlbums = this.topAlbums.length ? topAlb[bioCfg.lang.ix] + this.topAlbums.join('\u200b, ') : ''; if (this.itemValue[0]) { this.searchBio = 3; return this.search(this.artist, this.fo_bio, this.pth_bio); @@ -175,7 +175,7 @@ class DldLastfmBio { this.itemValue[2] = v.innerText.trim().split(',')[0]; return true }); - docBio.close(); + bioDoc.close(); if (this.itemValue[0].length) { const item = this.itemDate.length && this.itemValue[2].length && this.itemDate.length != this.itemValue[2].length ? ` (${this.itemDate} - ${this.itemValue[2]})` : this.itemValue[2].length ? ` (${this.itemValue[2]})` : this.itemDate.length ? ` (${this.itemDate})` : ''; if (item) this.pop += item; @@ -183,15 +183,15 @@ class DldLastfmBio { break; } } - if ((!this.con.length || this.con.length < 45 && noWiki(this.con)) && serverBio.langFallback && !this.retry) { + if ((!this.con.length || this.con.length < 45 && noWiki(this.con)) && bio.server.langFallback && !this.retry) { this.retry = true; this.searchBio = 1; return this.search(this.artist, this.fo_bio, this.pth_bio); } if (this.con.length < 45 && noWiki(this.con)) this.con = ''; - this.con = txt.add([this.tags, this.topAlbums, this.topTracks], this.con); + this.con = bio.txt.add([this.tags, this.topAlbums, this.topTracks], this.con); this.con += this.pop; - this.con = txt.add([this.simArtists], this.con); + this.con = bio.txt.add([this.simArtists], this.con); if (this.scrobbles[1].length && this.counts[1].length || this.scrobbles[0].length && this.counts[0].length) this.con += (`\r\n\r\nLast.fm: ${this.counts[1].length ? `${this.scrobbles[1]} ${this.counts[1]}; ` : ''}${this.counts[0].length ? `${this.scrobbles[0]} ${this.counts[0]}` : ''}`); this.con = this.con.trim(); if (!this.con.length) { @@ -201,26 +201,26 @@ class DldLastfmBio { if (!this.fo_bio) return; $Bio.buildPth(this.fo_bio); $Bio.save(this.pth_bio, this.con, true); - serverBio.res(); - panelBio.getList(); + bio.server.res(); + bio.panel.getList(); window.NotifyOthers('bio_getLookUpList', 'bio_getLookUpList'); } } -class DldArtImages { +class BioDldArtImages { img_exp(p_dl_ar, imgFolder, ex) { const f = `${imgFolder}update.txt`; const imgExisting = []; let allFiles = []; - if (!$Bio.file(f)) return [cfg.photoNum, 0, allFiles]; + if (!$Bio.file(f)) return [bioCfg.photoNum, 0, allFiles]; const getNew = Date.now() - $Bio.lastModified(f) > ex; - if (!getNew) return [0, cfg.photoAutoAdd, allFiles]; + if (!getNew) return [0, bioCfg.photoAutoAdd, allFiles]; allFiles = utils.Glob(`${imgFolder}*`); let imNo = 0; allFiles.forEach(v => { - if (name.isLfmImg(fsoBio.GetFileName(v), p_dl_ar)) { + if (bio.name.isLfmImg(bioFSO.GetFileName(v), p_dl_ar)) { imNo++; - if (cfg.photoLimit) { + if (bioCfg.photoLimit) { imgExisting.push({ p: v, m: $Bio.lastModified(v) @@ -229,38 +229,38 @@ class DldArtImages { } }); - if (cfg.photoLimit) imgExisting.sort((a, b) => a.m - b.m); - const newImgNo = cfg.photoNum - imNo + if (bioCfg.photoLimit) imgExisting.sort((a, b) => a.m - b.m); + const newImgNo = bioCfg.photoNum - imNo if (newImgNo > 0) return [newImgNo, 0, allFiles]; - else if (!cfg.photoAutoAdd) { - if (cfg.photoLimit) { - const remove = imgExisting.length - cfg.photoLimit; + else if (!bioCfg.photoAutoAdd) { + if (bioCfg.photoLimit) { + const remove = imgExisting.length - bioCfg.photoLimit; if (remove > 0) { for (let j = 0; j < remove; j++) { - serverBio.imgToRecycle.push({ + bio.server.imgToRecycle.push({ a: p_dl_ar, p: imgExisting[j].p }); } - serverBio.setImgRecycler(true); + bio.server.setImgRecycler(true); } } - return [0, cfg.photoAutoAdd, allFiles]; - } else return [5, cfg.photoAutoAdd, allFiles, imgExisting]; + return [0, bioCfg.photoAutoAdd, allFiles]; + } else return [5, bioCfg.photoAutoAdd, allFiles, imgExisting]; } run(dl_ar, force, art, p_stndBio, p_supCache) { - if (!$Bio.file(`${cfg.storageFolder}foo_lastfm_img.vbs`)) return; - let img_folder = p_stndBio && !panelBio.isRadio(art.focus) ? panelBio.cleanPth(cfg.pth.foImgArt, art.focus, 'server') : panelBio.cleanPth(cfg.remap.foImgArt, art.focus, 'remap', dl_ar, '', 1); - if (p_supCache && !$Bio.folder(img_folder)) img_folder = panelBio.cleanPth(cfg.sup.foImgArt, art.focus, 'remap', dl_ar, '', 1); - const getNo = this.img_exp(dl_ar, img_folder, !force ? serverBio.exp : 0); + if (!$Bio.file(`${bioCfg.storageFolder}foo_lastfm_img.vbs`)) return; + let img_folder = p_stndBio && !bio.panel.isRadio(art.focus) ? bio.panel.cleanPth(bioCfg.pth.foImgArt, art.focus, 'server') : bio.panel.cleanPth(bioCfg.remap.foImgArt, art.focus, 'remap', dl_ar, '', 1); + if (p_supCache && !$Bio.folder(img_folder)) img_folder = bio.panel.cleanPth(bioCfg.sup.foImgArt, art.focus, 'remap', dl_ar, '', 1); + const getNo = this.img_exp(dl_ar, img_folder, !force ? bio.server.exp : 0); if (!getNo[0]) return; - const lfm_art = new LfmArtImg(() => lfm_art.onStateChange()); + const lfm_art = new BioLfmArtImg(() => lfm_art.onStateChange()); lfm_art.search(dl_ar, img_folder, getNo[0], getNo[1], getNo[2], getNo[3], force); } } -class LfmArtImg { +class BioLfmArtImg { constructor(state_callback) { this.allFiles; this.autoAdd; @@ -293,9 +293,9 @@ class LfmArtImg { this.imgExisting = p_imgExisting; this.func = null; this.xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); - const URL = `https://${!this.retry ? serverBio.lfm.server : 'www.last.fm'}/music/${encodeURIComponent(this.dl_ar)}/+images`; + const URL = `https://${!this.retry ? bio.server.lfm.server : 'www.last.fm'}/music/${encodeURIComponent(this.dl_ar)}/+images`; this.func = this.analyse; - if (pptBio.multiServer && !force && serverBio.urlDone(md5Bio.hashStr(this.dl_ar + this.getNo + this.autoAdd + this.img_folder + URL))) return; + if (bioSet.multiServer && !force && bio.server.urlDone(bioMD5.hashStr(this.dl_ar + this.getNo + this.autoAdd + this.img_folder + URL))) return; this.xmlhttp.open('GET', URL); this.xmlhttp.onreadystatechange = this.ready_callback; if (force) this.xmlhttp.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT'); @@ -311,31 +311,31 @@ class LfmArtImg { analyse() { const a = $Bio.clean(this.dl_ar); - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = this.xmlhttp.responseText; const list = div.getElementsByTagName('img'); let links = []; if (!list) { - if (serverBio.langFallback && !this.retry) { + if (bio.server.langFallback && !this.retry) { this.retry = true; - docBio.close(); + bioDoc.close(); return this.search(this.dl_ar, this.img_folder); } - docBio.close(); + bioDoc.close(); return $Bio.trace(`last.fm artist photos: ${this.dl_ar}: none found`, true); } $Bio.htmlParse(list, false, false, v => { const attr = v.src || ''; if (attr.includes('avatar170s/')) links.push(attr.replace('avatar170s/', '')); }); - docBio.close(); - const blacklist = imgBio.blacklist(a.toLowerCase()); + bioDoc.close(); + const blacklist = bio.img.blacklist(a.toLowerCase()); links = links.filter(v => !blacklist.includes(`${v.substring(v.lastIndexOf('/') + 1)}.jpg`)); if (links.length) { $Bio.buildPth(this.img_folder); if ($Bio.folder(this.img_folder)) { - if (this.autoAdd && cfg.photoLimit) { + if (this.autoAdd && bioCfg.photoLimit) { let k = 0; let noNewLinks = 0; for (k = 0; k < Math.min(links.length, 5); k++) { @@ -343,28 +343,28 @@ class LfmArtImg { if (this.imgExisting.every(v => v.p !== iPth)) noNewLinks++; if (noNewLinks == 5) break; } - let remove = this.imgExisting.length + noNewLinks - cfg.photoLimit; + let remove = this.imgExisting.length + noNewLinks - bioCfg.photoLimit; remove = Math.min(remove, this.imgExisting.length); if (remove > 0) { for (k = 0; k < remove; k++) { - serverBio.imgToRecycle.push({ + bio.server.imgToRecycle.push({ a, p: this.imgExisting[k].p }); } - serverBio.setImgRecycler(true); + bio.server.setImgRecycler(true); } } $Bio.save(`${this.img_folder}update.txt`, '', true); - timerBio.decelerating(); + bio.timer.decelerating(); if (this.autoAdd) { - $Bio.take(links, this.getNo).forEach(v => $Bio.run(`cscript //nologo "${cfg.storageFolder}foo_lastfm_img.vbs" "${v}" "${this.img_folder + a}_${v.substring(v.lastIndexOf('/') + 1)}.jpg"`, 0)); + $Bio.take(links, this.getNo).forEach(v => $Bio.run(`cscript //nologo "${bioCfg.storageFolder}foo_lastfm_img.vbs" "${v}" "${this.img_folder + a}_${v.substring(v.lastIndexOf('/') + 1)}.jpg"`, 0)); } else { let c = 0; - $Bio.take(links, cfg.photoNum).some(v => { + $Bio.take(links, bioCfg.photoNum).some(v => { const imPth = `${this.img_folder + a}_${v.substring(v.lastIndexOf('/') + 1)}.jpg`; if (!this.allFiles.includes(imPth)) { - $Bio.run(`cscript //nologo "${cfg.storageFolder}foo_lastfm_img.vbs" "${v}" "${imPth}"`, 0); + $Bio.run(`cscript //nologo "${bioCfg.storageFolder}foo_lastfm_img.vbs" "${v}" "${imPth}"`, 0); c++; return c == this.getNo; } @@ -375,7 +375,7 @@ class LfmArtImg { } } -class LfmAlbum { +class BioLfmAlbum { constructor(state_callback) { this.albumArtist; this.albm; @@ -419,14 +419,14 @@ class LfmAlbum { this.albm = p_albm; this.rev_img = p_rev_img; if (!this.getStats && this.rev || !this.rev) { - URL = serverBio.url.lfm; - if (this.rev && !serverBio.lfm.def_EN && !this.retry) URL += `&lang=${cfg.language.toLowerCase()}`; - URL += `&method=album.getInfo&artist=${encodeURIComponent(this.albumArtist)}&album=${encodeURIComponent(this.rev || this.retry ? this.album : this.albm)}&autocorrect=${serverBio.auto_corr}`; - } else URL = `https://${serverBio.lfm.server}/music/${encodeURIComponent(this.albumArtist)}/${encodeURIComponent(this.album.replace(/\+/g, '%2B'))}`; + URL = bio.server.url.lfm; + if (this.rev && !bio.server.lfm.def_EN && !this.retry) URL += `&lang=${bioCfg.language.toLowerCase()}`; + URL += `&method=album.getInfo&artist=${encodeURIComponent(this.albumArtist)}&album=${encodeURIComponent(this.rev || this.retry ? this.album : this.albm)}&autocorrect=${bio.server.auto_corr}`; + } else URL = `https://${bio.server.lfm.server}/music/${encodeURIComponent(this.albumArtist)}/${encodeURIComponent(this.album.replace(/\+/g, '%2B'))}`; this.func = null; this.xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); this.func = this.analyse; - if (pptBio.multiServer && !force && serverBio.urlDone(md5Bio.hashStr(this.albumArtist + this.album + this.albm + this.rev + this.rev_img + (cfg.imgRevHQ || !this.rev_img) + this.pth + URL))) return; + if (bioSet.multiServer && !force && bio.server.urlDone(bioMD5.hashStr(this.albumArtist + this.album + this.albm + this.rev + this.rev_img + (bioCfg.imgRevHQ || !this.rev_img) + this.pth + URL))) return; this.xmlhttp.open('GET', URL); this.xmlhttp.onreadystatechange = this.ready_callback; if (!this.getStats && this.rev || !this.rev) this.xmlhttp.setRequestHeader('User-Agent', 'foobar2000_script'); @@ -445,7 +445,7 @@ class LfmAlbum { if (!this.getStats && this.rev) { let wiki = $Bio.jsonParse(this.xmlhttp.responseText, '', 'get', 'album.wiki.content'); if (!wiki) { - if (serverBio.langFallback && !this.retry) { + if (bio.server.langFallback && !this.retry) { this.retry = true; return this.search(this.albumArtist, this.album, this.rev, this.fo, this.pth); } @@ -461,12 +461,12 @@ class LfmAlbum { if (this.fo) { $Bio.buildPth(this.fo); $Bio.save(this.pth, wiki, true); - serverBio.res(); + bio.server.res(); } } else if (this.rev) { - docBio.open(); + bioDoc.open(); const counts = ['', '', '']; - const div = docBio.createElement('div'); + const div = bioDoc.createElement('div'); const scrobbles = ['', '', '']; div.innerHTML = this.xmlhttp.responseText; let j = 0; @@ -505,7 +505,7 @@ class LfmAlbum { counts[j] = j != 2 ? `${$Bio.titlecase(v.innerText.trim())} \u200b| ${v.title.trim()}` : $Bio.titlecase(v.innerText.trim()); j++ }); - docBio.close(); + bioDoc.close(); if (this.tags.length) { this.tags = [...new Set(this.tags)]; this.tags.length = Math.min(5, this.tags.length); @@ -520,38 +520,38 @@ class LfmAlbum { this.getStats = false; return this.search(this.albumArtist, this.album, this.rev, this.fo, this.pth); } else { - if (!$Bio.file(`${cfg.storageFolder}foo_lastfm_img.vbs`)) return; + if (!$Bio.file(`${bioCfg.storageFolder}foo_lastfm_img.vbs`)) return; const data = $Bio.jsonParse(this.xmlhttp.responseText, [], 'get', 'album.image'); if (data.length < 5) { - serverBio.updateNotFound(`${this.albumArtist} - ${this.retry ? this.album : this.albm} ${serverBio.auto_corr} ${this.pth}`); - if (!this.retry && cfg.albStrip && this.album != this.albm) { + bio.server.updateNotFound(`${this.albumArtist} - ${this.retry ? this.album : this.albm} ${bio.server.auto_corr} ${this.pth}`); + if (!this.retry && bioCfg.albStrip && this.album != this.albm) { this.retry = true; return this.search(this.albumArtist, this.album, this.rev, this.fo, this.pth, this.albm); } return $Bio.trace(`last.fm album cover: ${this.album} / ${this.albumArtist}: not found`, true); } - let link = data[cfg.imgRevHQ || !this.rev_img ? 4 : 3]['#text']; - if (link && (cfg.imgRevHQ || !this.rev_img)) { + let link = data[bioCfg.imgRevHQ || !this.rev_img ? 4 : 3]['#text']; + if (link && (bioCfg.imgRevHQ || !this.rev_img)) { const linkSplit = link.split('/'); linkSplit.splice(linkSplit.length - 2, 1); link = linkSplit.join('/'); } if (!link) { - serverBio.updateNotFound(`${this.albumArtist} - ${this.retry ? this.album : this.albm} ${serverBio.auto_corr} ${this.pth}`); - if (!this.retry && cfg.albStrip && this.album != this.albm) { + bio.server.updateNotFound(`${this.albumArtist} - ${this.retry ? this.album : this.albm} ${bio.server.auto_corr} ${this.pth}`); + if (!this.retry && bioCfg.albStrip && this.album != this.albm) { this.retry = true; return this.search(this.albumArtist, this.album, this.rev, this.fo, this.pth, this.albm); } return $Bio.trace(`last.fm album cover: ${this.album} / ${this.albumArtist}: not found`, true); } - timerBio.decelerating(true); + bio.timer.decelerating(true); $Bio.buildPth(this.fo); - $Bio.run(`cscript //nologo "${cfg.storageFolder}foo_lastfm_img.vbs" "${link}" "${this.pth + link.slice(-4)}"`, 0); + $Bio.run(`cscript //nologo "${bioCfg.storageFolder}foo_lastfm_img.vbs" "${link}" "${this.pth + link.slice(-4)}"`, 0); } } } -class LfmTrack { +class BioLfmTrack { constructor(state_callback) { this.album = []; this.artist; @@ -602,9 +602,9 @@ class LfmTrack { this.force = p_force; if (!this.lfm_done) { if (!this.getStats) { - URL = serverBio.url.lfm; - if (!serverBio.lfm.def_EN && !this.retry) URL += `&lang=${cfg.language.toLowerCase()}`; - URL += `&method=track.getInfo&artist=${encodeURIComponent(this.artist)}&track=${encodeURIComponent(this.track)}&autocorrect=${serverBio.auto_corr}`; + URL = bio.server.url.lfm; + if (!bio.server.lfm.def_EN && !this.retry) URL += `&lang=${bioCfg.language.toLowerCase()}`; + URL += `&method=track.getInfo&artist=${encodeURIComponent(this.artist)}&track=${encodeURIComponent(this.track)}&autocorrect=${bio.server.auto_corr}`; } else { this.text = $Bio.jsonParse(this.pth, false, 'file'); if (!this.text) { @@ -612,7 +612,7 @@ class LfmTrack { ids: {} } } - URL = `https://${serverBio.lfm.server}/music/${encodeURIComponent(this.artist)}/_/${encodeURIComponent(this.track)}`; + URL = `https://${bio.server.lfm.server}/music/${encodeURIComponent(this.artist)}/_/${encodeURIComponent(this.track)}`; } } else { if (this.text[this.track] && this.text[this.track].wiki && !this.force) { @@ -621,16 +621,16 @@ class LfmTrack { return this.revSave(); } const formatName = n => n.replace(/[\s/]/g, '-').replace(/[.,!?:;'\u2019"_\u2010+()[\]&]/g, '').replace(/\$/g, 's').replace(/-+/g, '-').toLowerCase(); - if (this.getIDs && (!this.text.ids.ids_update || this.text.ids.ids_update < Date.now() - serverBio.exp * 3 || this.force)) URL = `${serverBio.url.lfm_sf}songs/${formatName(this.artist)}`; - else if (this.text.ids[serverBio.tidy(this.track)]) { + if (this.getIDs && (!this.text.ids.ids_update || this.text.ids.ids_update < Date.now() - bio.server.exp * 3 || this.force)) URL = `${bio.server.url.lfm_sf}songs/${formatName(this.artist)}`; + else if (this.text.ids[bio.server.tidy(this.track)]) { this.getIDs = false; - URL = serverBio.url.lfm_sf + this.text.ids[serverBio.tidy(this.track)]; + URL = bio.server.url.lfm_sf + this.text.ids[bio.server.tidy(this.track)]; } else return this.revSave(); } this.func = null; this.xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); this.func = this.analyse; - if (pptBio.multiServer && !this.force && serverBio.urlDone(md5Bio.hashStr(this.artist + this.track + this.pth + URL))) return; + if (bioSet.multiServer && !this.force && bio.server.urlDone(bioMD5.hashStr(this.artist + this.track + this.pth + URL))) return; this.xmlhttp.open('GET', URL); this.xmlhttp.onreadystatechange = this.ready_callback; if (!this.getStats && !this.lfm_done) this.xmlhttp.setRequestHeader('User-Agent', 'foobar2000_script'); @@ -664,11 +664,11 @@ class LfmTrack { this.tags.length = Math.min(5, this.tags.length); this.length = this.convertDuration($Bio.jsonParse(this.xmlhttp.responseText, '', 'get', 'track.duration')); if (!this.wiki) { - if (serverBio.langFallback && !this.retry) { + if (bio.server.langFallback && !this.retry) { this.retry = true; return this.search(this.artist, this.track, this.fo, this.pth, this.force); } - if (!this.lfm_done && (cfg.language == 'EN' || serverBio.langFallback)) { + if (!this.lfm_done && (bioCfg.language == 'EN' || bio.server.langFallback)) { this.lfm_done = true; return this.search(this.artist, this.track, this.fo, this.pth, this.force); } @@ -676,9 +676,9 @@ class LfmTrack { } else this.src = 1; this.revSave(); } else { - docBio.open(); + bioDoc.open(); const counts = ['', '']; - const div = docBio.createElement('div'); + const div = bioDoc.createElement('div'); const scrobbles = ['', '']; div.innerHTML = this.xmlhttp.responseText; let from = ''; @@ -695,7 +695,7 @@ class LfmTrack { if (j == 1) return true; j++; }); - if (!cfg.lang.ix && !feat) { + if (!bioCfg.lang.ix && !feat) { $Bio.htmlParse(div.getElementsByTagName('p'), 'className', 'more-link-fullwidth-right', v => { feat = v.innerText.trim(); feat = /^\d+/.test(feat) ? feat.replace(/\D/g, '') : ''; @@ -712,7 +712,7 @@ class LfmTrack { counts[j] = $Bio.titlecase(v.innerText.trim()); j++ }); - docBio.close(); + bioDoc.close(); if (from && this.album.length) { this.album = [...new Set(this.album)].join('\u200b, '); this.releases += `${from}\u200b: ${this.album}`; @@ -728,8 +728,8 @@ class LfmTrack { return this.search(this.artist, this.track, this.fo, this.pth, this.force); } } else { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = this.xmlhttp.responseText; if (!this.getIDs) { let j = 0; @@ -744,7 +744,7 @@ class LfmTrack { } }); this.wiki = this.wiki.trim(); - docBio.close(); + bioDoc.close(); if (!this.wiki) { if (!this.releases && !this.stats.length) return this.revSave(true); } else this.src = 2; @@ -753,10 +753,10 @@ class LfmTrack { this.text.ids = {} $Bio.htmlParse(div.getElementsByTagName('li'), false, false, v => { const a = v.getElementsByTagName('a'); - if (a.length && a[0].href && a[0].href.includes('/facts/')) this.text.ids[serverBio.tidy(a[0].innerText)] = a[0].href.replace('about:/', ''); + if (a.length && a[0].href && a[0].href.includes('/facts/')) this.text.ids[bio.server.tidy(a[0].innerText)] = a[0].href.replace('about:/', ''); }); this.text.ids.ids_update = Date.now(); - docBio.close(); + bioDoc.close(); this.getIDs = false; this.search(this.artist, this.track, this.fo, this.pth, this.force); } @@ -773,7 +773,7 @@ class LfmTrack { } revSave(ret) { - if (this.text[this.track] && this.text[this.track].lang == cfg.language) { + if (this.text[this.track] && this.text[this.track].lang == bioCfg.language) { if (!this.releases) this.releases = this.text[this.track].releases; if (!this.wiki && !this.force) { this.wiki = this.text[this.track].wiki; @@ -788,7 +788,7 @@ class LfmTrack { tags: this.tags, wiki: this.wiki || '', s: this.src, - lang: this.retry ? 'EN' : cfg.language, + lang: this.retry ? 'EN' : bioCfg.language, update: Date.now() }; if (this.fo) { @@ -796,11 +796,11 @@ class LfmTrack { $Bio.save(this.pth, JSON.stringify($Bio.sortKeys(this.text), null, 3), true); } if (ret) return $Bio.trace(`last.fm track review: ${$Bio.titlecase(this.track)} / ${this.artist}: not found`, true); - serverBio.res(); + bio.server.res(); } } -class LfmSimilarArtists { +class BioLfmSimilarArtists { constructor(state_callback, on_search_done_callback) { this.artist; this.done; @@ -832,7 +832,7 @@ class LfmSimilarArtists { if (this.retry) this.lmt = this.lmt == 249 ? 235 + Math.floor(Math.random() * 14) : this.lmt + 10; this.func = null; this.xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); - const URL = 'http://ws.audioscrobbler.com/2.0/?format=json' + panelBio.lfm + '&method=artist.getSimilar&artist=' + encodeURIComponent(this.artist) + '&limit=' + this.lmt + '&autocorrect=1'; + const URL = 'http://ws.audioscrobbler.com/2.0/?format=json' + bio.panel.lfm + '&method=artist.getSimilar&artist=' + encodeURIComponent(this.artist) + '&limit=' + this.lmt + '&autocorrect=1'; this.func = this.analyse; this.xmlhttp.open('GET', URL); this.xmlhttp.onreadystatechange = this.ready_callback; @@ -866,8 +866,8 @@ class LfmSimilarArtists { }); $Bio.buildPth(this.pth_sim); $Bio.save(this.fn_sim, JSON.stringify(list), true); - if (cfg.lfmSim) { - panelBio.getList(); + if (bioCfg.lfmSim) { + bio.panel.getList(); window.NotifyOthers('bio_getLookUpList', 'bio_getLookUpList'); } } @@ -876,7 +876,7 @@ class LfmSimilarArtists { } } -class LfmTopAlbums { +class BioLfmTopAlbums { constructor(state_callback, on_search_done_callback) { this.artist; this.func = null; @@ -904,8 +904,8 @@ class LfmTopAlbums { } analyse() { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); const popAlbums = []; let i = 0; let topAlbums = []; @@ -915,7 +915,7 @@ class LfmTopAlbums { i++; if (i == 10) return true; }); - docBio.close(); + bioDoc.close(); if (popAlbums.length) { const mapAlbums = topAlbums.map(v => $Bio.cut(v)); const match = mapAlbums.includes($Bio.cut(popAlbums[0])); @@ -928,7 +928,7 @@ class LfmTopAlbums { } } -class DldLastfmGenresWhitelist { +class BioDldLastfmGenresWhitelist { constructor(state_callback) { this.func = null; this.ready_callback = state_callback; @@ -963,8 +963,8 @@ class DldLastfmGenresWhitelist { } analyse() { - docBio.open(); - const div = docBio.createElement('div'); + bioDoc.open(); + const div = bioDoc.createElement('div'); div.innerHTML = this.xmlhttp.responseText; const a = div.getElementsByTagName('a'); const genres = []; @@ -975,7 +975,7 @@ class DldLastfmGenresWhitelist { } if (genres.length > 860) { - const pth = `${cfg.storageFolder}lastfm_genre_whitelist.json`; + const pth = `${bioCfg.storageFolder}lastfm_genre_whitelist.json`; const existingGenres = $Bio.jsonParse(pth, [], 'file'); if (genres.length > existingGenres.length) { $Bio.buildPth(pth); diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-library.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-library.js index 35ecc0b2..b6f3a549 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-library.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-library.js @@ -1,6 +1,6 @@ 'use strict'; -class LibraryBio { +class BioLibrary { constructor() { this.db_lib; this.update = true; @@ -22,21 +22,21 @@ class LibraryBio { case 1: { let q = ''; let ql = ''; - cfg.albartFields.forEach((v, i) => q += `${(i ? ' OR ' : '') + v} IS ${a}`); - cfg.albFields.forEach((v, i) => ql += `${(i ? ' OR ' : '') + v} IS ${l}`); + bioCfg.albartFields.forEach((v, i) => q += `${(i ? ' OR ' : '') + v} IS ${a}`); + bioCfg.albFields.forEach((v, i) => ql += `${(i ? ' OR ' : '') + v} IS ${l}`); const items = $Bio.query(this.getLibItems(), `(${q}) AND (${ql})`); if (!items.Count) return false; return items.Count; } case 2: { - const q = `("${cfg.tf.albumArtist}" IS ${a}) AND (("${cfg.tf.album}" IS ${l}) OR ("$trim($replace($replace(${cfg.tf.album},CD1,,CD2,,CD3,,CD 1,,CD 2,,CD 3,,CD.01,,CD.02,,CD.03,,CD One,,CD Two,,CD Three,,Disc1,,Disc2,,Disc3,,Disc 1,,Disc 2,,Disc 3,,Disc One,,Disc Two,,Disc Three,,Disc I,,Disc II,,Disc III,,'()',,'[]',), , ,'()',,'[]',))" IS ${l}))`; + const q = `("${bioCfg.tf.albumArtist}" IS ${a}) AND (("${bioCfg.tf.album}" IS ${l}) OR ("$trim($replace($replace(${bioCfg.tf.album},CD1,,CD2,,CD3,,CD 1,,CD 2,,CD 3,,CD.01,,CD.02,,CD.03,,CD One,,CD Two,,CD Three,,Disc1,,Disc2,,Disc3,,Disc 1,,Disc 2,,Disc 3,,Disc One,,Disc Two,,Disc Three,,Disc I,,Disc II,,Disc III,,'()',,'[]',), , ,'()',,'[]',))" IS ${l}))`; const items = $Bio.query(this.getLibItems(), q); if (!items.Count) return false; return items[0]; } default: { let q = ''; - cfg.artFields.forEach((v, i) => q += `${(i ? ' OR ' : '') + v} IS ${a}`); + bioCfg.artFields.forEach((v, i) => q += `${(i ? ' OR ' : '') + v} IS ${a}`); const items = $Bio.query(this.getLibItems(), q); if (!items.Count) return false; return !type ? true : items[0]; diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-lyrics.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-lyrics.js index a5670de7..fea0be3e 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-lyrics.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-lyrics.js @@ -1,6 +1,6 @@ 'use strict'; -class LyricsBio { +class BioLyrics { constructor() { this.noLyrics = ['No lyrics found']; this.enhancedTimestamps = /(\s*)<(\d{1,2}:|)\d{1,2}:\d{2}(>|\.\d{1,3}>)(\s*)/g; @@ -45,7 +45,7 @@ class LyricsBio { } display() { - return this.lyrics.length && this.locus >= 0 && txt.lyricsDisplayed(); + return this.lyrics.length && this.locus >= 0 && bio.txt.lyricsDisplayed(); } draw(gr) { @@ -55,11 +55,11 @@ class LyricsBio { const transition_factor_in = !this.lyrics[this.locus].multiLine ? transition_factor : 1; const transition_factor_out = $Bio.clamp(transition_factor_in * 3, 0, 1); const alpha = Math.min(255 * transition_factor * 4 / 3, 255); - const blendIn = this.type.synced ? uiBio.getBlend(uiBio.col.lyricsHighlight, uiBio.col.lyricsNormal, transition_factor_in) : uiBio.col.lyricsNormal; - const blendOut = this.type.synced ? uiBio.getBlend(uiBio.col.lyricsNormal, uiBio.col.lyricsHighlight, transition_factor_out) : uiBio.col.lyricsNormal; + const blendIn = this.type.synced ? bio.ui.getBlend(bio.ui.col.lyricsHighlight, bio.ui.col.lyricsNormal, transition_factor_in) : bio.ui.col.lyricsNormal; + const blendOut = this.type.synced ? bio.ui.getBlend(bio.ui.col.lyricsNormal, bio.ui.col.lyricsHighlight, transition_factor_out) : bio.ui.col.lyricsNormal; const y = this.y + this.scroll; - let col = uiBio.col.lyricsNormal; + let col = bio.ui.col.lyricsNormal; let fadeBot = this.transBot[transition_factor]; if (!fadeBot) { @@ -79,24 +79,24 @@ class LyricsBio { const line_y = Math.round(y - top + lyric_y); const bottomLine = line_y > this.bot; if (this.showlyric(lyric_y, top)) { - const font = !lyric.highlight ? uiBio.font.lyrics : this.font.lyrics; + const font = !lyric.highlight ? bio.ui.font.lyrics : this.font.lyrics; if (this.shadowEffect && line_y >= this.top && !bottomLine) { if (this.dropNegativeShadowLevel) { - gr.DrawString(lyric.content, font, uiBio.col.dropShadow, this.x - this.dropNegativeShadowLevel, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); - gr.DrawString(lyric.content, font, uiBio.col.dropShadow, this.x, line_y - this.dropNegativeShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, bio.ui.col.dropShadow, this.x - this.dropNegativeShadowLevel, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, bio.ui.col.dropShadow, this.x, line_y - this.dropNegativeShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); } - gr.DrawString(lyric.content, font, uiBio.col.dropShadow, this.x + this.dropShadowLevel, line_y + this.dropShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, bio.ui.col.dropShadow, this.x + this.dropShadowLevel, line_y + this.dropShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); } - col = line_y >= this.top ? lyric.highlight ? blendIn : i == this.locus - 1 ? blendOut : bottomLine ? fadeBot : uiBio.col.lyricsNormal : fadeTop; + col = line_y >= this.top ? lyric.highlight ? blendIn : i == this.locus - 1 ? blendOut : bottomLine ? fadeBot : bio.ui.col.lyricsNormal : fadeTop; gr.DrawString(lyric.content, font, col, this.x, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); } }); if (this.showOffset) { - this.offsetW = gr.CalcTextWidth(`Offset: ${this.userOffset / 1000}s`, uiBio.font.lyrics) + this.lineHeight; - gr.FillRoundRect(this.x + this.w - this.offsetW * 0.5 + this.arc, this.top, this.offsetW, this.lineHeight + 1, this.arc, this.arc, uiBio.col.popupBg); + this.offsetW = gr.CalcTextWidth(`Offset: ${this.userOffset / 1000}s`, bio.ui.font.lyrics) + this.lineHeight; + gr.FillRoundRect(this.x + this.w - this.offsetW * 0.5 + this.arc, this.top, this.offsetW, this.lineHeight + 1, this.arc, this.arc, bio.ui.col.popupBg); gr.DrawRoundRect(this.x + this.w - this.offsetW * 0.5 + this.arc, this.top, this.offsetW, this.lineHeight + 1, this.arc, this.arc, 1, 0x64000000); - gr.DrawString(`Offset: ${this.userOffset / 1000}s`, uiBio.font.lyrics, uiBio.col.popupText, this.x + this.offsetW * 0.5 - this.lineHeight * 0.5, this.top, this.w, this.lineHeight + 1, this.alignRight); + gr.DrawString(`Offset: ${this.userOffset / 1000}s`, bio.ui.font.lyrics, bio.ui.col.popupText, this.x + this.offsetW * 0.5 - this.lineHeight * 0.5, this.top, this.w, this.lineHeight + 1, this.alignRight); } } @@ -150,7 +150,7 @@ class LyricsBio { if (t3 && t3 - t2 > 0 && t3 - t2 < this.durationScroll) durationScroll = $Bio.clamp(t3 - t2, this.minDurationScroll, this.durationScroll); } - const variSpeed = !pptBio.lyricsScrollMaxMethod ? 10 * 500 : 0; + const variSpeed = !bioSet.lyricsScrollMaxMethod ? 10 * 500 : 0; if (variSpeed) { let diff1 = 0; let diff2 = 0; @@ -180,16 +180,16 @@ class LyricsBio { this.userOffset = 0; } this.font = { - lyrics: !pptBio.largerSyncLyricLine ? uiBio.font.lyrics : gdi.Font(uiBio.font.main.Name, Math.floor(uiBio.font.zoomSize * 1.33), uiBio.font.lyrics.Style) + lyrics: !bioSet.largerSyncLyricLine ? bio.ui.font.lyrics : gdi.Font(bio.ui.font.main.Name, Math.floor(bio.ui.font.zoomSize * 1.33), bio.ui.font.lyrics.Style) } this.alignCenter = StringFormat(1, 1); this.alignRight = StringFormat(2, 1); this.init = true; - this.lineHeight = !pptBio.largerSyncLyricLine ? uiBio.font.lyrics_h + 4 * $Bio.scale : Math.floor(uiBio.font.lyrics_h * 1.33); + this.lineHeight = !bioSet.largerSyncLyricLine ? bio.ui.font.lyrics_h + 4 * $Bio.scale : Math.floor(bio.ui.font.lyrics_h * 1.33); this.arc = SCALE(6); - pptBio.lyricsScrollTimeMax = $Bio.clamp(Math.round(pptBio.lyricsScrollTimeMax), 0, 3000); - pptBio.lyricsScrollTimeAvg = $Bio.clamp(Math.round(pptBio.lyricsScrollTimeAvg), 0, 3000); - this.durationScroll = pptBio.lyricsScrollMaxMethod ? pptBio.lyricsScrollTimeMax : Math.round(pptBio.lyricsScrollTimeAvg * 2 / 3); + bioSet.lyricsScrollTimeMax = $Bio.clamp(Math.round(bioSet.lyricsScrollTimeMax), 0, 3000); + bioSet.lyricsScrollTimeAvg = $Bio.clamp(Math.round(bioSet.lyricsScrollTimeAvg), 0, 3000); + this.durationScroll = bioSet.lyricsScrollMaxMethod ? bioSet.lyricsScrollTimeMax : Math.round(bioSet.lyricsScrollTimeAvg * 2 / 3); this.factor = this.durationScroll < 1500 ? 20 : 24; this.delta = this.lineHeight * this.factor / this.durationScroll; this.locus = -1; @@ -199,8 +199,8 @@ class LyricsBio { this.minDurationScroll = Math.min(this.durationScroll, 250); this.newHighlighted = false; this.scroll = 0; - this.shadowEffect = pptBio.dropShadowLevel > 0; - this.dropShadowLevel = pptBio.dropShadowLevel; + this.shadowEffect = bioSet.dropShadowLevel > 0; + this.dropShadowLevel = bioSet.dropShadowLevel; this.dropNegativeShadowLevel = this.dropShadowLevel > 1 ? Math.floor(this.dropShadowLevel / 2) : 0; this.showOffsetTimer = null; this.timer = null; @@ -208,12 +208,12 @@ class LyricsBio { this.transitionOffset = this.durationScroll / 2; this.transBot = {}; this.transTop = {}; - pptBio.lyricsFadeHeight = $Bio.clamp(pptBio.lyricsFadeHeight, -1, 2); - const fadeHeight = this.lineHeight * pptBio.lyricsFadeHeight; - this.x = panelBio.text.l; - this.y = panelBio.text.t - this.lineHeight + fadeHeight; - this.w = panelBio.text.w; - this.h = panelBio.lines_drawn * uiBio.font.main_h + this.lineHeight * 2 - fadeHeight * 2; + bioSet.lyricsFadeHeight = $Bio.clamp(bioSet.lyricsFadeHeight, -1, 2); + const fadeHeight = this.lineHeight * bioSet.lyricsFadeHeight; + this.x = bio.panel.text.l; + this.y = bio.panel.text.t - this.lineHeight + fadeHeight; + this.w = bio.panel.text.w; + this.h = bio.panel.lines_drawn * bio.ui.font.main_h + this.lineHeight * 2 - fadeHeight * 2; const linesDrawn = Math.floor(this.h / this.lineHeight); const oddNumLines = linesDrawn % 2; @@ -271,7 +271,7 @@ class LyricsBio { } case this.type.unsynced: { this.format(this.parseUnsyncedLyrics(lyr, this.type.none)); - const ratio = !panelBio.isRadio() ? this.trackLength / this.lyrics.length * 1000 : 2000; + const ratio = !bio.panel.isRadio() ? this.trackLength / this.lyrics.length * 1000 : 2000; this.lyrics.forEach((line, i) => { line.timestamp = ratio * i }); break; } @@ -306,12 +306,12 @@ class LyricsBio { } playbackTime() { - const time = !panelBio.isRadio() ? fb.PlaybackTime : fb.PlaybackTime - txt.reader.trackStartTime; + const time = !bio.panel.isRadio() ? fb.PlaybackTime : fb.PlaybackTime - bio.txt.reader.trackStartTime; return Math.round(time * 1000) + this.lyricsOffset + this.transitionOffset + this.userOffset; } repaintRect() { - if (!displayBiography) return; + if (!grm.ui.displayBiography) return; window.RepaintRect(this.x + (this.w - this.maxLyrWidth) / 2, this.y, this.maxLyrWidth, this.h + this.lineHeight); if (this.showOffset) window.RepaintRect(this.w - this.arc, this.top - SCALE(3), this.w * 0.5 - this.x * 2 - this.offsetW * 0.5 + this.arc, this.lineHeight + SCALE(6)); } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-menu.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-menu.js index 06d4ebbe..950031b4 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-menu.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-menu.js @@ -1,9 +1,11 @@ 'use strict'; -const MF_GRAYED_BIO = 0x00000001; -const MF_STRING_BIO = 0x00000000; +/** @global @type {number} */ +const BIO_MF_GRAYED = 0x00000001; +/** @global @type {number} */ +const BIO_MF_STRING = 0x00000000; -class MenuManagerBio { +class BioMenuManager { constructor(name, clearArr, baseMenu) { this.baseMenu = baseMenu || 'baseMenu'; this.clearArr = clearArr; @@ -79,7 +81,7 @@ class MenuManagerBio { } load(x, y) { - if (!this.menuItems.length) menBio[this.name](); + if (!this.menuItems.length) bio.men[this.name](); let i = 0; let ln = this.menuNames.length; while (i < ln) { @@ -100,9 +102,9 @@ class MenuManagerBio { this.clear(); } - newItem({ str = null, func = null, menuName = this.baseMenu, flags = MF_STRING_BIO, checkItem = false, checkRadio = false, separator = false, hide = false }) { this.menuItems.push({ str, func, menuName, flags, checkItem, checkRadio, separator, hide }); } + newItem({ str = null, func = null, menuName = this.baseMenu, flags = BIO_MF_STRING, checkItem = false, checkRadio = false, separator = false, hide = false }) { this.menuItems.push({ str, func, menuName, flags, checkItem, checkRadio, separator, hide }); } - newMenu({ menuName = this.baseMenu, str = '', appendTo = this.baseMenu, flags = MF_STRING_BIO, separator = false, hide = false }) { + newMenu({ menuName = this.baseMenu, str = '', appendTo = this.baseMenu, flags = BIO_MF_STRING, separator = false, hide = false }) { this.menuNames.push(menuName); if (menuName != this.baseMenu) this.menuItems.push({ menuName, appendMenu: true, str, appendTo, flags, separator, hide }); } @@ -113,11 +115,24 @@ class MenuManagerBio { } } -const clearArrBio = true; -const menuBio = new MenuManagerBio('mainMenu', clearArrBio); -const bMenu = new MenuManagerBio('buttonMenu', clearArrBio); +/** @global @type {boolean} */ +const bioClearArr = true; -class MenuItemsBio { +/** + * The instance of `BioMenuManager` class for biography main menu operations. + * @typedef {BioMenuManager} + * @global + */ +const bioMenu = new BioMenuManager('mainMenu', bioClearArr); + +/** + * The instance of `BioMenuManager` class for biography buttion menu operations. + * @typedef {BioMenuManager} + * @global + */ +const bioBMenu = new BioMenuManager('buttonMenu', bioClearArr); + +class BioMenuItems { constructor() { this.docTxt = ''; this.handles = new FbMetadbHandleList(); @@ -141,7 +156,7 @@ class MenuItemsBio { artistClean: '', blacklist: [], blacklistStr: [], - covType: [lg.Front, lg.Back, lg.Disc, lg.Icon, lg.Artist, lg['Cycle above'], lg['Cycle from folder']], + covType: [bioLg.Front, bioLg.Back, bioLg.Disc, bioLg.Icon, bioLg.Artist, bioLg['Cycle above'], bioLg['Cycle from folder']], isLfm: true, list: [], name: '' @@ -174,60 +189,60 @@ class MenuItemsBio { // * METHODS * // buttonMenu() { - bMenu.newMenu({}); - const artist = panelBio.art.list.length ? panelBio.art.list[0].name : name.artist(panelBio.id.focus); - switch (pptBio.artistView) { + bioBMenu.newMenu({}); + const artist = bio.panel.art.list.length ? bio.panel.art.list[0].name : bio.name.artist(bio.panel.id.focus); + switch (bioSet.artistView) { case true: - panelBio.art.list.forEach((v, i) => bMenu.newItem({ + bio.panel.art.list.forEach((v, i) => bioBMenu.newItem({ str: v.name.replace(/&/g, '&&') + v.field.replace(/&/g, '&&'), func: () => this.lookUpArtist(i), - flags: v.type != 'label' ? MF_STRING_BIO : MF_GRAYED_BIO, - checkRadio: i == panelBio.art.ix, + flags: v.type != 'label' ? BIO_MF_STRING : BIO_MF_GRAYED, + checkRadio: i == bio.panel.art.ix, separator: !i || v.type == 'similarend' || v.type == 'label' || v.type == 'tagend' || v.type == 'historyend' })); - for (let i = 0; i < 4; i++) { bMenu.newItem({ + for (let i = 0; i < 4; i++) { bioBMenu.newItem({ str: this.getlookUpStr(i, 0), - func: () => this.lookUpArtist(panelBio.art.list.length + i), - flags: !i ? MF_GRAYED_BIO : MF_STRING_BIO, - checkItem: i == 1 && pptBio.cycItem, + func: () => this.lookUpArtist(bio.panel.art.list.length + i), + flags: !i ? BIO_MF_GRAYED : BIO_MF_STRING, + checkItem: i == 1 && bioSet.cycItem, separator: true }); } - bMenu.newMenu({ - menuName: lg['More...'] + bioBMenu.newMenu({ + menuName: bioLg['More...'] }); - for (let i = 0; i < 8; i++) { bMenu.newItem({ - menuName: lg['More...'], + for (let i = 0; i < 8; i++) { bioBMenu.newItem({ + menuName: bioLg['More...'], str: this.getlookUpStr(i, 1, artist), func: () => this.lookUpArtistItems(i), - checkItem: i < 4 && [pptBio.showSimilarArtists, pptBio.showMoreTags, pptBio.showArtistHistory, pptBio.autoLock][i], + checkItem: i < 4 && [bioSet.showSimilarArtists, bioSet.showMoreTags, bioSet.showArtistHistory, bioSet.autoLock][i], separator: i == 2 || i == 3 || i == 4 || i == 5 }); } break; case false: - panelBio.alb.list.forEach((v, i) => bMenu.newItem({ + bio.panel.alb.list.forEach((v, i) => bioBMenu.newItem({ str: ((!i || v.type.includes('history') ? `${v.artist.replace(/&/g, '&&')} - ${v.album.replace(/&/g, '&&')}` : v.album.replace(/&/g, '&&')) + (!v.composition ? '' : ' [composition]')).replace(/^\s-\s/, ''), func: () => this.lookUpAlbum(i), - flags: v.type != 'label' && v.album != lg['Album History:'] ? MF_STRING_BIO : MF_GRAYED_BIO, - checkRadio: i == panelBio.alb.ix, + flags: v.type != 'label' && v.album != bioLg['Album History:'] ? BIO_MF_STRING : BIO_MF_GRAYED, + checkRadio: i == bio.panel.alb.ix, separator: !i || v.type == 'albumend' || v.type == 'label' || v.type == 'historyend' })); - for (let i = 0; i < 4; i++) { bMenu.newItem({ + for (let i = 0; i < 4; i++) { bioBMenu.newItem({ str: this.getlookUpStr(i, 0), - func: () => this.lookUpAlbum(panelBio.alb.list.length + i), - flags: !i ? MF_GRAYED_BIO : MF_STRING_BIO, - checkItem: i == 1 && pptBio.cycItem, + func: () => this.lookUpAlbum(bio.panel.alb.list.length + i), + flags: !i ? BIO_MF_GRAYED : BIO_MF_STRING, + checkItem: i == 1 && bioSet.cycItem, separator: true }); } - bMenu.newMenu({ - menuName: lg['More...'] + bioBMenu.newMenu({ + menuName: bioLg['More...'] }); - for (let i = 0; i < 8; i++) { bMenu.newItem({ - menuName: lg['More...'], + for (let i = 0; i < 8; i++) { bioBMenu.newItem({ + menuName: bioLg['More...'], str: this.getlookUpStr(i, 2, artist), func: () => this.lookUpAlbumItems(i), - checkItem: i < 3 && [pptBio.showTopAlbums, pptBio.showAlbumHistory, pptBio.autoLock][i], + checkItem: i < 3 && [bioSet.showTopAlbums, bioSet.showAlbumHistory, bioSet.autoLock][i], separator: i == 1 || i == 2 || i == 3 || i == 4 }); } break; @@ -235,168 +250,168 @@ class MenuItemsBio { } mainMenu() { - menuBio.newMenu({}); - menuBio.newItem({ - str: `${$Bio.titlecase(cfg.cfgBaseName)} server`, - flags: MF_GRAYED_BIO, + bioMenu.newMenu({}); + bioMenu.newItem({ + str: `${$Bio.titlecase(bioCfg.cfgBaseName)} server`, + flags: BIO_MF_GRAYED, separator: true, - hide: !$Bio.server || !this.shift || !vkBio.k('ctrl') + hide: !$Bio.server || !this.shift || !bio.vk.k('ctrl') }); - const b = pptBio.artistView ? 'Bio' : 'Rev'; - const loadName = lg.Load + (!pptBio.sourceAll ? '' : lg[' first']); + const b = bioSet.artistView ? 'Bio' : 'Rev'; + const loadName = bioLg.Load + (!bioSet.sourceAll ? '' : bioLg[' first']); const n = b.toLowerCase(); - const separator = !pptBio.artistView && (pptBio.showTrackRevOptions || txt.isCompositionLoaded()) || !panelBio.stndItem(); + const separator = !bioSet.artistView && (bioSet.showTrackRevOptions || bio.txt.isCompositionLoaded()) || !bio.panel.stndItem(); - if (pref.layout === 'default' && pref.theme.startsWith('custom')) { - menuBio.newItem({ + if (grSet.layout === 'default' && grSet.theme.startsWith('custom')) { + bioMenu.newItem({ str: 'Edit custom theme', func: () => { - displayCustomThemeMenu = true; - initCustomThemeMenu(false, false, false, 'bio_bg'); + grm.ui.displayCustomThemeMenu = true; + grm.cthMenu.initCustomThemeMenu(false, false, false, 'bio_bg'); window.Repaint(); }, separator: true }); } - menuBio.newItem({ // * Biography layout switcher - str: pref.biographyLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', + bioMenu.newItem({ // * Biography layout switcher + str: grSet.biographyLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', func: () => { - if (pref.biographyLayout === 'normal') { - pref.biographyLayout = 'full'; - displayPlaylist = false; + if (grSet.biographyLayout === 'normal') { + grSet.biographyLayout = 'full'; + grm.ui.displayPlaylist = false; } else { - pref.biographyLayout = 'normal'; - displayPlaylist = true; + grSet.biographyLayout = 'normal'; + grm.ui.displayPlaylist = true; } - if (pref.panelWidthAuto) { - initPanelWidthAuto(); + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); } - initBiographyLayout(); + grm.ui.initBiographyLayout(); }, separator: true, - hide: pref.layout !== 'default' + hide: grSet.layout !== 'default' }); - menuBio.newMenu({ + bioMenu.newMenu({ menuName: loadName, - hide: pptBio.img_only + hide: bioSet.img_only }); - this.sources.forEach((v, i) => menuBio.newItem({ + this.sources.forEach((v, i) => bioMenu.newItem({ menuName: loadName, str: v, func: () => this.toggle(i, b, true), - flags: txt[n][this.types[i]] ? MF_STRING_BIO : MF_GRAYED_BIO, - checkRadio: i == txt[n].loaded.ix, - separator: txt[n].reader ? i == 3 && separator : i == 2 && separator + flags: bio.txt[n][this.types[i]] ? BIO_MF_STRING : BIO_MF_GRAYED, + checkRadio: i == bio.txt[n].loaded.ix, + separator: bio.txt[n].reader ? i == 3 && separator : i == 2 && separator })); - if (pptBio.showTrackRevOptions && !pptBio.artistView && panelBio.stndItem() && !txt.isCompositionLoaded()) { - menuBio.newItem({ + if (bioSet.showTrackRevOptions && !bioSet.artistView && bio.panel.stndItem() && !bio.txt.isCompositionLoaded()) { + bioMenu.newItem({ menuName: loadName, - str: lg['Type:'], - flags: MF_GRAYED_BIO, + str: bioLg['Type:'], + flags: BIO_MF_GRAYED, separator: true }); - [lg.Album, lg.Track, lg['Prefer both']].forEach((v, i) => menuBio.newItem({ + [bioLg.Album, bioLg.Track, bioLg['Prefer both']].forEach((v, i) => bioMenu.newItem({ menuName: loadName, str: v, func: () => this.setReviewType(i), - flags: !txt[n][this.types[0]] && !txt[n][this.types[1]] && !txt[n][this.types[2]] ? MF_STRING_BIO : !txt[n].loaded.txt && [this.albAvail, this.trkAvail, this.albAvail || this.trkAvail][i] ? MF_STRING_BIO : MF_GRAYED_BIO, - checkRadio: !i && !pptBio.inclTrackRev || i == 1 && pptBio.inclTrackRev == 2 || i == 2 && pptBio.inclTrackRev == 1 + flags: !bio.txt[n][this.types[0]] && !bio.txt[n][this.types[1]] && !bio.txt[n][this.types[2]] ? BIO_MF_STRING : !bio.txt[n].loaded.txt && [this.albAvail, this.trkAvail, this.albAvail || this.trkAvail][i] ? BIO_MF_STRING : BIO_MF_GRAYED, + checkRadio: !i && !bioSet.inclTrackRev || i == 1 && bioSet.inclTrackRev == 2 || i == 2 && bioSet.inclTrackRev == 1 })); } - if (!panelBio.stndItem() || txt.isCompositionLoaded()) { - menuBio.newItem({ + if (!bio.panel.stndItem() || bio.txt.isCompositionLoaded()) { + bioMenu.newItem({ menuName: loadName, - str: lg['Mode: '] + (pptBio.artistView ? lg['artist look-up'] : (txt.isCompositionLoaded() ? lg['composition loaded'] : lg['album look-up'])), - flags: MF_GRAYED_BIO + str: bioLg['Mode: '] + (bioSet.artistView ? bioLg['artist look-up'] : (bio.txt.isCompositionLoaded() ? bioLg['composition loaded'] : bioLg['album look-up'])), + flags: BIO_MF_GRAYED }); } - menuBio.addSeparator({ separator: !pptBio.img_only }); + bioMenu.addSeparator({ separator: !bioSet.img_only }); - menuBio.newMenu({ - menuName: lg.Display + bioMenu.newMenu({ + menuName: bioLg.Display }); - for (let i = 0; i < 11; i++) { menuBio.newItem({ - menuName: lg.Display, + for (let i = 0; i < 11; i++) { bioMenu.newItem({ + menuName: bioLg.Display, str: this.display.str[i], func: () => this.setDisplay(i), - flags: i == 1 && pptBio.autoEnlarge || i == 6 && !pptBio.summaryShow || i == 10 && (panelBio.id.lyricsSource || panelBio.id.nowplayingSource) ? MF_GRAYED_BIO : MF_STRING_BIO, + flags: i == 1 && bioSet.autoEnlarge || i == 6 && !bioSet.summaryShow || i == 10 && (bio.panel.id.lyricsSource || bio.panel.id.nowplayingSource) ? BIO_MF_GRAYED : BIO_MF_STRING, checkItem: (i > 2 && i < 6) && this.display.check[i], checkRadio: (i < 3 || i > 6 && i < 9 || i > 8) && this.display.check[i], separator: i == 2 || i == 5 || i == 6 || i == 8 }); } - menuBio.addSeparator({}); + bioMenu.addSeparator({}); - menuBio.newMenu({ - menuName: lg.Sources + bioMenu.newMenu({ + menuName: bioLg.Sources }); - menuBio.newMenu({ - menuName: lg.Text, - appendTo: lg.Sources + bioMenu.newMenu({ + menuName: bioLg.Text, + appendTo: bioLg.Sources }); - for (let i = 0; i < 5; i++) { menuBio.newItem({ - menuName: lg.Text, - str: [lg['Auto-fallback'], lg.Static, lg.Amalgamate, lg['Show track review options on load menu'], lg['Prefer composition reviews (allmusic && wikipedia)']][i], + for (let i = 0; i < 5; i++) { bioMenu.newItem({ + menuName: bioLg.Text, + str: [bioLg['Auto-fallback'], bioLg.Static, bioLg.Amalgamate, bioLg['Show track review options on load menu'], bioLg['Prefer composition reviews (allmusic && wikipedia)']][i], func: () => this.setTextType(i, b), - flags: !i && pptBio.sourceAll || i == 1 && pptBio.sourceAll ? MF_GRAYED_BIO : MF_STRING_BIO, - checkItem: i == 2 && pptBio.sourceAll || i == 3 && pptBio.showTrackRevOptions || i == 4 && pptBio.classicalMusicMode, - checkRadio: !i && (!pptBio.lockBio || pptBio.sourceAll) || i == 1 && pptBio.lockBio && !pptBio.sourceAll, - separator: i == 1 || i == 2 || i == 3 && cfg.classicalModeEnable, - hide: i == 4 && !cfg.classicalModeEnable + flags: !i && bioSet.sourceAll || i == 1 && bioSet.sourceAll ? BIO_MF_GRAYED : BIO_MF_STRING, + checkItem: i == 2 && bioSet.sourceAll || i == 3 && bioSet.showTrackRevOptions || i == 4 && bioSet.classicalMusicMode, + checkRadio: !i && (!bioSet.lockBio || bioSet.sourceAll) || i == 1 && bioSet.lockBio && !bioSet.sourceAll, + separator: i == 1 || i == 2 || i == 3 && bioCfg.classicalModeEnable, + hide: i == 4 && !bioCfg.classicalModeEnable }); } - menuBio.addSeparator({ menuName: lg.Sources }); + bioMenu.addSeparator({ menuName: bioLg.Sources }); - menuBio.newMenu({ - menuName: lg.Photo, - appendTo: lg.Sources + bioMenu.newMenu({ + menuName: bioLg.Photo, + appendTo: bioLg.Sources }); - [lg['Cycle from download folder'], lg['Cycle from custom folder [fallback to above]'], lg['Artist (single image [fb2k: display])']].forEach((v, i) => menuBio.newItem({ - menuName: lg.Photo, + [bioLg['Cycle from download folder'], bioLg['Cycle from custom folder [fallback to above]'], bioLg['Artist (single image [fb2k: display])']].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg.Photo, str: v, func: () => this.setPhotoType(i), - checkRadio: pptBio.cycPhotoLocation == i, + checkRadio: bioSet.cycPhotoLocation == i, separator: i == 1 })); - menuBio.newMenu({ - menuName: lg.Cover, - str: lg.Cover, - appendTo: lg.Sources, - flags: !panelBio.alb.ix || pptBio.artistView ? MF_STRING_BIO : MF_GRAYED_BIO + bioMenu.newMenu({ + menuName: bioLg.Cover, + str: bioLg.Cover, + appendTo: bioLg.Sources, + flags: !bio.panel.alb.ix || bioSet.artistView ? BIO_MF_STRING : BIO_MF_GRAYED }); - this.img.covType.forEach((v, i) => menuBio.newItem({ - menuName: lg.Cover, + this.img.covType.forEach((v, i) => bioMenu.newItem({ + menuName: bioLg.Cover, str: v, func: () => this.setCover(i), - flags: pptBio.loadCovFolder && !pptBio.loadCovAllFb && i < 5 ? MF_GRAYED_BIO : MF_STRING_BIO, - checkItem: (pptBio.loadCovAllFb || i > 4) && [imgBio.cov.selection[0] != -1, imgBio.cov.selection[1] != -1, imgBio.cov.selection[2] != -1, imgBio.cov.selection[3] != -1, imgBio.cov.selection[4] != -1, pptBio.loadCovAllFb, pptBio.loadCovFolder][i], - checkRadio: !pptBio.loadCovAllFb && i == pptBio.covType, + flags: bioSet.loadCovFolder && !bioSet.loadCovAllFb && i < 5 ? BIO_MF_GRAYED : BIO_MF_STRING, + checkItem: (bioSet.loadCovAllFb || i > 4) && [bio.img.cov.selection[0] != -1, bio.img.cov.selection[1] != -1, bio.img.cov.selection[2] != -1, bio.img.cov.selection[3] != -1, bio.img.cov.selection[4] != -1, bioSet.loadCovAllFb, bioSet.loadCovFolder][i], + checkRadio: !bioSet.loadCovAllFb && i == bioSet.covType, separator: i == 4 })); - menuBio.addSeparator({ menuName: lg.Sources }); + bioMenu.addSeparator({ menuName: bioLg.Sources }); - menuBio.newMenu({ - menuName: lg['Open file location'], - appendTo: lg.Sources, + bioMenu.newMenu({ + menuName: bioLg['Open file location'], + appendTo: bioLg.Sources, flags: this.getOpenFlag() }); - for (let i = 0; i < 8; i++) { menuBio.newItem({ - menuName: lg['Open file location'], + for (let i = 0; i < 8; i++) { bioMenu.newItem({ + menuName: bioLg['Open file location'], str: this.openName[i], func: () => $Bio.browser(`explorer /select,"${this.path.open[i]}"`, false), flags: this.getOpenFlag(), @@ -404,172 +419,172 @@ class MenuItemsBio { hide: !this.openName[i] }); } - menuBio.addSeparator({ menuName: lg.Sources }); + bioMenu.addSeparator({ menuName: bioLg.Sources }); - if (pptBio.menuShowPaste == 2 || pptBio.menuShowPaste && this.shift) { - menuBio.newMenu({ - menuName: lg['Paste text from clipboard'], - appendTo: lg.Sources, - separator: pptBio.menuShowPaste == 2 || pptBio.menuShowPaste && this.shift + if (bioSet.menuShowPaste == 2 || bioSet.menuShowPaste && this.shift) { + bioMenu.newMenu({ + menuName: bioLg['Paste text from clipboard'], + appendTo: bioLg.Sources, + separator: bioSet.menuShowPaste == 2 || bioSet.menuShowPaste && this.shift }); - for (let i = 0; i < 5; i++) { menuBio.newItem({ - menuName: lg['Paste text from clipboard'], - str: [pptBio.artistView ? lg['Biography [allmusic location]'] : lg['Review [allmusic location]'], pptBio.artistView ? lg['Biography [last.fm location]'] : lg['Review [last.fm location]'], pptBio.artistView ? lg['Biography [wikipedia location]'] : lg['Review [wikipedia location]'], lg['Open last edited'], lg.Undo][i], + for (let i = 0; i < 5; i++) { bioMenu.newItem({ + menuName: bioLg['Paste text from clipboard'], + str: [bioSet.artistView ? bioLg['Biography [allmusic location]'] : bioLg['Review [allmusic location]'], bioSet.artistView ? bioLg['Biography [last.fm location]'] : bioLg['Review [last.fm location]'], bioSet.artistView ? bioLg['Biography [wikipedia location]'] : bioLg['Review [wikipedia location]'], bioLg['Open last edited'], bioLg.Undo][i], func: () => this.setPaste(i), - flags: !i && !this.path.am[2] || i == 1 && !this.path.lfm[2] || i == 2 && !this.path.wiki[2] || i == 3 && !this.undo.path || i == 4 && this.undo.text == '#!#' ? MF_GRAYED_BIO : MF_STRING_BIO, + flags: !i && !this.path.am[2] || i == 1 && !this.path.lfm[2] || i == 2 && !this.path.wiki[2] || i == 3 && !this.undo.path || i == 4 && this.undo.text == '#!#' ? BIO_MF_GRAYED : BIO_MF_STRING, separator: i == 2 || i == 3 }); } } - menuBio.newItem({ - menuName: lg.Sources, - str: lg['Force update'], - func: () => panelBio.callServer(1, panelBio.id.focus, 'bio_forceUpdate', 0) + bioMenu.newItem({ + menuName: bioLg.Sources, + str: bioLg['Force update'], + func: () => bio.panel.callServer(1, bio.panel.id.focus, 'bio_forceUpdate', 0) }); - const style_arr = panelBio.style.name.slice(); - menuBio.newMenu({ - menuName: lg.Layout + const style_arr = bio.panel.style.name.slice(); + bioMenu.newMenu({ + menuName: bioLg.Layout }); - const style = pptBio.sameStyle ? pptBio.style : pptBio.artistView ? pptBio.bioStyle : pptBio.revStyle - style_arr.forEach((v, i) => menuBio.newItem({ - menuName: lg.Layout, + const style = bioSet.sameStyle ? bioSet.style : bioSet.artistView ? bioSet.bioStyle : bioSet.revStyle + style_arr.forEach((v, i) => bioMenu.newItem({ + menuName: bioLg.Layout, str: v, func: () => this.setStyle(i), checkRadio: style <= style_arr.length - 1 && i == style, separator: i == 3 || style_arr.length > 5 && i == style_arr.length - 1 })); - menuBio.newMenu({ - menuName: lg['Create && manage styles'], - appendTo: lg.Layout + bioMenu.newMenu({ + menuName: bioLg['Create && manage styles'], + appendTo: bioLg.Layout }); - [lg['Create new style...'], lg['Rename custom style...'], lg['Delete custom style...'], lg['Export custom style...'], lg['Reset style...']].forEach((v, i) => menuBio.newItem({ - menuName: lg['Create && manage styles'], + [bioLg['Create new style...'], bioLg['Rename custom style...'], bioLg['Delete custom style...'], bioLg['Export custom style...'], bioLg['Reset style...']].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg['Create && manage styles'], str: v, func: () => this.setStyles(i), - flags: !i || pptBio.style > 4 || i == 4 ? MF_STRING_BIO : MF_GRAYED_BIO, + flags: !i || bioSet.style > 4 || i == 4 ? BIO_MF_STRING : BIO_MF_GRAYED, separator: !i })); - menuBio.addSeparator({ menuName: lg.Layout }); + bioMenu.addSeparator({ menuName: bioLg.Layout }); - menuBio.newMenu({ - menuName: lg.Filmstrip, - appendTo: lg.Layout + bioMenu.newMenu({ + menuName: bioLg.Filmstrip, + appendTo: bioLg.Layout }); - [lg.Top, lg.Right, lg.Bottom, lg.Left, lg['Overlay image area'], lg['Reset to default size...']].forEach((v, i) => menuBio.newItem({ - menuName: lg.Filmstrip, + [bioLg.Top, bioLg.Right, bioLg.Bottom, bioLg.Left, bioLg['Overlay image area'], bioLg['Reset to default size...']].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg.Filmstrip, str: v, func: () => { - if (i == 4) pptBio.toggle('filmStripOverlay'); - if (i != 4 || pptBio.showFilmStrip) filmStrip.set(i == 4 ? pptBio.filmStripPos : i); + if (i == 4) bioSet.toggle('filmStripOverlay'); + if (i != 4 || bioSet.showFilmStrip) bio.filmStrip.set(i == 4 ? bioSet.filmStripPos : i); }, - flags: i != 4 || pptBio.style != 4 ? MF_STRING_BIO : MF_GRAYED_BIO, - checkItem: i == 4 && (pptBio.filmStripOverlay || (pptBio.style == 4 && !pptBio.text_only && !pptBio.img_only)), - checkRadio: i < 4 && i == pptBio.filmStripPos, + flags: i != 4 || bioSet.style != 4 ? BIO_MF_STRING : BIO_MF_GRAYED, + checkItem: i == 4 && (bioSet.filmStripOverlay || (bioSet.style == 4 && !bioSet.text_only && !bioSet.img_only)), + checkRadio: i < 4 && i == bioSet.filmStripPos, separator: i == 3 || i == 4 })); - menuBio.addSeparator({ menuName: lg.Layout }); + bioMenu.addSeparator({ menuName: bioLg.Layout }); - [lg['Reset zoom'], lg.Reload].forEach((v, i) => menuBio.newItem({ - menuName: lg.Layout, + [bioLg['Reset zoom'], bioLg.Reload].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg.Layout, str: v, - func: () => !i ? butBio.resetZoom() : window.Reload() + func: () => !i ? bio.but.resetZoom() : window.Reload() })); - menuBio.newMenu({ - menuName: lg.Image + bioMenu.newMenu({ + menuName: bioLg.Image }); - menuBio.newItem({ - menuName: lg.Image, - str: lg['Auto cycle'], - func: () => pptBio.toggle('cycPic'), - checkItem: pptBio.cycPic, + bioMenu.newItem({ + menuName: bioLg.Image, + str: bioLg['Auto cycle'], + func: () => bioSet.toggle('cycPic'), + checkItem: bioSet.cycPic, separator: true }); - if (pptBio.style < 4) { - menuBio.newMenu({ - menuName: lg.Alignment, - appendTo: lg.Image + if (bioSet.style < 4) { + bioMenu.newMenu({ + menuName: bioLg.Alignment, + appendTo: bioLg.Image }); - for (let i = 0; i < 4; i++) { menuBio.newItem({ - menuName: lg.Alignment, - str: pptBio.style == 0 || pptBio.style == 2 ? [lg.Left, lg.Centre, lg.Right, lg['Align with text']][i] : [lg.Top, lg.Centre, lg.Bottom, lg['Align with text']][i], + for (let i = 0; i < 4; i++) { bioMenu.newItem({ + menuName: bioLg.Alignment, + str: bioSet.style == 0 || bioSet.style == 2 ? [bioLg.Left, bioLg.Centre, bioLg.Right, bioLg['Align with text']][i] : [bioLg.Top, bioLg.Centre, bioLg.Bottom, bioLg['Align with text']][i], func: () => this.setImageAlignnment(i, 'standard'), - checkItem: i == 3 && pptBio.textAlign, - checkRadio: i == (pptBio.style == 0 || pptBio.style == 2 ? pptBio.alignH : pptBio.alignV), + checkItem: i == 3 && bioSet.textAlign, + checkRadio: i == (bioSet.style == 0 || bioSet.style == 2 ? bioSet.alignH : bioSet.alignV), separator: i == 2 }); } } - if (pptBio.style > 3) { - menuBio.newMenu({ - menuName: lg['Alignment horizontal'], - appendTo: lg.Image + if (bioSet.style > 3) { + bioMenu.newMenu({ + menuName: bioLg['Alignment horizontal'], + appendTo: bioLg.Image }); - [lg.Left, lg.Centre, lg.Right].forEach((v, i) => menuBio.newItem({ - menuName: lg['Alignment horizontal'], + [bioLg.Left, bioLg.Centre, bioLg.Right].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg['Alignment horizontal'], str: v, func: () => this.setImageAlignnment(i, 'horizontal'), - checkRadio: i == pptBio.alignH + checkRadio: i == bioSet.alignH })); - menuBio.newMenu({ - menuName: lg['Alignment vertical'], - appendTo: lg.Image + bioMenu.newMenu({ + menuName: bioLg['Alignment vertical'], + appendTo: bioLg.Image }); - [lg.Top, lg.Centre, lg.Bottom, lg.Auto].forEach((v, i) => menuBio.newItem({ - menuName: lg['Alignment vertical'], + [bioLg.Top, bioLg.Centre, bioLg.Bottom, bioLg.Auto].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg['Alignment vertical'], str: v, func: () => this.setImageAlignnment(i, 'vertical'), - checkRadio: [!pptBio.alignV && !pptBio.alignAuto, pptBio.alignV == 1 && !pptBio.alignAuto, pptBio.alignV == 2 && !pptBio.alignAuto, pptBio.alignAuto][i], + checkRadio: [!bioSet.alignV && !bioSet.alignAuto, bioSet.alignV == 1 && !bioSet.alignAuto, bioSet.alignV == 2 && !bioSet.alignAuto, bioSet.alignAuto][i], separator: i == 2 })); } - menuBio.addSeparator({ menuName: lg.Image }); + bioMenu.addSeparator({ menuName: bioLg.Image }); - menuBio.newMenu({ - menuName: lg['Black list'], - appendTo: lg.Image + bioMenu.newMenu({ + menuName: bioLg['Black list'], + appendTo: bioLg.Image }); - for (let i = 0; i < 3; i++) { menuBio.newItem({ - menuName: lg['Black list'], + for (let i = 0; i < 3; i++) { bioMenu.newItem({ + menuName: bioLg['Black list'], str: this.img.blacklistStr[i], func: () => this.setImageBlacklist(i), - flags: !i && this.img.isLfm || i == 2 ? MF_STRING_BIO : MF_GRAYED_BIO, - hide: i == 2 && imgBio.blackList.undo[0] != this.img.artistClean + flags: !i && this.img.isLfm || i == 2 ? BIO_MF_STRING : BIO_MF_GRAYED, + hide: i == 2 && bio.img.blackList.undo[0] != this.img.artistClean }); } - this.img.blacklist.forEach((v, i) => menuBio.newItem({ - menuName: lg['Black list'], + this.img.blacklist.forEach((v, i) => bioMenu.newItem({ + menuName: bioLg['Black list'], str: (`${this.img.artist}_${v}`).replace(/&/g, '&&'), - func: () => this.setImageBlacklist(i + (imgBio.blackList.undo[0] == this.img.artistClean ? 3 : 2)) + func: () => this.setImageBlacklist(i + (bio.img.blackList.undo[0] == this.img.artistClean ? 3 : 2)) })); - menuBio.addSeparator({}); + bioMenu.addSeparator({}); - if (pptBio.menuShowPlaylists == 2 || pptBio.menuShowPlaylists && this.shift) { + if (bioSet.menuShowPlaylists == 2 || bioSet.menuShowPlaylists && this.shift) { const pl_no = Math.ceil(this.playlist.menu.length / 30); - menuBio.newMenu({ - menuName: lg.Playlists, - separator: pptBio.menuShowPlaylists == 2 || pptBio.menuShowPlaylists && this.shift + bioMenu.newMenu({ + menuName: bioLg.Playlists, + separator: bioSet.menuShowPlaylists == 2 || bioSet.menuShowPlaylists && this.shift }); for (let j = 0; j < pl_no; j++) { const n = `# ${j * 30 + 1} - ${Math.min(this.playlist.menu.length, 30 + j * 30)}${30 + j * 30 > plman.ActivePlaylist && ((j * 30) - 1) < plman.ActivePlaylist ? ' >>>' : ''}`; - menuBio.newMenu({ + bioMenu.newMenu({ menuName: n, - appendTo: lg.Playlists + appendTo: bioLg.Playlists }); for (let i = j * 30; i < Math.min(this.playlist.menu.length, 30 + j * 30); i++) { - menuBio.newItem({ + bioMenu.newItem({ menuName: n, str: this.playlist.menu[i].name, func: () => this.setPlaylist(i), @@ -579,47 +594,47 @@ class MenuItemsBio { } } - if (pptBio.menuShowTagger == 2 || pptBio.menuShowTagger && this.shift) { - menuBio.newMenu({ - menuName: lg.Tagger, - str: lg.Tagger + (this.handles.Count ? '' : lg[': N/A no playlist tracks selected']), - flags: this.handles.Count ? MF_STRING_BIO : MF_GRAYED_BIO, - separator: pptBio.menuShowTagger == 2 || pptBio.menuShowTagger && this.shift + if (bioSet.menuShowTagger == 2 || bioSet.menuShowTagger && this.shift) { + bioMenu.newMenu({ + menuName: bioLg.Tagger, + str: bioLg.Tagger + (this.handles.Count ? '' : bioLg[': N/A no playlist tracks selected']), + flags: this.handles.Count ? BIO_MF_STRING : BIO_MF_GRAYED, + separator: bioSet.menuShowTagger == 2 || bioSet.menuShowTagger && this.shift }); - for (let i = 0; i < 13 + 4; i++) { menuBio.newItem({ - menuName: lg.Tagger, + for (let i = 0; i < 13 + 4; i++) { bioMenu.newItem({ + menuName: bioLg.Tagger, str: this.getTaggerStr(i), - func: () => cfg.setTag(i, this.handles), - flags: !i || i == 13 + 1 && !this.tags ? MF_GRAYED_BIO : MF_STRING_BIO, - checkItem: i && i < 13 + 1 && cfg[`tagEnabled${i - 1}`], + func: () => bioCfg.setTag(i, this.handles), + flags: !i || i == 13 + 1 && !this.tags ? BIO_MF_GRAYED : BIO_MF_STRING, + checkItem: i && i < 13 + 1 && bioCfg[`tagEnabled${i - 1}`], separator: !i || i == 5 || i == 11 || i == 13 }); } } - if (pptBio.menuShowMissingData == 2 || pptBio.menuShowMissingData && this.shift) { - menuBio.newMenu({ - menuName: lg['Missing data'], - separator: pptBio.menuShowMissingData == 2 || pptBio.menuShowMissingData && this.shift + if (bioSet.menuShowMissingData == 2 || bioSet.menuShowMissingData && this.shift) { + bioMenu.newMenu({ + menuName: bioLg['Missing data'], + separator: bioSet.menuShowMissingData == 2 || bioSet.menuShowMissingData && this.shift }); - [lg['Album review [allmusic]'], lg['Album review [last.fm]'], lg['Album review [wikipedia]'], lg['Biography [allmusic]'], lg['Biography [last.fm]'], lg['Biography [wikipedia]'], lg['Photos [last.fm]']].forEach((v, i) => menuBio.newItem({ - menuName: lg['Missing data'], + [bioLg['Album review [allmusic]'], bioLg['Album review [last.fm]'], bioLg['Album review [wikipedia]'], bioLg['Biography [allmusic]'], bioLg['Biography [last.fm]'], bioLg['Biography [wikipedia]'], bioLg['Photos [last.fm]']].forEach((v, i) => bioMenu.newItem({ + menuName: bioLg['Missing data'], str: v, func: () => this.checkMissingData(i), separator: i == 2 || i == 5 })); } - if (pptBio.menuShowInactivate == 2 || pptBio.menuShowInactivate && this.shift) { - menuBio.newItem({ - str: pptBio.panelActive ? lg.Inactivate : lg['Activate biography'], - func: () => panelBio.inactivate(), + if (bioSet.menuShowInactivate == 2 || bioSet.menuShowInactivate && this.shift) { + bioMenu.newItem({ + str: bioSet.panelActive ? bioLg.Inactivate : bioLg['Activate biography'], + func: () => bio.panel.inactivate(), separator: true }); } - for (let i = 0; i < 2; i++) { menuBio.newItem({ - str: [popUpBoxBio.ok ? lg['Options...'] : lg['Options: see console'], lg['Configure...']][i], - func: () => !i ? cfg.open('PanelCfg') : window.EditScript(), + for (let i = 0; i < 2; i++) { bioMenu.newItem({ + str: [bio.popUpBox.ok ? bioLg['Options...'] : bioLg['Options: see console'], bioLg['Configure...']][i], + func: () => !i ? bioCfg.open('PanelCfg') : window.EditScript(), separator: !i && this.shift, hide: i && !this.shift }); } @@ -652,29 +667,29 @@ class MenuItemsBio { } fresh() { - if (panelBio.block() || !pptBio.cycItem || panelBio.zoom() || panelBio.id.lyricsSource && lyricsBio.display() && lyricsBio.scroll) return; - if (pptBio.artistView) { + if (bio.panel.block() || !bioSet.cycItem || bio.panel.zoom() || bio.panel.id.lyricsSource && bio.lyrics.display() && bio.lyrics.scroll) return; + if (bioSet.artistView) { this.counter.bio++; - if (this.counter.bio < pptBio.cycTimeItem) return; + if (this.counter.bio < bioSet.cycTimeItem) return; this.counter.bio = 0; - if (panelBio.art.list.length < 2) return; + if (bio.panel.art.list.length < 2) return; } else { this.counter.rev++; - if (this.counter.rev < pptBio.cycTimeItem) return; + if (this.counter.rev < bioSet.cycTimeItem) return; this.counter.rev = 0; - if (panelBio.alb.list.length < 2) return; + if (bio.panel.alb.list.length < 2) return; } this.wheel(1, true, false); } getBlacklistImageItems() { - const imgInfo = imgBio.pth(); + const imgInfo = bio.img.pth(); this.img.artist = imgInfo.artist; this.path.img = imgInfo.imgPth; this.img.isLfm = imgInfo.blk && this.path.img; this.img.name = this.img.isLfm ? this.path.img.slice(this.path.img.lastIndexOf('_') + 1) : this.path.img.slice(this.path.img.lastIndexOf('\\') + 1); // needed for init this.img.blacklist = []; - this.path.blackList = `${cfg.storageFolder}blacklist_image.json`; + this.path.blackList = `${bioCfg.storageFolder}blacklist_image.json`; if (!$Bio.file(this.path.blackList)) { $Bio.save(this.path.blackList, JSON.stringify({ blacklist: {} @@ -686,32 +701,32 @@ class MenuItemsBio { this.img.blacklist = this.img.list.blacklist[this.img.artistClean] || []; } - this.img.blacklistStr = [this.img.isLfm ? `${lg['+ Add'] + (!panelBio.style.showFilmStrip ? '' : lg[' main image']) + lg[' to black list: '] + this.img.artist}_${this.img.name}` : lg['+ Add to black list: '] + (this.img.name ? lg['N/A - requires last.fm photo. Selected image: '] + this.img.name : lg['N/A - no'] + (!panelBio.style.showFilmStrip ? '' : '') + lg[' image file']), this.img.blacklist.length ? lg[' - Remove from black list (click name): '] : lg['No black listed images for current artist'], lg.Undo]; + this.img.blacklistStr = [this.img.isLfm ? `${bioLg['+ Add'] + (!bio.panel.style.showFilmStrip ? '' : bioLg[' main image']) + bioLg[' to black list: '] + this.img.artist}_${this.img.name}` : bioLg['+ Add to black list: '] + (this.img.name ? bioLg['N/A - requires last.fm photo. Selected image: '] + this.img.name : bioLg['N/A - no'] + (!bio.panel.style.showFilmStrip ? '' : '') + bioLg[' image file']), this.img.blacklist.length ? bioLg[' - Remove from black list (click name): '] : bioLg['No black listed images for current artist'], bioLg.Undo]; } getDisplayStr() { - const m = pptBio.artistView ? pptBio.bioMode : pptBio.revMode; - this.display.check = [pptBio.sameStyle ? !pptBio.img_only && !pptBio.text_only : m == 0, pptBio.sameStyle ? pptBio.img_only : m == 1, pptBio.sameStyle ? pptBio.text_only : m == 2, pptBio.showFilmStrip, pptBio.heading, pptBio.summaryShow, false, pptBio.artistView, !pptBio.artistView, !panelBio.id.focus, panelBio.id.focus]; - const n = [lg['Image+text'], lg.Image, lg.Text, lg.Filmstrip, lg.Heading, lg.Summary, pptBio.summaryCompact ? lg['Summary expand'] : lg['Summary compact'], lg['Artist view'], lg['Album view'], lg['Prefer nowplaying'], !panelBio.id.lyricsSource && !panelBio.id.nowplayingSource ? lg['Follow selected track (playlist)'] : lg['Follow selected track: N/A lyrics or nowplaying enabled']]; - const click = [!this.display.check[0] ? `\t${lg['Middle click']}` : '', !this.display.check[1] && !pptBio.text_only && !pptBio.img_only ? `\t${lg['Middle click']}` : '', !this.display.check[2] && !pptBio.img_only ? `\t${lg['Middle click']}` : '', `\t${lg['Alt+Middle click']}`, '', '', !pptBio.sourceAll ? `\t${lg.Click}` : '', !pptBio.artistView ? (!pptBio.dblClickToggle ? `\t${lg.Click}` : `\t${lg['Double click']}`) : '', pptBio.artistView ? (!pptBio.dblClickToggle ? `\t${lg.Click}` : `\t${lg['Double click']}`) : '', '', '']; + const m = bioSet.artistView ? bioSet.bioMode : bioSet.revMode; + this.display.check = [bioSet.sameStyle ? !bioSet.img_only && !bioSet.text_only : m == 0, bioSet.sameStyle ? bioSet.img_only : m == 1, bioSet.sameStyle ? bioSet.text_only : m == 2, bioSet.showFilmStrip, bioSet.heading, bioSet.summaryShow, false, bioSet.artistView, !bioSet.artistView, !bio.panel.id.focus, bio.panel.id.focus]; + const n = [bioLg['Image+text'], bioLg.Image, bioLg.Text, bioLg.Filmstrip, bioLg.Heading, bioLg.Summary, bioSet.summaryCompact ? bioLg['Summary expand'] : bioLg['Summary compact'], bioLg['Artist view'], bioLg['Album view'], bioLg['Prefer nowplaying'], !bio.panel.id.lyricsSource && !bio.panel.id.nowplayingSource ? bioLg['Follow selected track (playlist)'] : bioLg['Follow selected track: N/A lyrics or nowplaying enabled']]; + const click = [!this.display.check[0] ? `\t${bioLg['Middle click']}` : '', !this.display.check[1] && !bioSet.text_only && !bioSet.img_only ? `\t${bioLg['Middle click']}` : '', !this.display.check[2] && !bioSet.img_only ? `\t${bioLg['Middle click']}` : '', `\t${bioLg['Alt+Middle click']}`, '', '', !bioSet.sourceAll ? `\t${bioLg.Click}` : '', !bioSet.artistView ? (!bioSet.dblClickToggle ? `\t${bioLg.Click}` : `\t${bioLg['Double click']}`) : '', bioSet.artistView ? (!bioSet.dblClickToggle ? `\t${bioLg.Click}` : `\t${bioLg['Double click']}`) : '', '', '']; this.display.str = n.map((v, i) => v + click[i]) } getlookUpStr(i, j, artist) { return [ - [lg['Manual cycle: wheel over button'], lg['Auto cycle items'], popUpBoxBio.ok ? lg['Options...'] : lg['Options: see console'], lg.Reload][i], - [lg['Show similar artists'], lg['Show more tags (circle button if present)'], lg['Show artist history'], lg['Auto lock'], lg['Reset artist history...'], lg['Last.fm: '] + artist + lg['...'], lg['Last.fm: '] + artist + lg[': similar artists...'], lg['Last.fm: '] + artist + lg[': top albums...'], lg['Allmusic: '] + artist + lg['...']][i], - [lg['Show top albums'], lg['Show album history'], lg['Auto lock'], lg['Reset album history...'], lg['Last.fm: '] + artist + lg['...'], lg['Last.fm: '] + artist + lg[': similar artists...'], lg['Last.fm: '] + artist + lg[': top albums...'], lg['Allmusic: '] + artist + lg['...']][i] + [bioLg['Manual cycle: wheel over button'], bioLg['Auto cycle items'], bio.popUpBox.ok ? bioLg['Options...'] : bioLg['Options: see console'], bioLg.Reload][i], + [bioLg['Show similar artists'], bioLg['Show more tags (circle button if present)'], bioLg['Show artist history'], bioLg['Auto lock'], bioLg['Reset artist history...'], bioLg['Last.fm: '] + artist + bioLg['...'], bioLg['Last.fm: '] + artist + bioLg[': similar artists...'], bioLg['Last.fm: '] + artist + bioLg[': top albums...'], bioLg['Allmusic: '] + artist + bioLg['...']][i], + [bioLg['Show top albums'], bioLg['Show album history'], bioLg['Auto lock'], bioLg['Reset album history...'], bioLg['Last.fm: '] + artist + bioLg['...'], bioLg['Last.fm: '] + artist + bioLg[': similar artists...'], bioLg['Last.fm: '] + artist + bioLg[': top albums...'], bioLg['Allmusic: '] + artist + bioLg['...']][i] ][j]; } getOpenFlag() { - return this.path.img || this.path.am[3] || this.path.lfm[3] || this.path.wiki[3] || this.path.txt[3] || this.path.tracksAm[3] || this.path.tracksLfm[3] || this.path.tracksWiki[3] ? MF_STRING_BIO : MF_GRAYED_BIO; + return this.path.img || this.path.am[3] || this.path.lfm[3] || this.path.wiki[3] || this.path.txt[3] || this.path.tracksAm[3] || this.path.tracksLfm[3] || this.path.tracksWiki[3] ? BIO_MF_STRING : BIO_MF_GRAYED; } getOpenName() { const fo = [this.path.img, this.path.am[3], this.path.lfm[3], this.path.wiki[3], this.path.tracksAm[3], this.path.tracksLfm[3], this.path.tracksWiki[3], this.path.txt[3]]; - this.openName = [`${lg['Image ']}\t${lg['Alt+Click']}`, pptBio.artistView ? lg['Biography [allmusic]'] : lg['Review [allmusic]'], pptBio.artistView ? lg['Biography [last.fm]'] : lg['Review [last.fm]'], pptBio.artistView ? lg['Biography [wikipedia]'] : lg['Review [wikipedia]'], pptBio.artistView ? '' : lg['Tracks [allmusic]'], pptBio.artistView ? '' : lg['Tracks [last.fm]'], pptBio.artistView ? '' : lg['Tracks [wikipedia]'], pptBio.artistView ? txt.bio.subhead.txt[0] : txt.rev.subhead.txt[0]]; + this.openName = [`${bioLg['Image ']}\t${bioLg['Alt+Click']}`, bioSet.artistView ? bioLg['Biography [allmusic]'] : bioLg['Review [allmusic]'], bioSet.artistView ? bioLg['Biography [last.fm]'] : bioLg['Review [last.fm]'], bioSet.artistView ? bioLg['Biography [wikipedia]'] : bioLg['Review [wikipedia]'], bioSet.artistView ? '' : bioLg['Tracks [allmusic]'], bioSet.artistView ? '' : bioLg['Tracks [last.fm]'], bioSet.artistView ? '' : bioLg['Tracks [wikipedia]'], bioSet.artistView ? bio.txt.bio.subhead.txt[0] : bio.txt.rev.subhead.txt[0]]; let i = this.openName.length; while (i--) { if (!fo[i]) { @@ -722,73 +737,73 @@ class MenuItemsBio { } getSourceNames() { - const b = pptBio.artistView ? 'Bio' : 'Rev'; + const b = bioSet.artistView ? 'Bio' : 'Rev'; const n = b.toLowerCase(); - this.types = !txt[n].reader ? $Bio.source.amLfmWiki : $Bio.source.amLfmWikiTxt; - this.sources = [lg.Allmusic, lg['Last.fm'], lg.Wikipedia]; - this.sources = this.sources.map(v => v + (pptBio.artistView ? ' biography' : ' review')); - if (txt[n].reader) this.sources.push(txt[n].subhead.txt[0] || ''); - if (!panelBio.stndItem() && (txt.reader[n].lyrics || txt.reader[n].props)) this.sources[3] += ' // current track'; + this.types = !bio.txt[n].reader ? $Bio.source.amLfmWiki : $Bio.source.amLfmWikiTxt; + this.sources = [bioLg.Allmusic, bioLg['Last.fm'], bioLg.Wikipedia]; + this.sources = this.sources.map(v => v + (bioSet.artistView ? ' biography' : ' review')); + if (bio.txt[n].reader) this.sources.push(bio.txt[n].subhead.txt[0] || ''); + if (!bio.panel.stndItem() && (bio.txt.reader[n].lyrics || bio.txt.reader[n].props)) this.sources[3] += ' // current track'; } getTaggerStr(i) { - return !i ? lg['Write existing file info to tags: '] : i == 13 + 1 ? lg['All tagger settings...'] : i == 13 + 2 ? (cfg.taggerConfirm ? lg['Tag files...'] : `${lg.Tag} ${this.handles.Count} ${this.handles.Count > 1 ? lg.tracks : lg.track}...`) + (this.tags ? '' : lg[' N/A no tags enabled']) + (cfg.tagEnabled5 || cfg.tagEnabled7 ? tagBio.genres.length > 700 || !cfg.useWhitelist ? '' : lg[' WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]'] : '') : i == 13 + 3 ? lg.Cancel : i == 11 ? cfg[`tagName${i - 1}`] + (cfg[`tagEnabled${i - 1}`] ? ` (${cfg[`tagEnabled${i + 2}`]})` : '') : cfg[`tagName${i - 1}`]; + return !i ? bioLg['Write existing file info to tags: '] : i == 13 + 1 ? bioLg['All tagger settings...'] : i == 13 + 2 ? (bioCfg.taggerConfirm ? bioLg['Tag files...'] : `${bioLg.Tag} ${this.handles.Count} ${this.handles.Count > 1 ? bioLg.tracks : bioLg.track}...`) + (this.tags ? '' : bioLg[' N/A no tags enabled']) + (bioCfg.tagEnabled5 || bioCfg.tagEnabled7 ? bio.tag.genres.length > 700 || !bioCfg.useWhitelist ? '' : bioLg[' WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]'] : '') : i == 13 + 3 ? bioLg.Cancel : i == 11 ? bioCfg[`tagName${i - 1}`] + (bioCfg[`tagEnabled${i - 1}`] ? ` (${bioCfg[`tagEnabled${i + 2}`]})` : '') : bioCfg[`tagName${i - 1}`]; } images(v) { - return name.isLfmImg(fsoBio.GetFileName(v)); + return bio.name.isLfmImg(bioFSO.GetFileName(v)); } isRevAvail() { const type = ['alb', 'trk']; type.forEach(w => { - this[`${w}Avail`] = $Bio.source.amLfmWiki.some(v => pptBio.lockBio ? txt.rev.loaded.ix == txt.avail[`${v}${w}`] : txt.avail[`${v}${w}`] != -1); + this[`${w}Avail`] = $Bio.source.amLfmWiki.some(v => bioSet.lockBio ? bio.txt.rev.loaded.ix == bio.txt.avail[`${v}${w}`] : bio.txt.avail[`${v}${w}`] != -1); }); } lookUpAlbum(i) { - const origArr = JSON.stringify(panelBio.alb.list); + const origArr = JSON.stringify(bio.panel.alb.list); switch (true) { - case i < panelBio.alb.list.length: { - if (origArr != JSON.stringify(panelBio.alb.list) || !i && !panelBio.alb.ix || panelBio.alb.ix == i) break; - txt.logScrollPos(); - filmStrip.logScrollPos(); - panelBio.alb.ix = i; - imgBio.get = false; - txt.get = 0; + case i < bio.panel.alb.list.length: { + if (origArr != JSON.stringify(bio.panel.alb.list) || !i && !bio.panel.alb.ix || bio.panel.alb.ix == i) break; + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + bio.panel.alb.ix = i; + bio.img.get = false; + bio.txt.get = 0; let force = false; - if (pptBio.sourcerev == 3) { - pptBio.sourcerev = 0; + if (bioSet.sourcerev == 3) { + bioSet.sourcerev = 0; this.setSource('Rev'); } - panelBio.style.inclTrackRev = pptBio.inclTrackRev; - if (pptBio.inclTrackRev) { - if (i) panelBio.style.inclTrackRev = 0; - txt.albumFlush(); + bio.panel.style.inclTrackRev = bioSet.inclTrackRev; + if (bioSet.inclTrackRev) { + if (i) bio.panel.style.inclTrackRev = 0; + bio.txt.albumFlush(); force = true; } - if (panelBio.alb.list[panelBio.alb.ix].composition && pptBio.sourcerev != 0 && pptBio.sourcerev != 2) { - pptBio.sourcerev = txt.rev.am ? 0 : txt.rev.wiki ? 2 : txt.rev.am; + if (bio.panel.alb.list[bio.panel.alb.ix].composition && bioSet.sourcerev != 0 && bioSet.sourcerev != 2) { + bioSet.sourcerev = bio.txt.rev.am ? 0 : bio.txt.rev.wiki ? 2 : bio.txt.rev.am; this.setSource('Rev'); } - txt.getItem(false, panelBio.art.ix, panelBio.alb.ix, force); - txt.getScrollPos(); - imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); - panelBio.callServer(false, panelBio.id.focus, 'bio_lookUpItem', 0); - filmStrip.check(); - if (pptBio.autoLock) panelBio.mbtn_up(1, 1, true); - if (panelBio.alb.list[panelBio.alb.ix].type.includes('history')) break; - panelBio.logAlbumHistory(panelBio.alb.list[panelBio.alb.ix].artist, panelBio.alb.list[panelBio.alb.ix].album); - panelBio.getList(); + bio.txt.getItem(false, bio.panel.art.ix, bio.panel.alb.ix, force); + bio.txt.getScrollPos(); + bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); + bio.panel.callServer(false, bio.panel.id.focus, 'bio_lookUpItem', 0); + bio.filmStrip.check(); + if (bioSet.autoLock) bio.panel.mbtn_up(1, 1, true); + if (bio.panel.alb.list[bio.panel.alb.ix].type.includes('history')) break; + bio.panel.logAlbumHistory(bio.panel.alb.list[bio.panel.alb.ix].artist, bio.panel.alb.list[bio.panel.alb.ix].album); + bio.panel.getList(); break; } - case i == panelBio.alb.list.length + 1: - pptBio.toggle('cycItem'); + case i == bio.panel.alb.list.length + 1: + bioSet.toggle('cycItem'); break; - case i == panelBio.alb.list.length + 2: - cfg.open('PanelCfg'); + case i == bio.panel.alb.list.length + 2: + bioCfg.open('PanelCfg'); break; - case i == panelBio.alb.list.length + 3: + case i == bio.panel.alb.list.length + 3: window.Reload(); break; } @@ -798,82 +813,82 @@ class MenuItemsBio { lookUpAlbumItems(i) { switch (i) { case 0: - panelBio.alb.ix = 0; - pptBio.toggle('showTopAlbums'); - panelBio.getList(!pptBio.showTopAlbums, true); + bio.panel.alb.ix = 0; + bioSet.toggle('showTopAlbums'); + bio.panel.getList(!bioSet.showTopAlbums, true); break; case 1: - panelBio.alb.ix = 0; - pptBio.toggle('showAlbumHistory'); - panelBio.getList(!pptBio.showAlbumHistory, true); + bio.panel.alb.ix = 0; + bioSet.toggle('showAlbumHistory'); + bio.panel.getList(!bioSet.showAlbumHistory, true); break; case 2: - pptBio.toggle('autoLock'); + bioSet.toggle('autoLock'); break; case 3: - panelBio.resetAlbumHistory(); + bio.panel.resetAlbumHistory(); break; default: { - const artist = panelBio.art.list.length ? panelBio.art.list[0].name : name.artist(panelBio.id.focus); + const artist = bio.panel.art.list.length ? bio.panel.art.list[0].name : bio.name.artist(bio.panel.id.focus); const brArr = ['', '/+similar', '/+albums']; - if (i < 7) $Bio.browser(`https://www.last.fm/${cfg.language == 'EN' ? '' : `${cfg.language.toLowerCase()}/`}music/${encodeURIComponent(artist)}${brArr[i - 4]}`, true); + if (i < 7) $Bio.browser(`https://www.last.fm/${bioCfg.language == 'EN' ? '' : `${bioCfg.language.toLowerCase()}/`}music/${encodeURIComponent(artist)}${brArr[i - 4]}`, true); else $Bio.browser(`https://www.allmusic.com/search/artists/${encodeURIComponent(artist)}`, true); break; } } if (i < 4) { - txt.logScrollPos(); - filmStrip.logScrollPos(); - imgBio.get = false; - txt.get = 0; - if (pptBio.sourcerev == 3) { - pptBio.sourcerev = 0; + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + bio.img.get = false; + bio.txt.get = 0; + if (bioSet.sourcerev == 3) { + bioSet.sourcerev = 0; this.setSource('Rev'); } - panelBio.style.inclTrackRev = pptBio.inclTrackRev; - if (pptBio.inclTrackRev) { - if (panelBio.alb.list[panelBio.alb.ix].type.includes('history')) panelBio.style.inclTrackRev = 0; - txt.albumFlush(); + bio.panel.style.inclTrackRev = bioSet.inclTrackRev; + if (bioSet.inclTrackRev) { + if (bio.panel.alb.list[bio.panel.alb.ix].type.includes('history')) bio.panel.style.inclTrackRev = 0; + bio.txt.albumFlush(); } - txt.getItem(false, panelBio.art.ix, panelBio.alb.ix, true); - txt.getScrollPos(); - imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); - panelBio.callServer(false, panelBio.id.focus, 'bio_lookUpItem', 0); - filmStrip.check(); + bio.txt.getItem(false, bio.panel.art.ix, bio.panel.alb.ix, true); + bio.txt.getScrollPos(); + bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); + bio.panel.callServer(false, bio.panel.id.focus, 'bio_lookUpItem', 0); + bio.filmStrip.check(); } } lookUpArtist(i) { - const origArr = JSON.stringify(panelBio.art.list); + const origArr = JSON.stringify(bio.panel.art.list); switch (true) { - case i < panelBio.art.list.length: - if (origArr != JSON.stringify(panelBio.art.list) || !i && !panelBio.art.ix || panelBio.art.ix == i) break; - txt.logScrollPos(); - filmStrip.logScrollPos(); - panelBio.art.ix = i; - imgBio.get = false; - txt.get = 0; - if (pptBio.sourcebio == 3) { - pptBio.sourcebio = 1; + case i < bio.panel.art.list.length: + if (origArr != JSON.stringify(bio.panel.art.list) || !i && !bio.panel.art.ix || bio.panel.art.ix == i) break; + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + bio.panel.art.ix = i; + bio.img.get = false; + bio.txt.get = 0; + if (bioSet.sourcebio == 3) { + bioSet.sourcebio = 1; this.setSource('Bio'); } - txt.getItem(false, panelBio.art.ix, panelBio.alb.ix); - txt.getScrollPos(); - imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); - panelBio.callServer(false, panelBio.id.focus, 'bio_lookUpItem', 0); - filmStrip.check(); - if (pptBio.autoLock) panelBio.mbtn_up(1, 1, true); - if (panelBio.art.list[panelBio.art.ix].type.includes('history')) break; - panelBio.logArtistHistory(panelBio.art.list[panelBio.art.ix].name); - panelBio.getList(); + bio.txt.getItem(false, bio.panel.art.ix, bio.panel.alb.ix); + bio.txt.getScrollPos(); + bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); + bio.panel.callServer(false, bio.panel.id.focus, 'bio_lookUpItem', 0); + bio.filmStrip.check(); + if (bioSet.autoLock) bio.panel.mbtn_up(1, 1, true); + if (bio.panel.art.list[bio.panel.art.ix].type.includes('history')) break; + bio.panel.logArtistHistory(bio.panel.art.list[bio.panel.art.ix].name); + bio.panel.getList(); break; - case i == panelBio.art.list.length + 1: - pptBio.toggle('cycItem'); + case i == bio.panel.art.list.length + 1: + bioSet.toggle('cycItem'); break; - case i == panelBio.art.list.length + 2: - cfg.open('PanelCfg'); + case i == bio.panel.art.list.length + 2: + bioCfg.open('PanelCfg'); break; - case i == panelBio.art.list.length + 3: + case i == bio.panel.art.list.length + 3: window.Reload(); break; } @@ -883,48 +898,48 @@ class MenuItemsBio { lookUpArtistItems(i) { switch (i) { case 0: - panelBio.art.ix = 0; - pptBio.toggle('showSimilarArtists'); - panelBio.getList(!pptBio.showSimilarArtists); + bio.panel.art.ix = 0; + bioSet.toggle('showSimilarArtists'); + bio.panel.getList(!bioSet.showSimilarArtists); break; case 1: - panelBio.art.ix = 0; - pptBio.toggle('showMoreTags'); - panelBio.getList(!pptBio.showMoreTags); + bio.panel.art.ix = 0; + bioSet.toggle('showMoreTags'); + bio.panel.getList(!bioSet.showMoreTags); break; case 2: - panelBio.art.ix = 0; - pptBio.toggle('showArtistHistory'); - panelBio.getList(!pptBio.showArtistHistory); + bio.panel.art.ix = 0; + bioSet.toggle('showArtistHistory'); + bio.panel.getList(!bioSet.showArtistHistory); break; case 3: - pptBio.toggle('autoLock'); + bioSet.toggle('autoLock'); break; case 4: - panelBio.resetArtistHistory(); + bio.panel.resetArtistHistory(); break; default: { - const artist = panelBio.art.list.length ? panelBio.art.list[0].name : name.artist(panelBio.id.focus); + const artist = bio.panel.art.list.length ? bio.panel.art.list[0].name : bio.name.artist(bio.panel.id.focus); const brArr = ['', '/+similar', '/+albums']; - if (i < 8) $Bio.browser(`https://www.last.fm/${cfg.language == 'EN' ? '' : `${cfg.language.toLowerCase()}/`}music/${encodeURIComponent(artist)}${brArr[i - 5]}`, true); + if (i < 8) $Bio.browser(`https://www.last.fm/${bioCfg.language == 'EN' ? '' : `${bioCfg.language.toLowerCase()}/`}music/${encodeURIComponent(artist)}${brArr[i - 5]}`, true); else $Bio.browser(`https://www.allmusic.com/search/artists/${encodeURIComponent(artist)}`, true); break; } } if (i < 5) { - txt.logScrollPos(); - filmStrip.logScrollPos(); - if (pptBio.sourcebio == 3) { - pptBio.sourcebio = 1; + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + if (bioSet.sourcebio == 3) { + bioSet.sourcebio = 1; this.setSource('Bio'); } - imgBio.get = false; - txt.get = 0; - txt.getItem(false, panelBio.art.ix, panelBio.alb.ix); - txt.getScrollPos(); - imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); - panelBio.callServer(false, panelBio.id.focus, 'bio_lookUpItem', 0); - filmStrip.check(); + bio.img.get = false; + bio.txt.get = 0; + bio.txt.getItem(false, bio.panel.art.ix, bio.panel.alb.ix); + bio.txt.getScrollPos(); + bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); + bio.panel.callServer(false, bio.panel.id.focus, 'bio_lookUpItem', 0); + bio.filmStrip.check(); } } @@ -933,8 +948,8 @@ class MenuItemsBio { if (confirmed) { const handleList = fb.GetLibraryItems(); if (!handleList) return; - const tf_a = FbTitleFormat(cfg.tf.artist); - const sort = FbTitleFormat(`${cfg.tf.artist} | ${cfg.tf.album} | [[%discnumber%.]%tracknumber%. ][%track artist% - ]${cfg.tf.title}`); + const tf_a = FbTitleFormat(bioCfg.tf.artist); + const sort = FbTitleFormat(`${bioCfg.tf.artist} | ${bioCfg.tf.album} | [[%discnumber%.]%tracknumber%. ][%track artist% - ]${bioCfg.tf.title}`); let a = ''; let cur_a = '####'; let found = false; @@ -945,7 +960,7 @@ class MenuItemsBio { a = artists[i].toLowerCase(); if (a != cur_a) { cur_a = a; - const pth = panelBio.cleanPth(cfg.pth[n1], h, 'tag'); + const pth = bio.panel.cleanPth(bioCfg.pth[n1], h, 'tag'); let files = utils.Glob(`${pth}*`); files = files.some(this.images); if (a && !files) { @@ -959,7 +974,7 @@ class MenuItemsBio { } const caption = this.popUpTitle; const prompt = this.popUpText(n2, n3); - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); } @@ -968,8 +983,8 @@ class MenuItemsBio { if (confirmed) { const handleList = fb.GetLibraryItems(); if (!handleList) return; - const tf_a = FbTitleFormat(cfg.tf.artist); - const sort = FbTitleFormat(`${cfg.tf.artist} | ${cfg.tf.album} | [[%discnumber%.]%tracknumber%. ][%track artist% - ]${cfg.tf.title}`); + const tf_a = FbTitleFormat(bioCfg.tf.artist); + const sort = FbTitleFormat(`${bioCfg.tf.artist} | ${bioCfg.tf.album} | [[%discnumber%.]%tracknumber%. ][%track artist% - ]${bioCfg.tf.title}`); let a = ''; let cur_a = '####'; let found = false; @@ -980,7 +995,7 @@ class MenuItemsBio { a = artists[i].toLowerCase(); if (a != cur_a) { cur_a = a; - const pth = `${panelBio.cleanPth(cfg.pth[n1], h, 'tag') + $Bio.clean(a) + cfg.suffix[n1]}.txt`; + const pth = `${bio.panel.cleanPth(bioCfg.pth[n1], h, 'tag') + $Bio.clean(a) + bioCfg.suffix[n1]}.txt`; if (a && !$Bio.file(pth)) { found = false; m.Insert(m.Count, h); @@ -992,7 +1007,7 @@ class MenuItemsBio { } const caption = this.popUpTitle; const prompt = this.popUpText(n2, n3); - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); } @@ -1001,9 +1016,9 @@ class MenuItemsBio { if (confirmed) { const handleList = fb.GetLibraryItems(); if (!handleList) return; - const tf_albumArtist = FbTitleFormat(cfg.tf.albumArtist); - const tf_album = FbTitleFormat(cfg.tf.album); - const sort = FbTitleFormat(`${cfg.tf.albumArtist} | ${cfg.tf.album} | [[%discnumber%.]%tracknumber%. ][%track artist% - ]${cfg.tf.title}`); + const tf_albumArtist = FbTitleFormat(bioCfg.tf.albumArtist); + const tf_album = FbTitleFormat(bioCfg.tf.album); + const sort = FbTitleFormat(`${bioCfg.tf.albumArtist} | ${bioCfg.tf.album} | [[%discnumber%.]%tracknumber%. ][%track artist% - ]${bioCfg.tf.title}`); let albumArtist = ''; let cur_albumArtist = '####'; let cur_album = '####'; @@ -1016,14 +1031,14 @@ class MenuItemsBio { handleList.Convert().forEach((h, i) => { albumArtist = albumartists[i].toLowerCase(); album = albums[i].toLowerCase(); - album = !cfg.albStrip ? name.albumTidy(album) : name.albumClean(album); + album = !bioCfg.albStrip ? bio.name.albumTidy(album) : bio.name.albumClean(album); if (albumArtist + album != cur_albumArtist + cur_album) { cur_albumArtist = albumArtist; cur_album = album; - let pth = `${panelBio.cleanPth(cfg.pth[n1], h, 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix[n1]}.txt`; + let pth = `${bio.panel.cleanPth(bioCfg.pth[n1], h, 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix[n1]}.txt`; if (pth.length > 259) { album = $Bio.abbreviate(album); - pth = `${panelBio.cleanPth(cfg.pth[n1], h, 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix[n1]}.txt`; + pth = `${bio.panel.cleanPth(bioCfg.pth[n1], h, 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix[n1]}.txt`; } if (albumArtist && album && !$Bio.file(pth)) { found = false; @@ -1036,12 +1051,12 @@ class MenuItemsBio { } const caption = this.popUpTitle; const prompt = this.popUpText(n2, n3); - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); } playlists_changed() { - if (!pptBio.menuShowPlaylists) return; + if (!bioSet.menuShowPlaylists) return; this.playlist.menu = []; for (let i = 0; i < plman.PlaylistCount; i++) { this.playlist.menu.push({ name: plman.GetPlaylistName(i).replace(/&/g, '&&'), @@ -1055,10 +1070,10 @@ class MenuItemsBio { rbtn_up(x, y) { this.right_up = true; - this.shift = vkBio.k('shift'); - const imgInfo = imgBio.pth(); + this.shift = bio.vk.k('shift'); + const imgInfo = bio.img.pth(); this.docTxt = $Bio.getClipboardData() || ''; - if (!tagBio.genres.length) tagBio.setGenres(); + if (!bio.tag.genres.length) bio.tag.setGenres(); this.getDisplayStr(); this.getSourceNames(); this.img.artist = imgInfo.artist; @@ -1066,29 +1081,29 @@ class MenuItemsBio { this.img.isLfm = imgInfo.blk && this.path.img; this.img.name = this.img.isLfm ? this.path.img.slice(this.path.img.lastIndexOf('_') + 1) : this.path.img.slice(this.path.img.lastIndexOf('\\') + 1); this.isRevAvail(); - this.path.am = pptBio.artistView ? txt.bioPth('Am') : txt.revPth('Am'); - this.path.lfm = pptBio.artistView ? txt.bioPth('Lfm') : txt.revPth('Lfm'); - this.path.txt = pptBio.artistView ? txt.txtBioPth() : txt.txtRevPth(); - this.path.wiki = pptBio.artistView ? txt.bioPth('Wiki') : txt.revPth('Wiki'); - this.path.tracksAm = pptBio.artistView ? '' : txt.trackPth('Am'); - this.path.tracksLfm = pptBio.artistView ? '' : txt.trackPth('Lfm'); - this.path.tracksWiki = pptBio.artistView ? '' : txt.trackPth('Wiki'); + this.path.am = bioSet.artistView ? bio.txt.bioPth('Am') : bio.txt.revPth('Am'); + this.path.lfm = bioSet.artistView ? bio.txt.bioPth('Lfm') : bio.txt.revPth('Lfm'); + this.path.txt = bioSet.artistView ? bio.txt.txtBioPth() : bio.txt.txtRevPth(); + this.path.wiki = bioSet.artistView ? bio.txt.bioPth('Wiki') : bio.txt.revPth('Wiki'); + this.path.tracksAm = bioSet.artistView ? '' : bio.txt.trackPth('Am'); + this.path.tracksLfm = bioSet.artistView ? '' : bio.txt.trackPth('Lfm'); + this.path.tracksWiki = bioSet.artistView ? '' : bio.txt.trackPth('Wiki'); this.path.open = [this.path.img, this.path.am[1], this.path.lfm[1], this.path.wiki[1], this.path.tracksAm[1], this.path.tracksLfm[1], this.path.tracksWiki[1], this.path.txt[1]]; this.getOpenName(); this.getBlacklistImageItems(); - if (pptBio.menuShowTagger == 2 || pptBio.menuShowTagger && this.shift) this.handles = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); + if (bioSet.menuShowTagger == 2 || bioSet.menuShowTagger && this.shift) this.handles = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); this.tagsEnabled(); - menuBio.load(x, y); + bioMenu.load(x, y); this.right_up = false; // * Link with top menu Options > Biography > Display - if (!pptBio.img_only && !pptBio.text_only) { - pref.biographyDisplay = 'Image+text'; - } else if (pptBio.img_only) { - pref.biographyDisplay = 'Image'; - } else if (pptBio.text_only) { - pref.biographyDisplay = 'Text'; + if (!bioSet.img_only && !bioSet.text_only) { + grSet.biographyDisplay = 'Image+text'; + } else if (bioSet.img_only) { + grSet.biographyDisplay = 'Image'; + } else if (bioSet.text_only) { + grSet.biographyDisplay = 'Text'; } } @@ -1104,20 +1119,20 @@ class MenuItemsBio { setCover(i) { switch (true) { case i < 5: - !pptBio.loadCovAllFb ? pptBio.covType = i : imgBio.cov.selection[i] = imgBio.cov.selection[i] == -1 ? i : -1; - imgBio.cov.selFiltered = imgBio.cov.selection.filter(v => v != -1); - if (!imgBio.cov.selFiltered.length) { - imgBio.cov.selection = [0, -1, -1, -1, -1]; - imgBio.cov.selFiltered = [0]; + !bioSet.loadCovAllFb ? bioSet.covType = i : bio.img.cov.selection[i] = bio.img.cov.selection[i] == -1 ? i : -1; + bio.img.cov.selFiltered = bio.img.cov.selection.filter(v => v != -1); + if (!bio.img.cov.selFiltered.length) { + bio.img.cov.selection = [0, -1, -1, -1, -1]; + bio.img.cov.selFiltered = [0]; } - pptBio.loadCovSelFb = JSON.stringify(imgBio.cov.selection); - !pptBio.loadCovAllFb ? imgBio.getImages() : imgBio.check(); + bioSet.loadCovSelFb = JSON.stringify(bio.img.cov.selection); + !bioSet.loadCovAllFb ? bio.img.getImages() : bio.img.check(); break; case i == 5: - imgBio.toggle('loadCovAllFb'); + bio.img.toggle('loadCovAllFb'); break; case i == 6: - imgBio.toggle('loadCovFolder'); + bio.img.toggle('loadCovFolder'); break; } } @@ -1127,40 +1142,40 @@ class MenuItemsBio { case 0: case 1: case 2: - if (pptBio.sameStyle) panelBio.mode(i); + if (bioSet.sameStyle) bio.panel.mode(i); else { - pptBio.artistView ? pptBio.bioMode = i : pptBio.revMode = i; - txt.refresh(0); + bioSet.artistView ? bioSet.bioMode = i : bioSet.revMode = i; + bio.txt.refresh(0); } break; case 3: - filmStrip.mbtn_up('onOff'); + bio.filmStrip.mbtn_up('onOff'); break; case 4: - txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; - pptBio.heading = !pptBio.heading ? 1 : 0; - panelBio.style.fullWidthHeading = pptBio.heading && pptBio.fullWidthHeading; - if (panelBio.style.inclTrackRev == 1) txt.logScrollPos(); - txt.refresh(1); + bio.txt.bio.scrollPos = {}; bio.txt.rev.scrollPos = {}; + bioSet.heading = !bioSet.heading ? 1 : 0; + bio.panel.style.fullWidthHeading = bioSet.heading && bioSet.fullWidthHeading; + if (bio.panel.style.inclTrackRev == 1) bio.txt.logScrollPos(); + bio.txt.refresh(1); break; case 5: case 6: - txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; - pptBio.toggle(i == 5 ? 'summaryShow' : 'summaryCompact'); - panelBio.setSummary(); - txt.refresh(1); + bio.txt.bio.scrollPos = {}; bio.txt.rev.scrollPos = {}; + bioSet.toggle(i == 5 ? 'summaryShow' : 'summaryCompact'); + bio.panel.setSummary(); + bio.txt.refresh(1); break; case 7: case 8: - panelBio.click('', '', true); + bio.panel.click('', '', true); break; case 9: case 10: - pptBio.toggle('focus'); - panelBio.id.focus = pptBio.focus; - panelBio.changed(); - txt.on_playback_new_track(); - imgBio.on_playback_new_track(); + bioSet.toggle('focus'); + bio.panel.id.focus = bioSet.focus; + bio.panel.changed(); + bio.txt.on_playback_new_track(); + bio.img.on_playback_new_track(); break; } } @@ -1170,38 +1185,38 @@ class MenuItemsBio { case 'standard': switch (i) { case 3: - pptBio.toggle('textAlign'); - panelBio.setStyle(); - imgBio.clearCache(); - imgBio.getImages(); + bioSet.toggle('textAlign'); + bio.panel.setStyle(); + bio.img.clearCache(); + bio.img.getImages(); break; default: - if (pptBio.style == 0 || pptBio.style == 2) pptBio.alignH = i; - else pptBio.alignV = i; - imgBio.clearCache(); - imgBio.getImages(); + if (bioSet.style == 0 || bioSet.style == 2) bioSet.alignH = i; + else bioSet.alignV = i; + bio.img.clearCache(); + bio.img.getImages(); break; } break; case 'horizontal': - pptBio.alignH = i; - imgBio.clearCache(); - imgBio.getImages(); + bioSet.alignH = i; + bio.img.clearCache(); + bio.img.getImages(); break; case 'vertical': switch (i) { case 3: - pptBio.alignAuto = true; - panelBio.setStyle(); - imgBio.clearCache(); - imgBio.getImages(); + bioSet.alignAuto = true; + bio.panel.setStyle(); + bio.img.clearCache(); + bio.img.getImages(); break; default: - pptBio.alignV = i; - pptBio.alignAuto = false; - panelBio.setStyle(); - imgBio.clearCache(); - imgBio.getImages(); + bioSet.alignV = i; + bioSet.alignAuto = false; + bio.panel.setStyle(); + bio.img.clearCache(); + bio.img.getImages(); break; } break; @@ -1212,45 +1227,45 @@ class MenuItemsBio { if (!i) { if (!this.img.list.blacklist[this.img.artistClean]) this.img.list.blacklist[this.img.artistClean] = []; this.img.list.blacklist[this.img.artistClean].push(this.img.name); - } else if (imgBio.blackList.undo[0] == this.img.artistClean && i == 2) { - if (!this.img.list.blacklist[imgBio.blackList.undo[0]]) this.img.list.blacklist[this.img.artistClean] = []; - if (imgBio.blackList.undo[1].length) this.img.list.blacklist[imgBio.blackList.undo[0]].push(imgBio.blackList.undo[1]); - imgBio.blackList.undo = []; + } else if (bio.img.blackList.undo[0] == this.img.artistClean && i == 2) { + if (!this.img.list.blacklist[bio.img.blackList.undo[0]]) this.img.list.blacklist[this.img.artistClean] = []; + if (bio.img.blackList.undo[1].length) this.img.list.blacklist[bio.img.blackList.undo[0]].push(bio.img.blackList.undo[1]); + bio.img.blackList.undo = []; } else { - const bl_ind = i - (imgBio.blackList.undo[0] == this.img.artistClean ? 3 : 2); - imgBio.blackList.undo = [this.img.artistClean, this.img.list.blacklist[this.img.artistClean][bl_ind]]; + const bl_ind = i - (bio.img.blackList.undo[0] == this.img.artistClean ? 3 : 2); + bio.img.blackList.undo = [this.img.artistClean, this.img.list.blacklist[this.img.artistClean][bl_ind]]; this.img.list.blacklist[this.img.artistClean].splice(bl_ind, 1); $Bio.removeNulls(this.img.list); } const bl = this.img.list.blacklist[this.img.artistClean]; if (bl) this.img.list.blacklist[this.img.artistClean] = this.sort([...new Set(bl)]); - imgBio.blackList.artist = ''; + bio.img.blackList.artist = ''; $Bio.save(this.path.blackList, JSON.stringify({ blacklist: $Bio.sortKeys(this.img.list.blacklist) }, null, 3), true); - imgBio.check(); + bio.img.check(); window.NotifyOthers('bio_blacklist', 'bio_blacklist'); } setPaste(i) { switch (i) { case 0: case 1: case 2: { - const n = pptBio.artistView ? 'bio' : 'rev'; + const n = bioSet.artistView ? 'bio' : 'rev'; const s = $Bio.source.amLfmWiki[i]; this.undo.folder = this.path[s][0]; this.undo.path = this.path[s][1]; this.undo.text = $Bio.open(this.undo.path); $Bio.buildPth(this.undo.folder); - $Bio.save(this.undo.path, `${this.docTxt}\r\n\r\nCustom ${pptBio.artistView ? 'Biography' : 'Review'}`, true); - const b = pptBio.artistView ? 'Bio' : 'Rev'; - const pth = txt[`${n}Pth`](['Am', 'Lfm', 'Wiki'][i]); + $Bio.save(this.undo.path, `${this.docTxt}\r\n\r\nCustom ${bioSet.artistView ? 'Biography' : 'Review'}`, true); + const b = bioSet.artistView ? 'Bio' : 'Rev'; + const pth = bio.txt[`${n}Pth`](['Am', 'Lfm', 'Wiki'][i]); if (this.path[s][1] == pth[1]) { - pptBio[`source${b}`] = 0; - txt[n].source[s] = true; + bioSet[`source${b}`] = 0; + bio.txt[n].source[s] = true; } window.NotifyOthers('bio_getText', 'bio_getText'); - txt.grab(); - if (pptBio.text_only) txt.paint(); + bio.txt.grab(); + if (bioSet.text_only) bio.txt.paint(); break; } case 3: { @@ -1262,14 +1277,14 @@ class MenuItemsBio { } case 4: if (!this.undo.text.length && $Bio.file(this.undo.path)) { - fsoBio.DeleteFile(this.undo.path); + bioFSO.DeleteFile(this.undo.path); window.NotifyOthers('bio_reload', 'bio_reload'); - if (panelBio.stndItem()) window.Reload(); + if (bio.panel.stndItem()) window.Reload(); else { - txt.artistFlush(); - txt.albumFlush(); - txt.grab(); - if (pptBio.text_only) txt.paint(); + bio.txt.artistFlush(); + bio.txt.albumFlush(); + bio.txt.grab(); + if (bioSet.text_only) bio.txt.paint(); } break; } @@ -1277,20 +1292,20 @@ class MenuItemsBio { $Bio.save(this.undo.path, this.undo.text, true); this.undo.text = '#!#'; window.NotifyOthers('bio_getText', 'bio_getText'); - txt.grab(); - if (pptBio.text_only) txt.paint(); + bio.txt.grab(); + if (bioSet.text_only) bio.txt.paint(); break; } } setPhotoType(i) { - pptBio.cycPhoto = i < 2; - pptBio.cycPhotoLocation = i; - if (i == 1 && !pptBio.get('SYSTEM.Photo Folder Checked', false)) { + bioSet.cycPhoto = i < 2; + bioSet.cycPhotoLocation = i; + if (i == 1 && !bioSet.get('SYSTEM.Photo Folder Checked', false)) { fb.ShowPopupMessage('Enter folder in options: "Server Settings"\\Photo\\Custom photo folder.', 'Biography: custom folder for photo cycling'); - pptBio.set('SYSTEM.Photo Folder Checked', true); + bioSet.set('SYSTEM.Photo Folder Checked', true); } - imgBio.updImages(); + bio.img.updImages(); } setPlaylist(i) { @@ -1298,52 +1313,52 @@ class MenuItemsBio { } setReviewType(i) { - txt.logScrollPos(); - panelBio.style.inclTrackRev = pptBio.inclTrackRev = [0, 2, 1][i]; - if (pptBio.inclTrackRev) { serverBio.checkTrack({ - focus: panelBio.id.focus, + bio.txt.logScrollPos(); + bio.panel.style.inclTrackRev = bioSet.inclTrackRev = [0, 2, 1][i]; + if (bioSet.inclTrackRev) { bio.server.checkTrack({ + focus: bio.panel.id.focus, force: false, menu: true, - artist: panelBio.art.list.length ? panelBio.art.list[0].name : name.artist(panelBio.id.focus), - title: name.title(panelBio.id.focus) + artist: bio.panel.art.list.length ? bio.panel.art.list[0].name : bio.name.artist(bio.panel.id.focus), + title: bio.name.title(bio.panel.id.focus) }); } - txt.refresh(1); - txt.getScrollPos(); + bio.txt.refresh(1); + bio.txt.getScrollPos(); } setSource(b, n) { n = n || b.toLowerCase(); - $Bio.source.amLfmWikiTxt.forEach((v, i) => txt[n].source[v] = pptBio[`source${n}`] == i); - $Bio.source.amLfmWiki.forEach(v => { if (txt[n].source[v]) txt.done[`${v}${b}`] = false }); - txt[n].source.ix = pptBio[`source${n}`]; + $Bio.source.amLfmWikiTxt.forEach((v, i) => bio.txt[n].source[v] = bioSet[`source${n}`] == i); + $Bio.source.amLfmWiki.forEach(v => { if (bio.txt[n].source[v]) bio.txt.done[`${v}${b}`] = false }); + bio.txt[n].source.ix = bioSet[`source${n}`]; } setStyle(i) { - const prop = pptBio.sameStyle ? 'style' : pptBio.artistView ? 'bioStyle' : 'revStyle'; - pptBio[prop] = i; - imgBio.mask.reset = true; - pptBio.img_only = false; - pptBio.text_only = false; - txt.refresh(0); - if (pptBio.filmStripOverlay && pptBio.showFilmStrip) filmStrip.set(pptBio.filmStripPos); + const prop = bioSet.sameStyle ? 'style' : bioSet.artistView ? 'bioStyle' : 'revStyle'; + bioSet[prop] = i; + bio.img.mask.reset = true; + bioSet.img_only = false; + bioSet.text_only = false; + bio.txt.refresh(0); + if (bioSet.filmStripOverlay && bioSet.showFilmStrip) bio.filmStrip.set(bioSet.filmStripPos); } setStyles(i) { switch (i) { case 0: - panelBio.createStyle(); + bio.panel.createStyle(); break; case 1: - panelBio.renameStyle(pptBio.style); + bio.panel.renameStyle(bioSet.style); break; case 2: - panelBio.deleteStyle(pptBio.style); + bio.panel.deleteStyle(bioSet.style); break; case 3: - panelBio.exportStyle(pptBio.style); + bio.panel.exportStyle(bioSet.style); break; case 4: - panelBio.resetStyle(pptBio.style); + bio.panel.resetStyle(bioSet.style); break; } } @@ -1352,22 +1367,22 @@ class MenuItemsBio { switch (i) { case 0: case 1: this.toggle(4, b); break; - case 2: txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; pptBio.toggle('sourceAll'); txt.refresh(1); break; + case 2: bio.txt.bio.scrollPos = {}; bio.txt.rev.scrollPos = {}; bioSet.toggle('sourceAll'); bio.txt.refresh(1); break; case 3: - pptBio.toggle('showTrackRevOptions'); - txt.logScrollPos(); - panelBio.style.inclTrackRev = pptBio.inclTrackRev = 0; - if (pptBio.showTrackRevOptions) { serverBio.checkTrack({ - focus: panelBio.id.focus, + bioSet.toggle('showTrackRevOptions'); + bio.txt.logScrollPos(); + bio.panel.style.inclTrackRev = bioSet.inclTrackRev = 0; + if (bioSet.showTrackRevOptions) { bio.server.checkTrack({ + focus: bio.panel.id.focus, force: false, menu: true, - artist: panelBio.art.list.length ? panelBio.art.list[0].name : name.artist(panelBio.id.focus), - title: name.title(panelBio.id.focus) + artist: bio.panel.art.list.length ? bio.panel.art.list[0].name : bio.name.artist(bio.panel.id.focus), + title: bio.name.title(bio.panel.id.focus) }); } - txt.refresh(1); - txt.getScrollPos(); + bio.txt.refresh(1); + bio.txt.getScrollPos(); break; - case 4: pptBio.toggle('classicalMusicMode'); pptBio.classicalAlbFallback = pptBio.classicalMusicMode; txt.refresh(1); break; + case 4: bioSet.toggle('classicalMusicMode'); bioSet.classicalAlbFallback = bioSet.classicalMusicMode; bio.txt.refresh(1); break; } } @@ -1378,102 +1393,102 @@ class MenuItemsBio { tagsEnabled() { this.tags = false; for (let i = 0; i < 13; i++) - { if (cfg[`tagEnabled${i}`]) { + { if (bioCfg[`tagEnabled${i}`]) { this.tags = true; break; } } } toggle(i, b, fix, direction) { - txt.logScrollPos(); + bio.txt.logScrollPos(); const n = b.toLowerCase(); - if (i === pptBio[`source${n}`]) return; + if (i === bioSet[`source${n}`]) return; if (i == 4) { - pptBio.toggle('lockBio'); + bioSet.toggle('lockBio'); } else { - if (i === '') i = pptBio[`source${n}`]; + if (i === '') i = bioSet[`source${n}`]; if (fix) { - pptBio[`source${n}`] = i; - } else if (pptBio.lockBio && !pptBio.sourceAll) { - const limit = txt[n].reader ? 3 : 2; - direction == 1 ? pptBio[`source${n}`] = i == limit ? 0 : ++i : pptBio[`source${n}`] = i == 0 ? limit : --i; - } else if (txt[n].reader) { - switch (txt[n].loaded.ix) { - case 0: pptBio[`source${n}`] = direction == 1 ? (txt[n].lfm ? 1 : txt[n].wiki ? 2 : txt[n].txt ? 3 : 0) : (txt[n].txt ? 3 : txt[n].wiki ? 2 : txt[n].lfm ? 1 : 0); break; - case 1: pptBio[`source${n}`] = direction == 1 ? (txt[n].wiki ? 2 : txt[n].txt ? 3 : txt[n].am ? 0 : 1) : (txt[n].am ? 0 : txt[n].txt ? 3 : txt[n].wiki ? 2 : 1); break; - case 2: pptBio[`source${n}`] = direction == 1 ? (txt[n].txt ? 3 : txt[n].am ? 0 : txt[n].lfm ? 1 : 2) : (txt[n].lfm ? 1 : txt[n].am ? 0 : txt[n].txt ? 3 : 2); break; - case 3: pptBio[`source${n}`] = direction == 1 ? (txt[n].am ? 0 : txt[n].lfm ? 1 : txt[n].wiki ? 2 : 3) : (txt[n].wiki ? 2 : txt[n].lfm ? 1 : txt[n].am ? 0 : 3); break; + bioSet[`source${n}`] = i; + } else if (bioSet.lockBio && !bioSet.sourceAll) { + const limit = bio.txt[n].reader ? 3 : 2; + direction == 1 ? bioSet[`source${n}`] = i == limit ? 0 : ++i : bioSet[`source${n}`] = i == 0 ? limit : --i; + } else if (bio.txt[n].reader) { + switch (bio.txt[n].loaded.ix) { + case 0: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].lfm ? 1 : bio.txt[n].wiki ? 2 : bio.txt[n].txt ? 3 : 0) : (bio.txt[n].txt ? 3 : bio.txt[n].wiki ? 2 : bio.txt[n].lfm ? 1 : 0); break; + case 1: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].wiki ? 2 : bio.txt[n].txt ? 3 : bio.txt[n].am ? 0 : 1) : (bio.txt[n].am ? 0 : bio.txt[n].txt ? 3 : bio.txt[n].wiki ? 2 : 1); break; + case 2: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].txt ? 3 : bio.txt[n].am ? 0 : bio.txt[n].lfm ? 1 : 2) : (bio.txt[n].lfm ? 1 : bio.txt[n].am ? 0 : bio.txt[n].txt ? 3 : 2); break; + case 3: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].am ? 0 : bio.txt[n].lfm ? 1 : bio.txt[n].wiki ? 2 : 3) : (bio.txt[n].wiki ? 2 : bio.txt[n].lfm ? 1 : bio.txt[n].am ? 0 : 3); break; } } else { - switch (txt[n].loaded.ix) { - case 0: pptBio[`source${n}`] = direction == 1 ? (txt[n].lfm ? 1 : txt[n].wiki ? 2 : 0) : (txt[n].wiki ? 2 : txt[n].lfm ? 1 : 0); break; - case 1: pptBio[`source${n}`] = direction == 1 ? (txt[n].wiki ? 2 : txt[n].am ? 0 : 1) : (txt[n].am ? 0 : txt[n].wiki ? 2 : 1); break; - case 2: pptBio[`source${n}`] = direction == 1 ? (txt[n].am ? 0 : txt[n].lfm ? 1 : 2) : (txt[n].lfm ? 1 : txt[n].am ? 0 : 2); break; + switch (bio.txt[n].loaded.ix) { + case 0: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].lfm ? 1 : bio.txt[n].wiki ? 2 : 0) : (bio.txt[n].wiki ? 2 : bio.txt[n].lfm ? 1 : 0); break; + case 1: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].wiki ? 2 : bio.txt[n].am ? 0 : 1) : (bio.txt[n].am ? 0 : bio.txt[n].wiki ? 2 : 1); break; + case 2: bioSet[`source${n}`] = direction == 1 ? (bio.txt[n].am ? 0 : bio.txt[n].lfm ? 1 : 2) : (bio.txt[n].lfm ? 1 : bio.txt[n].am ? 0 : 2); break; } } } this.setSource(b, n); - txt.getText(false); - butBio.src.y = butBio.src.fontSize < 12 || txt[n].loaded.ix == 2 ? 1 : 0; - txt.getScrollPos(); - imgBio.getImages(); + bio.txt.getText(false); + bio.but.src.y = bio.but.src.fontSize < 12 || bio.txt[n].loaded.ix == 2 ? 1 : 0; + bio.txt.getScrollPos(); + bio.img.getImages(); } wheel(step, resetCounters) { let i = 0; - butBio.clearTooltip(); + bio.but.clearTooltip(); let force = false; switch (true) { - case pptBio.artistView: - if (!panelBio.art.uniq.length) break; - for (i = 0; i < panelBio.art.uniq.length; i++) - { if (!panelBio.art.ix && name.artist(panelBio.id.focus) == panelBio.art.uniq[i].name || panelBio.art.ix == panelBio.art.uniq[i].ix) break; } + case bioSet.artistView: + if (!bio.panel.art.uniq.length) break; + for (i = 0; i < bio.panel.art.uniq.length; i++) + { if (!bio.panel.art.ix && bio.name.artist(bio.panel.id.focus) == bio.panel.art.uniq[i].name || bio.panel.art.ix == bio.panel.art.uniq[i].ix) break; } i += step; - if (i < 0) i = panelBio.art.uniq.length - 1; - else if (i >= panelBio.art.uniq.length) i = 0; - txt.logScrollPos(); - filmStrip.logScrollPos(); - if (pptBio.sourcebio == 3) { - pptBio.sourcebio = 1; + if (i < 0) i = bio.panel.art.uniq.length - 1; + else if (i >= bio.panel.art.uniq.length) i = 0; + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + if (bioSet.sourcebio == 3) { + bioSet.sourcebio = 1; this.setSource('Bio'); } - panelBio.art.ix = panelBio.art.uniq[i].ix; - if (panelBio.art.list[panelBio.art.ix].type.includes('history')) break; - panelBio.logArtistHistory(panelBio.art.list[panelBio.art.ix].name); - panelBio.getList(); + bio.panel.art.ix = bio.panel.art.uniq[i].ix; + if (bio.panel.art.list[bio.panel.art.ix].type.includes('history')) break; + bio.panel.logArtistHistory(bio.panel.art.list[bio.panel.art.ix].name); + bio.panel.getList(); break; - case !pptBio.artistView: - if (!panelBio.alb.uniq.length) break; - for (i = 0; i < panelBio.alb.uniq.length; i++) - { if (!panelBio.alb.ix && `${name.albumArtist(panelBio.id.focus)} - ${name.album(panelBio.id.focus)}` == `${panelBio.alb.uniq[i].artist} - ${panelBio.alb.uniq[i].album}` || panelBio.alb.ix == panelBio.alb.uniq[i].ix) break; } + case !bioSet.artistView: + if (!bio.panel.alb.uniq.length) break; + for (i = 0; i < bio.panel.alb.uniq.length; i++) + { if (!bio.panel.alb.ix && `${bio.name.albumArtist(bio.panel.id.focus)} - ${bio.name.album(bio.panel.id.focus)}` == `${bio.panel.alb.uniq[i].artist} - ${bio.panel.alb.uniq[i].album}` || bio.panel.alb.ix == bio.panel.alb.uniq[i].ix) break; } i += step; - if (i < 0) i = panelBio.alb.uniq.length - 1; - else if (i >= panelBio.alb.uniq.length) i = 0; - txt.logScrollPos(); - filmStrip.logScrollPos(); - if (pptBio.sourcerev == 3) { - pptBio.sourcerev = 0; + if (i < 0) i = bio.panel.alb.uniq.length - 1; + else if (i >= bio.panel.alb.uniq.length) i = 0; + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + if (bioSet.sourcerev == 3) { + bioSet.sourcerev = 0; this.setSource('Rev'); } - panelBio.alb.ix = panelBio.alb.uniq[i].ix; - if (panelBio.alb.ix) seeker.show = false; - if (pptBio.showAlbumHistory && pptBio.inclTrackRev) { - panelBio.style.inclTrackRev = pptBio.inclTrackRev; - if (panelBio.alb.list[panelBio.alb.ix].type.includes('history')) panelBio.style.inclTrackRev = 0; - txt.albumFlush(); + bio.panel.alb.ix = bio.panel.alb.uniq[i].ix; + if (bio.panel.alb.ix) bio.seeker.show = false; + if (bioSet.showAlbumHistory && bioSet.inclTrackRev) { + bio.panel.style.inclTrackRev = bioSet.inclTrackRev; + if (bio.panel.alb.list[bio.panel.alb.ix].type.includes('history')) bio.panel.style.inclTrackRev = 0; + bio.txt.albumFlush(); force = true; } - if (panelBio.alb.list[panelBio.alb.ix].type.includes('history')) break; - panelBio.logAlbumHistory(panelBio.alb.list[panelBio.alb.ix].artist, panelBio.alb.list[panelBio.alb.ix].album); - panelBio.getList(); + if (bio.panel.alb.list[bio.panel.alb.ix].type.includes('history')) break; + bio.panel.logAlbumHistory(bio.panel.alb.list[bio.panel.alb.ix].artist, bio.panel.alb.list[bio.panel.alb.ix].album); + bio.panel.getList(); break; } - imgBio.get = false; - txt.getItem(false, panelBio.art.ix, panelBio.alb.ix, force); - txt.getScrollPos(); - imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); - panelBio.lookUpServer(); - if (resetCounters) pptBio.artistView ? this.counter.bio = 0 : this.counter.rev = 0; - filmStrip.check(); + bio.img.get = false; + bio.txt.getItem(false, bio.panel.art.ix, bio.panel.alb.ix, force); + bio.txt.getScrollPos(); + bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); + bio.panel.lookUpServer(); + if (resetCounters) bioSet.artistView ? this.counter.bio = 0 : this.counter.rev = 0; + bio.filmStrip.check(); } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-names.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-names.js index 0b52e9cc..218026a1 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-names.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-names.js @@ -1,6 +1,6 @@ 'use strict'; -class Names { +class BioNames { constructor() { this.cur_artist = ''; this.lfmUID = '_[a-f0-9]{32}\\.jpg$'; @@ -9,33 +9,33 @@ class Names { // * METHODS * // alb(focus) { - return $Bio.eval(`[$trim(${cfg.tf.album})]`, focus); + return $Bio.eval(`[$trim(${bioCfg.tf.album})]`, focus); } albID(focus, n) { - if (!this.alb(focus)) return $Bio.eval(`${cfg.tf.artist + cfg.tf.albumArtist}%path%`, focus); + if (!this.alb(focus)) return $Bio.eval(`${bioCfg.tf.artist + bioCfg.tf.albumArtist}%path%`, focus); switch (n) { case 'simple': - return $Bio.eval(cfg.tf.artist + cfg.tf.albumArtist + cfg.tf.album, focus); + return $Bio.eval(bioCfg.tf.artist + bioCfg.tf.albumArtist + bioCfg.tf.album, focus); case 'stnd': - return $Bio.eval(`${cfg.tf.albumArtist + cfg.tf.album}%discnumber%%date%`, focus); + return $Bio.eval(`${bioCfg.tf.albumArtist + bioCfg.tf.album}%discnumber%%date%`, focus); case 'full': - return $Bio.eval(`${cfg.tf.artist + cfg.tf.albumArtist + cfg.tf.album}%discnumber%%date%`, focus); + return $Bio.eval(`${bioCfg.tf.artist + bioCfg.tf.albumArtist + bioCfg.tf.album}%discnumber%%date%`, focus); } } albm(focus, ignoreLock) { - return this.albumTidy($Bio.eval(`[${cfg.tf.album}]`, focus, ignoreLock)); + return this.albumTidy($Bio.eval(`[${bioCfg.tf.album}]`, focus, ignoreLock)); } album(focus, ignoreLock) { - if (!cfg.albStrip) return this.albm(focus); - return this.albumClean($Bio.eval(`[${cfg.tf.album}]`, focus, ignoreLock)); + if (!bioCfg.albStrip) return this.albm(focus); + return this.albumClean($Bio.eval(`[${bioCfg.tf.album}]`, focus, ignoreLock)); } albumArtist(focus, ignoreLock) { - const albumArtist = $Bio.eval(`[$trim(${cfg.tf.albumArtist})]`, focus, ignoreLock); - const radioTrackArtist = !isRadioStreamParser || !panelBio.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).artist; + const albumArtist = $Bio.eval(`[$trim(${bioCfg.tf.albumArtist})]`, focus, ignoreLock); + const radioTrackArtist = !bioIsRadioStreamParser || !bio.panel.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).artist; return radioTrackArtist || albumArtist; } @@ -48,13 +48,13 @@ class Names { } artist(focus, ignoreLock) { - const artist = $Bio.eval(`[$trim(${cfg.tf.artist})]`, focus, ignoreLock); - const radioTrackArtist = !isRadioStreamParser || !panelBio.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).artist; + const artist = $Bio.eval(`[$trim(${bioCfg.tf.artist})]`, focus, ignoreLock); + const radioTrackArtist = !bioIsRadioStreamParser || !bio.panel.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).artist; return radioTrackArtist || artist; } composition(focus, ignoreLock) { - return $Bio.eval(`[${cfg.tf.composition}]`, focus, ignoreLock); + return $Bio.eval(`[${bioCfg.tf.composition}]`, focus, ignoreLock); } isLfmImg(fn, artist) { @@ -68,12 +68,12 @@ class Names { } title(focus, ignoreLock) { - const title = $Bio.eval(`[$trim(${cfg.tf.title})]`, focus, ignoreLock); - const radioTrackTitle = !isRadioStreamParser || !panelBio.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).title; + const title = $Bio.eval(`[$trim(${bioCfg.tf.title})]`, focus, ignoreLock); + const radioTrackTitle = !bioIsRadioStreamParser || !bio.panel.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).title; return radioTrackTitle || title; } trackID(focus) { - return $Bio.eval(cfg.tf.artist + cfg.tf.title, focus); + return $Bio.eval(bioCfg.tf.artist + bioCfg.tf.title, focus); } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-panel.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-panel.js index 718b7f8a..06f3fc1d 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-panel.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-panel.js @@ -1,6 +1,6 @@ 'use strict'; -class PanelBio { +class BioPanel { constructor() { this.arc = 10; this.calc = true; @@ -17,7 +17,7 @@ class PanelBio { }; this.notifyTimestamp = Date.now(); - window.NotifyOthers(`bio_notServer${pptBio.serverName}`, this.notifyTimestamp); + window.NotifyOthers(`bio_notServer${bioSet.serverName}`, this.notifyTimestamp); this.tf = {}; this.w = 0; @@ -26,14 +26,14 @@ class PanelBio { init: [], ix: 0, list: [], - history: $Bio.jsonParse(pptBio.albumHistory, []), + history: $Bio.jsonParse(bioSet.albumHistory, []), uniq: [] }; this.art = { cur: '', - fields: cfg.artFields.map(v => `%${v}%`), - history: $Bio.jsonParse(pptBio.artistHistory, []), + fields: bioCfg.artFields.map(v => `%${v}%`), + history: $Bio.jsonParse(bioSet.artistHistory, []), init: [], ix: 0, list: [], @@ -43,10 +43,10 @@ class PanelBio { }; this.bor = { - t: pptBio.borL, - r: pptBio.borR, - b: pptBio.borB, - l: pptBio.borL + t: bioSet.borL, + r: bioSet.borR, + b: bioSet.borB, + l: bioSet.borL }; this.filmStripSize = { @@ -73,7 +73,7 @@ class PanelBio { curAlb: '', artist: '', curArtist: '', - focus: pptBio.focus, + focus: bioSet.focus, last_pressed_coord: { x: -1, y: -1 @@ -91,7 +91,7 @@ class PanelBio { }; for (let i = 0; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && pptBio[`pthTxtReader${i}`] && pptBio[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1]) && !/nowplaying/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1])) { + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && bioSet[`pthTxtReader${i}`] && bioSet[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1]) && !/nowplaying/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1])) { this.id.lyricsSource = true; this.id.focus = false; break; @@ -99,7 +99,7 @@ class PanelBio { } for (let i = 0; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && /nowplaying/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1])) { + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && /nowplaying/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1])) { this.id.nowplayingSource = true; this.id.focus = false; break; @@ -107,13 +107,13 @@ class PanelBio { } for (let i = 0; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && /item_properties/i.test(utils.SplitFilePath(pptBio[`pthTxtReader${i}`])[1])) { + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && /item_properties/i.test(utils.SplitFilePath(bioSet[`pthTxtReader${i}`])[1])) { this.id.propsSource = true; break; } } - this.id.lookUp = pptBio.lookUp; + this.id.lookUp = bioSet.lookUp; this.im = { t: 0, @@ -130,7 +130,7 @@ class PanelBio { }; this.logo = { - img: my_utilsBio.getImageAsset('Logo.png') + img: bio_my_utils.getImageAsset('Logo.png') }; this.repaint = { @@ -145,24 +145,24 @@ class PanelBio { x: 0, y: 0, h: 100, - style: !pptBio.sbarFullHeight ? 2 : 0, + style: !bioSet.sbarFullHeight ? 2 : 0, top_corr: 0 }; this.style = { - cycTimeItem: Math.max(pptBio.cycTimeItem, 30), + cycTimeItem: Math.max(bioSet.cycTimeItem, 30), enlarged_img: false, - free: $Bio.jsonParse(pptBio.styleFree, false), - fullWidthHeading: pptBio.heading && pptBio.fullWidthHeading, - gap: pptBio.gap, + free: $Bio.jsonParse(bioSet.styleFree, false), + fullWidthHeading: bioSet.heading && bioSet.fullWidthHeading, + gap: bioSet.gap, imgSize: 0, - inclTrackRev: pptBio.inclTrackRev, + inclTrackRev: bioSet.inclTrackRev, max_y: 0, minH: 50, moreTags: false, name: [], new: false, - overlay: $Bio.jsonParse(pptBio.styleOverlay, false), + overlay: $Bio.jsonParse(bioSet.styleOverlay, false), showFilmStrip: false }; @@ -200,9 +200,9 @@ class PanelBio { this.topAlbumsKey = 'Top Albums: |Top-Alben: |\\u00c1lbumes M\\u00e1s Escuchados: |Top Albums: |Album Pi\\u00f9 Ascoltati: |\\u4eba\\u6c17\\u30a2\\u30eb\\u30d0\\u30e0: |Najpopularniejsze Albumy: |\\u00c1lbuns Principais: |\\u041f\\u043e\\u043f\\u0443\\u043b\\u044f\\u0440\\u043d\\u044b\\u0435 \\u0430\\u043b\\u044c\\u0431\\u043e\\u043c\\u044b: |Toppalbum: |En Sevilen Alb\\u00fcmler: |\\u6700\\u4f73\\u4e13\\u8f91: '; if (!this.style.free || !$Bio.isArray(this.style.free)) { - pptBio.set('Panel Biography - System: Freestyle Custom BackUp', pptBio.styleFree); + bioSet.set('Panel Biography - System: Freestyle Custom BackUp', bioSet.styleFree); this.style.free = []; - pptBio.styleFree = JSON.stringify(this.style.free); + bioSet.styleFree = JSON.stringify(this.style.free); fb.ShowPopupMessage('Unable to load custom styles.\n\nThe save location was corrupt. Custom styles have been reset.\n\nThe original should be backed up to "SYSTEM.Freestyle Custom BackUp" in panel properties.', 'Biography'); } else { let valid = true; @@ -210,14 +210,14 @@ class PanelBio { if (!$Bio.objHasOwnProperty(v, 'name') || isNaN(v.imL) || isNaN(v.imR) || isNaN(v.imT) || isNaN(v.imB) || isNaN(v.txL) || isNaN(v.txR) || isNaN(v.txT) || isNaN(v.txB)) valid = false; }); if (!valid) { - pptBio.set('Panel Biography - System: Freestyle Custom BackUp', pptBio.styleFree); + bioSet.set('Panel Biography - System: Freestyle Custom BackUp', bioSet.styleFree); this.style.free = []; - pptBio.styleFree = JSON.stringify(this.style.free); + bioSet.styleFree = JSON.stringify(this.style.free); fb.ShowPopupMessage('Unable to load custom styles.\n\nThe save location was corrupt. Custom styles have been reset.\n\nThe original should be backed up to "SYSTEM.Freestyle Custom BackUp" in panel properties.', 'Biography'); } } if (!this.style.overlay || !$Bio.objHasOwnProperty(this.style.overlay, 'name') || isNaN(this.style.overlay.imL) || isNaN(this.style.overlay.imR) || isNaN(this.style.overlay.imT) || isNaN(this.style.overlay.imB) || isNaN(this.style.overlay.txL) || isNaN(this.style.overlay.txR) || isNaN(this.style.overlay.txT) || isNaN(this.style.overlay.txB)) { - pptBio.set('Panel Biography - System: Overlay BackUp', pptBio.styleOverlay); + bioSet.set('Panel Biography - System: Overlay BackUp', bioSet.styleOverlay); this.style.overlay = { name: 'Overlay', @@ -231,7 +231,7 @@ class PanelBio { txB: 0 }; - pptBio.styleOverlay = JSON.stringify(this.style.overlay); + bioSet.styleOverlay = JSON.stringify(this.style.overlay); fb.ShowPopupMessage('Unable to load "Panel Biography - System: Overlay".\n\nThe save location was corrupt. The overlay style has been reset to default.\n\nThe original should be backed up to "Panel Biography - System: Overlay BackUp" in panel properties.', 'Biography'); } @@ -259,7 +259,7 @@ class PanelBio { switch (type) { case 0: if ($Bio.server) { - serverBio.download(force, { + bio.server.download(force, { ix: this.art.ix, focus, arr: this.art.list.slice(0) @@ -269,7 +269,7 @@ class PanelBio { arr: this.alb.list.slice(0) }, notify); } - if (!$Bio.server || pptBio.multiServer) { + if (!$Bio.server || bioSet.multiServer) { window.NotifyOthers(notify, [{ ix: this.art.ix, focus, @@ -282,7 +282,7 @@ class PanelBio { } break; case 1: - serverBio.download(force, { + bio.server.download(force, { ix: this.art.ix, focus, arr: this.art.list.slice(0) @@ -307,75 +307,75 @@ class PanelBio { { key: 'lookUpServerRate', descr: 'Panel Biography - Panel Lookup Refresh Rate', min: 1500, max: 15000, oldDef: 1500, newDef: 1500 } ].forEach((rate) => { const name = `${rate.descr} ${rate.min}-${rate.max} msec (Max)`; - const value = pptBio.get(name, null); + const value = bioSet.get(name, null); if (value === null) { throw (`property_name: ${name}\nPanel's rate property name does not match range checked`); } else { - if (pptBio[rate.key] === rate.oldDef && pptBio[rate.key] !== rate.newDef) { pptBio[rate.key] = rate.newDef; } - pptBio[rate.key] = $Bio.clamp(pptBio[rate.key], rate.min, rate.max); - if (pptBio[rate.key] !== Number(value)) { - pptBio.set(name, pptBio[rate.key]); + if (bioSet[rate.key] === rate.oldDef && bioSet[rate.key] !== rate.newDef) { bioSet[rate.key] = rate.newDef; } + bioSet[rate.key] = $Bio.clamp(bioSet[rate.key], rate.min, rate.max); + if (bioSet[rate.key] !== Number(value)) { + bioSet.set(name, bioSet[rate.key]); } } }); this.focusLoad = $Bio.debounce(() => { - if (!pptBio.img_only) txt.on_playback_new_track(); - if (!pptBio.text_only || uiBio.style.isBlur || pptBio.showFilmStrip) imgBio.on_playback_new_track(); - }, pptBio.focusLoadRate, { - leading: pptBio.focusLoadImmediate, + if (!bioSet.img_only) bio.txt.on_playback_new_track(); + if (!bioSet.text_only || bio.ui.style.isBlur || bioSet.showFilmStrip) bio.img.on_playback_new_track(); + }, bioSet.focusLoadRate, { + leading: bioSet.focusLoadImmediate, trailing: true }); this.focusServer = $Bio.debounce(() => { this.changed(); - }, pptBio.focusServerRate); + }, bioSet.focusServerRate); this.lookUpServer = $Bio.debounce(() => { this.callServer(false, this.id.focus, 'bio_lookUpItem', 0); - }, pptBio.lookUpServerRate); + }, bioSet.lookUpServerRate); } checkNumServers() { - pptBio.multiServer = false; + bioSet.multiServer = false; window.NotifyOthers('bio_checkNumServers', 0); this.id.numServersChecked = true; } changeView(x, y, menu) { - if (!menu && (this.zoom() || vkBio.k('alt') || x < 0 || y < 0 || x > this.w || y > this.h || butBio.Dn)) return false; - if (!menu && !pptBio.dblClickToggle && this.isTouchEvent(x, y)) return false; - if (!menu && !pptBio.img_only && (txt.scrollbar_type().onSbar && !txt.lyricsDisplayed()) || butBio.trace('heading', x, y) || butBio.trace('lookUp', x, y)) return false; + if (!menu && (this.zoom() || bio.vk.k('alt') || x < 0 || y < 0 || x > this.w || y > this.h || bio.but.Dn)) return false; + if (!menu && !bioSet.dblClickToggle && this.isTouchEvent(x, y)) return false; + if (!menu && !bioSet.img_only && (bio.txt.scrollbar_type().onSbar && !bio.txt.lyricsDisplayed()) || bio.but.trace('heading', x, y) || bio.but.trace('lookUp', x, y)) return false; return true; } checkFilm() { - if (!pptBio.showFilmStrip) return; + if (!bioSet.showFilmStrip) return; const item = this.getItem(); if (Date.now() - this.id.loadTimestamp > 1500) { // delay needed for correct sizing on init; ignored by click (sets loadTimestamp = 0); switch (item) { case 'stndArtist': - !this.id.lookUp ? txt.getText(true) : txt.getItem(true, this.art.ix, this.alb.ix); - imgBio.getImages(); + !this.id.lookUp ? bio.txt.getText(true) : bio.txt.getItem(true, this.art.ix, this.alb.ix); + bio.img.getImages(); break; case 'stndAlbum': - this.style.inclTrackRev != 1 || !this.id.lookUp ? txt.getText(true) : txt.getItem(true, this.art.ix, this.alb.ix); - imgBio.getImages(); + this.style.inclTrackRev != 1 || !this.id.lookUp ? bio.txt.getText(true) : bio.txt.getItem(true, this.art.ix, this.alb.ix); + bio.img.getImages(); break; case 'lookUp': - txt.getItem(true, this.art.ix, this.alb.ix); - imgBio.getItem(this.art.ix, this.alb.ix); + bio.txt.getItem(true, this.art.ix, this.alb.ix); + bio.img.getItem(this.art.ix, this.alb.ix); break; } - butBio.refresh(true); - txt.getScrollPos(); - txt.paint(); + bio.but.refresh(true); + bio.txt.getScrollPos(); + bio.txt.paint(); } } cleanPth(pth, item, type, artist, album, bio) { if (!pth) return ''; pth = pth.trim().replace(/\//g, '\\'); - pth = cfg.expandPath(pth); + pth = bioCfg.expandPath(pth); switch (type) { case 'remap': pth = bio ? this.tfBio(pth, artist, item) : this.tfRev(pth, artist, album, item); @@ -413,39 +413,39 @@ class PanelBio { this.clicked = this.changeView(x, y, menu); if (!this.clicked) return; this.id.loadTimestamp = 0; - txt.logScrollPos(); - filmStrip.logScrollPos(); - pptBio.toggle('artistView'); - imgBio.resetTimestamps(); + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); + bioSet.toggle('artistView'); + bio.img.resetTimestamps(); const sameStyle = this.sameStyle(); if (!sameStyle) this.setStyle(); - txt.na = ''; - timerBio.clear(timerBio.source); + bio.txt.na = ''; + bio.timer.clear(bio.timer.source); if (!this.lock && this.updateNeeded()) { this.getList(true, true); - if (!pptBio.artistView) txt.albumReset(); + if (!bioSet.artistView) bio.txt.albumReset(); } const item = this.getItem(); switch (item) { case 'stndArtist': - !this.id.lookUp ? txt.getText(this.calc) : txt.getItem(this.calc, this.art.ix, this.alb.ix); - imgBio.getImages(); + !this.id.lookUp ? bio.txt.getText(this.calc) : bio.txt.getItem(this.calc, this.art.ix, this.alb.ix); + bio.img.getImages(); break; case 'stndAlbum': - this.style.inclTrackRev != 1 || !this.id.lookUp ? txt.getText(this.calc) : txt.getItem(this.calc, this.art.ix, this.alb.ix); - imgBio.getImages(); + this.style.inclTrackRev != 1 || !this.id.lookUp ? bio.txt.getText(this.calc) : bio.txt.getItem(this.calc, this.art.ix, this.alb.ix); + bio.img.getImages(); break; case 'lookUp': - txt.getItem(this.calc, this.art.ix, this.alb.ix); - imgBio.getItem(this.art.ix, this.alb.ix); + bio.txt.getItem(this.calc, this.art.ix, this.alb.ix); + bio.img.getItem(this.art.ix, this.alb.ix); break; } - if (pptBio.img_only) imgBio.setCrop(true); - butBio.refresh(true); - if (!sameStyle && pptBio.filmStripOverlay && pptBio.showFilmStrip) filmStrip.set(pptBio.filmStripPos); - if (!pptBio.artistView) imgBio.setCheckArr(null); + if (bioSet.img_only) bio.img.setCrop(true); + bio.but.refresh(true); + if (!sameStyle && bioSet.filmStripOverlay && bioSet.showFilmStrip) bio.filmStrip.set(bioSet.filmStripPos); + if (!bioSet.artistView) bio.img.setCheckArr(null); this.move(x, y, true); - txt.getScrollPos(); + bio.txt.getScrollPos(); this.calc = false; } @@ -458,7 +458,7 @@ class PanelBio { }; const caption = 'Create New Layout'; const prompt = 'This copies the current layout style & saves it to the entered name\n\nThe copy is in freestyle format that offers fully flexible drag style positioning of image & text boxes + overlay effects\n\nContinue?'; - const fallback = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.input(caption, prompt, ok_callback, '', 'My Style') : true; + const fallback = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.input(caption, prompt, ok_callback, '', 'My Style') : true; if (fallback) { try { ns = utils.InputBox(0, prompt, caption, 'My Style', true); @@ -469,14 +469,14 @@ class PanelBio { let lines_drawn; let imgs; let te_t; - switch (pptBio.style) { + switch (bioSet.style) { case 0: { - let txt_h = Math.round((this.h - this.bor.t - pptBio.textB) * (1 - pptBio.rel_imgs)); - lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); - txt_h = lines_drawn * uiBio.font.main_h + this.style.gap; - imgs = Math.max(this.h - txt_h - this.bor.t - pptBio.textB - uiBio.heading.h, 10); + let txt_h = Math.round((this.h - this.bor.t - bioSet.textB) * (1 - bioSet.rel_imgs)); + lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); + txt_h = lines_drawn * bio.ui.font.main_h + this.style.gap; + imgs = Math.max(this.h - txt_h - this.bor.t - bioSet.textB - bio.ui.heading.h, 10); this.im.b = (this.h - this.bor.t - imgs - this.bor.b) / this.h; - this.tx.t = (this.bor.t + imgs - pptBio.textT + this.style.gap) / this.h; + this.tx.t = (this.bor.t + imgs - bioSet.textT + this.style.gap) / this.h; this.im.l = 0; this.im.r = 0; this.im.t = 0; @@ -486,12 +486,12 @@ class PanelBio { break; } case 1: { - const txt_sp = Math.round((this.w - pptBio.textL - this.bor.r) * (1 - pptBio.rel_imgs)); - lines_drawn = Math.max(Math.floor((this.h - pptBio.textT - pptBio.textB - uiBio.heading.h) / uiBio.font.main_h), 0); - te_t = !pptBio.topAlign ? pptBio.textT + (this.h - pptBio.textT - pptBio.textB - lines_drawn * uiBio.font.main_h + uiBio.heading.h) / 2 : pptBio.textT + uiBio.heading.h; - this.im.l = (txt_sp + this.style.gap + (pptBio.sbarShow ? uiBio.sbar.sp + 10 : 0)) / this.w; - this.tx.r = (this.w - (txt_sp + pptBio.textR)) / this.w; - this.tx.t = (te_t - uiBio.heading.h - pptBio.textT) / this.h; + const txt_sp = Math.round((this.w - bioSet.textL - this.bor.r) * (1 - bioSet.rel_imgs)); + lines_drawn = Math.max(Math.floor((this.h - bioSet.textT - bioSet.textB - bio.ui.heading.h) / bio.ui.font.main_h), 0); + te_t = !bioSet.topAlign ? bioSet.textT + (this.h - bioSet.textT - bioSet.textB - lines_drawn * bio.ui.font.main_h + bio.ui.heading.h) / 2 : bioSet.textT + bio.ui.heading.h; + this.im.l = (txt_sp + this.style.gap + (bioSet.sbarShow ? bio.ui.sbar.sp + 10 : 0)) / this.w; + this.tx.r = (this.w - (txt_sp + bioSet.textR)) / this.w; + this.tx.t = (te_t - bio.ui.heading.h - bioSet.textT) / this.h; this.im.r = 0; this.im.t = 0; this.im.b = 0; @@ -500,13 +500,13 @@ class PanelBio { break; } case 2: { - let txt_h = Math.round((this.h - pptBio.textT - this.bor.b) * (1 - pptBio.rel_imgs)); - lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); - txt_h = lines_drawn * uiBio.font.main_h + this.style.gap; - imgs = Math.max(this.h - txt_h - this.bor.b - pptBio.textT - uiBio.heading.h, 10); + let txt_h = Math.round((this.h - bioSet.textT - this.bor.b) * (1 - bioSet.rel_imgs)); + lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); + txt_h = lines_drawn * bio.ui.font.main_h + this.style.gap; + imgs = Math.max(this.h - txt_h - this.bor.b - bioSet.textT - bio.ui.heading.h, 10); const img_t = this.h - this.bor.b - imgs; this.im.t = img_t / this.h; - this.tx.b = (this.h - img_t - pptBio.textB + this.style.gap) / this.h; + this.tx.b = (this.h - img_t - bioSet.textB + this.style.gap) / this.h; this.im.l = 0; this.im.r = 0; this.im.b = 0; @@ -516,14 +516,14 @@ class PanelBio { break; } case 3: { - const te_r = pptBio.sbarShow ? Math.max(pptBio.textR, uiBio.sbar.sp + 10) : pptBio.textR; - const txt_sp = Math.round((this.w - this.bor.l - te_r) * (1 - pptBio.rel_imgs)); + const te_r = bioSet.sbarShow ? Math.max(bioSet.textR, bio.ui.sbar.sp + 10) : bioSet.textR; + const txt_sp = Math.round((this.w - this.bor.l - te_r) * (1 - bioSet.rel_imgs)); imgs = Math.max(this.w - txt_sp - this.bor.l - te_r - this.style.gap, 10); - lines_drawn = Math.max(Math.floor((this.h - pptBio.textT - pptBio.textB - uiBio.heading.h) / uiBio.font.main_h), 0); - te_t = !pptBio.topAlign ? pptBio.textT + (this.h - pptBio.textT - pptBio.textB - lines_drawn * uiBio.font.main_h + uiBio.heading.h) / 2 : pptBio.textT + uiBio.heading.h; + lines_drawn = Math.max(Math.floor((this.h - bioSet.textT - bioSet.textB - bio.ui.heading.h) / bio.ui.font.main_h), 0); + te_t = !bioSet.topAlign ? bioSet.textT + (this.h - bioSet.textT - bioSet.textB - lines_drawn * bio.ui.font.main_h + bio.ui.heading.h) / 2 : bioSet.textT + bio.ui.heading.h; this.im.r = (this.w - this.bor.l - imgs - this.bor.r) / this.w; - this.tx.l = (this.bor.l + imgs - pptBio.textL + this.style.gap) / this.w; - this.tx.t = (te_t - uiBio.heading.h - pptBio.textT) / this.h; + this.tx.l = (this.bor.l + imgs - bioSet.textL + this.style.gap) / this.w; + this.tx.t = (te_t - bio.ui.heading.h - bioSet.textT) / this.h; this.im.l = 0; this.im.t = 0; this.im.b = 0; @@ -535,9 +535,9 @@ class PanelBio { this.style.free.forEach(v => { if (v.name == ns) ns = ns + ' New'; }); - if (pptBio.style > 3 && (pptBio.img_only || pptBio.text_only)) { - if (pptBio.style - 6 >= this.style.free.length) this.getStyleFallback(); - const obj = pptBio.style == 4 || pptBio.style == 5 ? this.style.overlay : this.style.free[pptBio.style - 6]; + if (bioSet.style > 3 && (bioSet.img_only || bioSet.text_only)) { + if (bioSet.style - 6 >= this.style.free.length) this.getStyleFallback(); + const obj = bioSet.style == 4 || bioSet.style == 5 ? this.style.overlay : this.style.free[bioSet.style - 6]; this.im.l = $Bio.clamp(obj.imL, 0, 1); this.im.r = $Bio.clamp(obj.imR, 0, 1); this.im.t = $Bio.clamp(obj.imT, 0, 1); @@ -559,24 +559,24 @@ class PanelBio { txB: this.tx.b }); this.sort(this.style.free, 'name'); - pptBio.styleFree = JSON.stringify(this.style.free); + bioSet.styleFree = JSON.stringify(this.style.free); this.style.free.some((v, i) => { if (v.name == ns) { - if (pptBio.sameStyle) pptBio.style = i + 6; - else if (pptBio.artistView) pptBio.bioStyle = i + 6; - else pptBio.revStyle = i + 6; + if (bioSet.sameStyle) bioSet.style = i + 6; + else if (bioSet.artistView) bioSet.bioStyle = i + 6; + else bioSet.revStyle = i + 6; return true; } }); this.getStyleNames(); - txt.refresh(0); - timerBio.clear(timerBio.source); - timerBio.source.id = setTimeout(() => { + bio.txt.refresh(0); + bio.timer.clear(bio.timer.source); + bio.timer.source.id = setTimeout(() => { this.style.new = false; window.Repaint(); - timerBio.source.id = null; + bio.timer.source.id = null; }, 10000); - if (timerBio.source.id !== 0) { + if (bio.timer.source.id !== 0) { this.style.new = true; window.Repaint(); } @@ -586,47 +586,47 @@ class PanelBio { const continue_confirmation = (status, confirmed) => { if (confirmed) { this.style.free.splice(n - 6, 1); - pptBio.styleFree = JSON.stringify(this.style.free); - pptBio.style = 0; - if (!pptBio.sameStyle) { - if (pptBio.artistView) pptBio.bioStyle = 0; - else pptBio.revStyle = 0; + bioSet.styleFree = JSON.stringify(this.style.free); + bioSet.style = 0; + if (!bioSet.sameStyle) { + if (bioSet.artistView) bioSet.bioStyle = 0; + else bioSet.revStyle = 0; } this.getStyleNames(); - if (!pptBio.showFilmStrip) txt.refresh(0); - else filmStrip.set(pptBio.filmStripPos); + if (!bioSet.showFilmStrip) bio.txt.refresh(0); + else bio.filmStrip.set(bioSet.filmStripPos); } }; const caption = 'Delete Current Style'; const prompt = `Delete: ${this.style.name[n]}\n\nStyle will be set to top`; - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); } draw(gr) { - let font = uiBio.font.main; + let font = bio.ui.font.main; let str = 'POWERED by allmusic, last.fm & Wikipedia.\r\n\r\nShift+middle click to activate / inactivate.'; let textHeight2; const textHeight1 = Math.round(gr.MeasureString(str, font, 10, 0, this.w - 20, 1000, StringFormat(1, 1)).Height); const version = ` ${window.ScriptInfo.Name}: v${window.ScriptInfo.Version}`; - const versionHeight = gr.CalcTextHeight(version, uiBio.font.small); + const versionHeight = gr.CalcTextHeight(version, bio.ui.font.small); const txtSp = this.h * 0.37; if (textHeight1 > txtSp) { str = str.replace('\r\n\r\n', ' '); textHeight2 = Math.round(gr.MeasureString(str, font, 10, 0, this.w - 20, 1000, StringFormat(1, 1)).Height); - if (textHeight2 > txtSp) font = uiBio.font.small; + if (textHeight2 > txtSp) font = bio.ui.font.small; } const textHeight3 = Math.round(gr.MeasureString(str, font, 10, 0, this.w - 20, 1000, StringFormat(1, 1)).Height); if (textHeight3 > txtSp) str = 'Shift+middle click to activate.'; - let textCol = uiBio.col.text; - let textCol_h = uiBio.col.text_h; - if (pptBio.theme > 0) { - textCol = uiBio.dui ? window.GetColourDUI(0) : window.GetColourCUI(0); - textCol_h = uiBio.dui ? window.GetColourDUI(2) : window.GetColourCUI(2); + let textCol = bio.ui.col.text; + // let textCol_h = bio.ui.col.text_h; + if (bioSet.theme > 0) { + textCol = bio.ui.dui ? window.GetColourDUI(0) : window.GetColourCUI(0); + // textCol_h = bio.ui.dui ? window.GetColourDUI(2) : window.GetColourCUI(2); } const hAvail = (this.h - txtSp - versionHeight) * 0.9; const wAvail = this.w * 0.9; @@ -634,13 +634,13 @@ class PanelBio { this.logo.w = scale[0]; this.logo.h = scale[1]; this.logo.x = (this.w - this.logo.w) / 2; - this.logo.y = hAvail - this.logo.h + versionHeight + hAvail * 0.145 + uiBio.y + pptBio.borT; + this.logo.y = hAvail - this.logo.h + versionHeight + hAvail * 0.145 + bio.ui.y + bioSet.borT; gr.SetInterpolationMode(7); if (this.logo.img) gr.DrawImage(this.logo.img, this.logo.x, this.logo.y, this.logo.w, this.logo.h, 0, 0, this.logo.img.Width, this.logo.img.Height); gr.SetInterpolationMode(0); - // gr.GdiDrawText(version, uiBio.font.small, textCol, 10, pptBio.borT * 0.5 + this.h - txtSp, this.w - 20, txtSp, txt.ncc); - gr.GdiDrawText(str, font, textCol, 10, uiBio.y + this.h - txtSp, this.w - 20, txtSp, txt.ncc); + // gr.GdiDrawText(version, bio.ui.font.small, textCol, 10, bioSet.borT * 0.5 + this.h - txtSp, this.w - 20, txtSp, txt.ncc); + gr.GdiDrawText(str, font, textCol, 10, bio.ui.y + this.h - txtSp, this.w - 20, txtSp, bio.txt.ncc); } exportStyle(n) { @@ -651,21 +651,21 @@ class PanelBio { }; const caption = 'Export Current Style To Other Biography Panels'; const prompt = `Export: ${this.style.name[n]}`; - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); } getItem() { - if (!this.art.ix && pptBio.artistView) return 'stndArtist'; - return !this.alb.ix && !pptBio.artistView ? 'stndAlbum' : 'lookUp'; + if (!this.art.ix && bioSet.artistView) return 'stndArtist'; + return !this.alb.ix && !bioSet.artistView ? 'stndAlbum' : 'lookUp'; } getList(p_clear, isAlbum) { if (!this.id.lookUp) return; - const artist = name.artist(this.id.focus, true) || lg['Artist Unknown']; - const albumArtist = (!panelBio.isRadio(this.id.focus) ? name.albumArtist(this.id.focus, true) : artist) || lg['Artist Unknown']; - const composition = isAlbum ? false : pptBio.classicalMusicMode && (txt.rev.loaded.am && !txt.rev.amFallback || txt.rev.loaded.wiki && !txt.rev.wikiFallback); - const album = !composition ? name.album(this.id.focus, true) || lg['Album Unknown'] : name.composition(this.id.focus, true) || lg['Composition Unknown']; + const artist = bio.name.artist(this.id.focus, true) || bioLg['Artist Unknown']; + const albumArtist = (!bio.panel.isRadio(this.id.focus) ? bio.name.albumArtist(this.id.focus, true) : artist) || bioLg['Artist Unknown']; + const composition = isAlbum ? false : bioSet.classicalMusicMode && (bio.txt.rev.loaded.am && !bio.txt.rev.amFallback || bio.txt.rev.loaded.wiki && !bio.txt.rev.wikiFallback); + const album = !composition ? bio.name.album(this.id.focus, true) || bioLg['Album Unknown'] : bio.name.composition(this.id.focus, true) || bioLg['Composition Unknown']; if (this.lock) { this.logArtistHistory(artist); this.logAlbumHistory(albumArtist, album, composition); @@ -673,9 +673,9 @@ class PanelBio { } let k = 0; - const lfmBio = `${(!panelBio.isRadio(this.id.focus) ? this.cleanPth(cfg.pth.foLfmBio, this.id.focus) : this.cleanPth(cfg.remap.foLfmBio, this.id.focus, 'remap', artist, '', 1)) + $Bio.clean(artist) + cfg.suffix.foLfmBio}.txt`; + const lfmBio = `${(!bio.panel.isRadio(this.id.focus) ? this.cleanPth(bioCfg.pth.foLfmBio, this.id.focus) : this.cleanPth(bioCfg.remap.foLfmBio, this.id.focus, 'remap', artist, '', 1)) + $Bio.clean(artist) + bioCfg.suffix.foLfmBio}.txt`; const lBio = $Bio.open(lfmBio); - const lfmSim = `${(!panelBio.isRadio(this.id.focus) ? this.cleanPth(cfg.pth.foLfmSim, this.id.focus) : this.cleanPth(cfg.remap.foLfmSim, this.id.focus, 'remap', artist, '', 1)) + $Bio.clean(artist)} And Similar Artists.json`; + const lfmSim = `${(!bio.panel.isRadio(this.id.focus) ? this.cleanPth(bioCfg.pth.foLfmSim, this.id.focus) : this.cleanPth(bioCfg.remap.foLfmSim, this.id.focus, 'remap', artist, '', 1)) + $Bio.clean(artist)} And Similar Artists.json`; const mult_arr = []; let mn = ''; let nm = ''; @@ -690,17 +690,17 @@ class PanelBio { field: '', type: 'Artist' }); - if (pptBio.showSimilarArtists) { + if (bioSet.showSimilarArtists) { if ($Bio.file(lfmSim)) { const lSim = $Bio.jsonParse(lfmSim, false, 'file'); let newStyle = false; if (lSim) { if ($Bio.objHasOwnProperty(lSim[0], 'name')) newStyle = true; lSim.shift(); - $Bio.take(lSim, cfg.menuSimilarNum); + $Bio.take(lSim, bioCfg.menuSimilarNum); if (lSim.length) { this.art.list.push({ - name: lg['Similar Artists:'], + name: bioLg['Similar Artists:'], field: '', type: 'label' }); @@ -714,26 +714,26 @@ class PanelBio { } else { if ($Bio.file(lfmBio)) { let found = false; - sa = tagBio.getTag(lBio, this.similarArtistsKey).tag; + sa = bio.tag.getTag(lBio, this.similarArtistsKey).tag; if (sa.length < 7 && sa) { - $Bio.take(sa, cfg.menuSimilarNum); + $Bio.take(sa, bioCfg.menuSimilarNum); found = true; } if (!found) { this.art.similar.some(v => { if (v.name == artist) { - sa = $Bio.take(v.similar, cfg.menuSimilarNum); + sa = $Bio.take(v.similar, bioCfg.menuSimilarNum); return found = true; } }); if (!found) { - const getSimilar = new LfmSimilarArtists(() => getSimilar.onStateChange(), this.getSimilar_search_done.bind(this)); + const getSimilar = new BioLfmSimilarArtists(() => getSimilar.onStateChange(), this.getSimilar_search_done.bind(this)); getSimilar.search(artist, '', '', 6); } } if (found && $Bio.isArray(sa) && sa.length) { this.art.list.push({ - name: lg['Similar Artists:'], + name: bioLg['Similar Artists:'], field: '', type: 'label' }); @@ -747,14 +747,14 @@ class PanelBio { } } - if (pptBio.showMoreTags) { + if (bioSet.showMoreTags) { this.style.moreTags = false; this.art.fields.forEach(v => { nm = v.replace(/%/g, ''); for (let h = 0; h < $Bio.eval(`$meta_num(${nm})`, this.id.focus); h++) { mn = `$trim($meta(${nm},${h}))`; const name = $Bio.eval(mn, this.id.focus); - if (this.art.list.every(v => v.name !== name) && name.toLowerCase() != cfg.va.toLowerCase()) { + if (this.art.list.every(v => v.name !== name) && name.toLowerCase() != bioCfg.va.toLowerCase()) { mult_arr.push({ name, field: ` ~ ${$Bio.titlecase(nm)}`, @@ -776,7 +776,7 @@ class PanelBio { if (mult_arr.length) { this.style.moreTags = true; this.art.list.push({ - name: lg['More Tags:'], + name: bioLg['More Tags:'], field: '', type: 'label' }); @@ -792,13 +792,13 @@ class PanelBio { if (!(albumArtist + album) || !this.alb.cur || albumArtist + album != this.alb.cur) { this.logAlbumHistory(albumArtist, album, composition); - this.style.inclTrackRev = pptBio.inclTrackRev; + this.style.inclTrackRev = bioSet.inclTrackRev; this.alb.cur = albumArtist + album; } - if (this.art.history.length && pptBio.showArtistHistory) { + if (this.art.history.length && bioSet.showArtistHistory) { this.art.list.push({ - name: lg['Artist History:'], + name: bioLg['Artist History:'], field: '', type: 'label' }); @@ -811,9 +811,9 @@ class PanelBio { this.art.list.forEach((v, i) => v.ix = i); this.art.uniq = this.art.list.filter(v => v.type != 'label'); - if (pptBio.showTopAlbums && $Bio.file(lfmBio)) { + if (bioSet.showTopAlbums && $Bio.file(lfmBio)) { let found = false; - ta = tagBio.getTag(lBio, this.topAlbumsKey).tag; + ta = bio.tag.getTag(lBio, this.topAlbumsKey).tag; if (ta.length < 7 && ta) found = true; if (!found) { this.art.topAlbums.some(v => { @@ -823,7 +823,7 @@ class PanelBio { } }); if (!found) { - const getTopAlb = new LfmTopAlbums(() => getTopAlb.onStateChange(), this.getTopAlb_search_done.bind(this)); + const getTopAlb = new BioLfmTopAlbums(() => getTopAlb.onStateChange(), this.getTopAlb_search_done.bind(this)); getTopAlb.search(artist); } } @@ -836,8 +836,8 @@ class PanelBio { }); if (found && $Bio.isArray(ta) && ta.length) { this.alb.list.push({ - artist: `${lg['Last.fm Top Albums: '] + artist}:`, - album: `${lg['Last.fm Top Albums: '] + artist}:`, + artist: `${bioLg['Last.fm Top Albums: '] + artist}:`, + album: `${bioLg['Last.fm Top Albums: '] + artist}:`, type: 'label' }); ta.forEach((v, i) => this.alb.list.push({ @@ -856,10 +856,10 @@ class PanelBio { }); } - if (this.alb.history.length && pptBio.showAlbumHistory) { + if (this.alb.history.length && bioSet.showAlbumHistory) { this.alb.list.push({ artist: '', - album: lg['Album History:'], + album: bioLg['Album History:'], type: 'label' }); for (let h = 0; h < this.alb.history.length; h++) { @@ -881,12 +881,12 @@ class PanelBio { let pth; switch (sw) { case 'bio': - if (panelBio.isRadio(pptBio.focus)) stnd = false; else if (stnd === '') stnd = this.stnd(this.art.ix, this.art.list); - if (server) fo = stnd ? this.cleanPth(cfg.pth[folder], focus, 'server') : this.cleanPth(cfg.remap[folder], focus, 'remap', artist, '', 1); - else fo = stnd && !this.lock ? this.cleanPth(cfg.pth[folder], focus) : this.cleanPth(cfg.remap[folder], focus, 'remap', artist, '', 1); - pth = `${fo + cleanArtist + cfg.suffix[folder]}.txt`; - if (!stnd && supCache && !$Bio.file(pth)) fo = this.cleanPth(cfg.sup[folder], focus, 'remap', artist, '', 1); - pth = `${fo + cleanArtist + cfg.suffix[folder]}.txt`; + if (bio.panel.isRadio(bioSet.focus)) stnd = false; else if (stnd === '') stnd = this.stnd(this.art.ix, this.art.list); + if (server) fo = stnd ? this.cleanPth(bioCfg.pth[folder], focus, 'server') : this.cleanPth(bioCfg.remap[folder], focus, 'remap', artist, '', 1); + else fo = stnd && !this.lock ? this.cleanPth(bioCfg.pth[folder], focus) : this.cleanPth(bioCfg.remap[folder], focus, 'remap', artist, '', 1); + pth = `${fo + cleanArtist + bioCfg.suffix[folder]}.txt`; + if (!stnd && supCache && !$Bio.file(pth)) fo = this.cleanPth(bioCfg.sup[folder], focus, 'remap', artist, '', 1); + pth = `${fo + cleanArtist + bioCfg.suffix[folder]}.txt`; return basic ? { fo, pth @@ -894,34 +894,34 @@ class PanelBio { case 'rev': if (stnd === '') stnd = this.stnd(this.alb.ix, this.alb.list); if (!stnd) cleanAlbumArtist = cleanArtist; - if (server) fo = stnd ? this.cleanPth(cfg.pth[folder], focus, 'server') : this.cleanPth(cfg.remap[folder], focus, 'remap', artist, album, 0); - else fo = stnd && !this.lock ? this.cleanPth(cfg.pth[folder], focus) : this.cleanPth(cfg.remap[folder], focus, 'remap', artist, album, 0); - pth = `${fo + cleanAlbumArtist} - ${cleanAlbum}${cfg.suffix[folder]}.txt`; - if (!stnd && supCache && !$Bio.file(pth)) fo = this.cleanPth(cfg.sup[folder], focus, 'remap', artist, album, 0); - pth = `${fo + cleanAlbumArtist} - ${cleanAlbum}${cfg.suffix[folder]}.txt`; + if (server) fo = stnd ? this.cleanPth(bioCfg.pth[folder], focus, 'server') : this.cleanPth(bioCfg.remap[folder], focus, 'remap', artist, album, 0); + else fo = stnd && !this.lock ? this.cleanPth(bioCfg.pth[folder], focus) : this.cleanPth(bioCfg.remap[folder], focus, 'remap', artist, album, 0); + pth = `${fo + cleanAlbumArtist} - ${cleanAlbum}${bioCfg.suffix[folder]}.txt`; + if (!stnd && supCache && !$Bio.file(pth)) fo = this.cleanPth(bioCfg.sup[folder], focus, 'remap', artist, album, 0); + pth = `${fo + cleanAlbumArtist} - ${cleanAlbum}${bioCfg.suffix[folder]}.txt`; if (pth.length > 259) { cleanAlbum = $Bio.abbreviate(cleanAlbum); - pth = `${fo + cleanAlbumArtist} - ${cleanAlbum}${cfg.suffix[folder]}.txt`; + pth = `${fo + cleanAlbumArtist} - ${cleanAlbum}${bioCfg.suffix[folder]}.txt`; } return basic ? { fo, pth } : [fo, pth, !!(cleanAlbumArtist && cleanAlbum), $Bio.file(pth)]; case 'track': - fo = this.cleanPth(cfg.remap[folder], focus, 'remap', artist, album, 0); - pth = `${fo + cleanArtist} - ${cleanAlbum}${cfg.suffix[folder].replace(' Review', '')}.json`; + fo = this.cleanPth(bioCfg.remap[folder], focus, 'remap', artist, album, 0); + pth = `${fo + cleanArtist} - ${cleanAlbum}${bioCfg.suffix[folder].replace(' Review', '')}.json`; return basic ? { fo, pth } : [fo, pth, !!cleanArtist, $Bio.file(pth)]; case 'cov': - fo = this.cleanPth(cfg.pth.foImgCov, focus, 'server'); - pth = fo + $Bio.clean($Bio.eval(cfg.pth.fnImgCov, focus, true)); + fo = this.cleanPth(bioCfg.pth.foImgCov, focus, 'server'); + pth = fo + $Bio.clean($Bio.eval(bioCfg.pth.fnImgCov, focus, true)); return { fo, pth }; case 'img': { - fo = this.cleanPth(cfg.remap.foImgRev, focus, 'remap', artist, album, 0); + fo = this.cleanPth(bioCfg.remap.foImgRev, focus, 'remap', artist, album, 0); let fn = $Bio.clean(`${artist} - ${album}`); pth = fo + fn; if (pth.length > 259) { @@ -937,7 +937,7 @@ class PanelBio { }; } const pe = [fo]; - if (supCache) pe.push(this.cleanPth(cfg.sup.foImgRev, focus, 'remap', artist, album, 0)); + if (supCache) pe.push(this.cleanPth(bioCfg.sup.foImgRev, focus, 'remap', artist, album, 0)); // fn long file path done above return { pe, @@ -961,10 +961,10 @@ class PanelBio { } getStyleFallback() { - pptBio.style = 4; - if (!pptBio.sameStyle) { - if (pptBio.artistView) pptBio.bioStyle = 4; - else pptBio.revStyle = 4; + bioSet.style = 4; + if (!bioSet.sameStyle) { + if (bioSet.artistView) bioSet.bioStyle = 4; + else bioSet.revStyle = 4; } fb.ShowPopupMessage('Unable to locate style. Using overlay layout instead.', 'Biography'); } @@ -978,15 +978,15 @@ class PanelBio { } getStyleNames() { - this.style.name = [lg.Top, lg.Right, lg.Bottom, lg.Left, lg['Full overlay'], lg['Part overlay']]; + this.style.name = [bioLg.Top, bioLg.Right, bioLg.Bottom, bioLg.Left, bioLg['Full overlay'], bioLg['Part overlay']]; this.style.free.forEach(v => this.style.name.push(v.name)); } imgBoxTrace(x, y) { if (this.trace.film || this.m.y == -1) return false; - if (pptBio.img_only) return true; - if (pptBio.style < 4) { - switch (pptBio.style) { + if (bioSet.img_only) return true; + if (bioSet.style < 4) { + switch (bioSet.style) { case 0: case 2: return y > this.img.t && y < this.img.t + this.style.imgSize; @@ -998,8 +998,8 @@ class PanelBio { } inactivate() { - pptBio.toggle('panelActive'); - window.NotifyOthers('bio_status', pptBio.panelActive); + bioSet.toggle('panelActive'); + window.NotifyOthers('bio_status', bioSet.panelActive); window.Reload(); } @@ -1016,19 +1016,19 @@ class PanelBio { } isTouchEvent(x, y) { - return pptBio.touchControl && Math.sqrt((Math.pow(this.id.last_pressed_coord.x - x, 2) + Math.pow(this.id.last_pressed_coord.y - y, 2))) > 3 * $Bio.scale; + return bioSet.touchControl && Math.sqrt((Math.pow(this.id.last_pressed_coord.x - x, 2) + Math.pow(this.id.last_pressed_coord.y - y, 2))) > 3 * $Bio.scale; } leave() { - if (!pptBio.autoEnlarge || menBio.right_up) return; - if (pptBio.img_only) { + if (!bioSet.autoEnlarge || bio.men.right_up) return; + if (bioSet.img_only) { this.mode(0); this.style.enlarged_img = false; } } logAlbumHistory(albumArtist, album, composition) { - if (albumArtist != lg['Artist Unknown'] && album != lg['Album Unknown']) { + if (albumArtist != bioLg['Artist Unknown'] && album != bioLg['Album Unknown']) { this.alb.history.unshift({ artist: albumArtist, album, @@ -1038,11 +1038,11 @@ class PanelBio { } this.alb.history = this.uniqAlbum(this.alb.history); if (this.alb.history.length > 20) this.alb.history.length = 20; - pptBio.albumHistory = JSON.stringify(this.alb.history); + bioSet.albumHistory = JSON.stringify(this.alb.history); } logArtistHistory(artist) { - if (artist != lg['Artist Unknown']) { + if (artist != bioLg['Artist Unknown']) { this.art.history.unshift({ name: artist, field: '', @@ -1051,41 +1051,41 @@ class PanelBio { } this.art.history = this.uniqArtist(this.art.history); if (this.art.history.length > 20) this.art.history.length = 20; - pptBio.artistHistory = JSON.stringify(this.art.history); + bioSet.artistHistory = JSON.stringify(this.art.history); } mbtn_up(x, y, menuLock, bypass) { if ((x < 0 || y < 0 || x > this.w || y > this.h) && !bypass) return; - if (this.id.lookUp && (butBio.btns.lookUp.trace(x, y) || menuLock || bypass)) { + if (this.id.lookUp && (bio.but.btns.lookUp.trace(x, y) || menuLock || bypass)) { if (this.id.lyricsSource || this.id.nowplayingSource) { this.lock = 0; return; } - const mArtist = pptBio.artistView && this.art.ix; - if (!this.lock && !mArtist) imgBio.artistReset(); + const mArtist = bioSet.artistView && this.art.ix; + if (!this.lock && !mArtist) bio.img.artistReset(); if (!this.lock) { this.id.lockArt = $Bio.eval(this.art.fields, this.id.focus); - this.id.lockAlb = name.albID(this.id.focus, 'full') + (this.style.inclTrackRev ? name.trackID(this.id.focus) : ''); + this.id.lockAlb = bio.name.albID(this.id.focus, 'full') + (this.style.inclTrackRev ? bio.name.trackID(this.id.focus) : ''); this.lockHandle = $Bio.handle(this.id.focus); - imgBio.setAlbID(); - imgBio.cov.folder = this.cleanPth(cfg.albCovFolder, this.id.focus); + bio.img.setAlbID(); + bio.img.cov.folder = this.cleanPth(bioCfg.albCovFolder, this.id.focus); } if (!bypass) this.lock = this.lock == 0 || menuLock ? 1 : 0; - txt.curHeadingID = this.lock ? txt.headingID() : ''; - if (!this.lock && (pptBio.artistView && this.id.lockArt != $Bio.eval(this.art.fields, this.id.focus) || !pptBio.artistView && this.id.lockAlb != name.albID(this.id.focus, 'full') + (this.style.inclTrackRev ? name.trackID(this.id.focus) : ''))) { - txt.on_playback_new_track(true); - imgBio.on_playback_new_track(true); + bio.txt.curHeadingID = this.lock ? bio.txt.headingID() : ''; + if (!this.lock && (bioSet.artistView && this.id.lockArt != $Bio.eval(this.art.fields, this.id.focus) || !bioSet.artistView && this.id.lockAlb != bio.name.albID(this.id.focus, 'full') + (this.style.inclTrackRev ? bio.name.trackID(this.id.focus) : ''))) { + bio.txt.on_playback_new_track(true); + bio.img.on_playback_new_track(true); } - butBio.check(); + bio.but.check(); window.Repaint(); return; } switch (true) { - case ((pptBio.img_only || pptBio.text_only) && !this.trace.film): + case ((bioSet.img_only || bioSet.text_only) && !this.trace.film): this.mode(0); break; case this.trace.image: - this.mode(!pptBio.img_only ? 1 : 2); + this.mode(!bioSet.img_only ? 1 : 2); break; case this.trace.text: this.mode(2); @@ -1095,70 +1095,70 @@ class PanelBio { } mode(n) { - if (!pptBio.sameStyle) pptBio.artistView ? pptBio.bioMode = n : pptBio.revMode = n; + if (!bioSet.sameStyle) bioSet.artistView ? bioSet.bioMode = n : bioSet.revMode = n; let calcText = true; this.calc = true; - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); switch (n) { case 0: { - calcText = this.calcText || pptBio.text_only; - pptBio.img_only = false; - pptBio.text_only = false; + calcText = this.calcText || bioSet.text_only; + bioSet.img_only = false; + bioSet.text_only = false; this.setStyle(); - imgBio.clearCache(); - if (!this.art.ix && pptBio.artistView && !txt.bio.lookUp || !this.alb.ix && !pptBio.artistView && !txt.rev.lookUp) { - txt.albumReset(); - txt.artistReset(); - txt.getText(calcText); - imgBio.getImages(); + bio.img.clearCache(); + if (!this.art.ix && bioSet.artistView && !bio.txt.bio.lookUp || !this.alb.ix && !bioSet.artistView && !bio.txt.rev.lookUp) { + bio.txt.albumReset(); + bio.txt.artistReset(); + bio.txt.getText(calcText); + bio.img.getImages(); } else { - txt.getItem(calcText, this.art.ix, this.alb.ix); - imgBio.getItem(this.art.ix, this.alb.ix); + bio.txt.getItem(calcText, this.art.ix, this.alb.ix); + bio.img.getItem(this.art.ix, this.alb.ix); } this.calcText = false; break; } case 1: - pptBio.img_only = true; - pptBio.text_only = false; - imgBio.setCrop(); + bioSet.img_only = true; + bioSet.text_only = false; + bio.img.setCrop(); this.setStyle(); - imgBio.clearCache(); - imgBio.getImages(); + bio.img.clearCache(); + bio.img.getImages(); break; case 2: - pptBio.img_only = false; - pptBio.text_only = true; + bioSet.img_only = false; + bioSet.text_only = true; this.setStyle(); - if (uiBio.style.isBlur) imgBio.clearCache(); - if (!pptBio.sameStyle && (pptBio.bioMode != pptBio.revMode || pptBio.bioStyle != pptBio.revStyle)) calcText = true; - if (!this.art.ix && pptBio.artistView && !txt.bio.lookUp || !this.alb.ix && !pptBio.artistView && !txt.rev.lookUp) { - txt.albumReset(); - txt.artistReset(); - txt.getText(calcText); - if (uiBio.style.isBlur) imgBio.getImages(); + if (bio.ui.style.isBlur) bio.img.clearCache(); + if (!bioSet.sameStyle && (bioSet.bioMode != bioSet.revMode || bioSet.bioStyle != bioSet.revStyle)) calcText = true; + if (!this.art.ix && bioSet.artistView && !bio.txt.bio.lookUp || !this.alb.ix && !bioSet.artistView && !bio.txt.rev.lookUp) { + bio.txt.albumReset(); + bio.txt.artistReset(); + bio.txt.getText(calcText); + if (bio.ui.style.isBlur) bio.img.getImages(); } else { - txt.getItem(calcText, this.art.ix, this.alb.ix); - if (uiBio.style.isBlur) imgBio.getItem(this.art.ix, this.alb.ix); - imgBio.setCheckArr(null); + bio.txt.getItem(calcText, this.art.ix, this.alb.ix); + if (bio.ui.style.isBlur) bio.img.getItem(this.art.ix, this.alb.ix); + bio.img.setCheckArr(null); } this.calcText = true; break; } - if (pptBio.text_only) seeker.upd(true); - if (pptBio.filmStripOverlay && pptBio.showFilmStrip) filmStrip.set(pptBio.filmStripPos); - butBio.refresh(true); + if (bioSet.text_only) bio.seeker.upd(true); + if (bioSet.filmStripOverlay && bioSet.showFilmStrip) bio.filmStrip.set(bioSet.filmStripPos); + bio.but.refresh(true); } move(x, y, click) { this.trace.film = false; this.trace.text = false; this.trace.image = false; - if (filmStrip.trace(x, y)) this.trace.film = true; - else if (pptBio.text_only) this.trace.text = true; - else if (pptBio.img_only) this.trace.text = false; - else if (pptBio.style < 4) { - switch (pptBio.style) { + if (bio.filmStrip.trace(x, y)) this.trace.film = true; + else if (bioSet.text_only) this.trace.text = true; + else if (bioSet.img_only) this.trace.text = false; + else if (bioSet.style < 4) { + switch (bioSet.style) { case 0: this.trace.text = y > this.img.t + this.style.imgSize; break; @@ -1173,11 +1173,11 @@ class PanelBio { break; } } else this.trace.text = y > this.tbox.t && y < this.tbox.t + this.tbox.h && x > this.tbox.l && x < this.tbox.l + this.tbox.w; - if (!this.trace.text && !this.trace.film) this.trace.image = imgBio.trace(x, y); - if (!pptBio.autoEnlarge || click || this.zoom() || seeker.dn) return; + if (!this.trace.text && !this.trace.film) this.trace.image = bio.img.trace(x, y); + if (!bioSet.autoEnlarge || click || this.zoom() || bio.seeker.dn) return; const enlarged_img_o = this.style.enlarged_img; this.style.enlarged_img = !this.trace.text && this.trace.image; - if (this.style.enlarged_img && !pptBio.text_only && !pptBio.img_only && !enlarged_img_o) this.mode(1); + if (this.style.enlarged_img && !bioSet.text_only && !bioSet.img_only && !enlarged_img_o) this.mode(1); } on_notify(info) { @@ -1187,7 +1187,7 @@ class PanelBio { }); this.style.free.push(rec); this.sort(this.style.free, 'name'); - pptBio.styleFree = JSON.stringify(this.style.free); + bioSet.styleFree = JSON.stringify(this.style.free); this.getStyleNames(); } @@ -1204,10 +1204,10 @@ class PanelBio { }); this.style.free[n - 6].name = input; this.sort(this.style.free, 'name'); - pptBio.styleFree = JSON.stringify(this.style.free); + bioSet.styleFree = JSON.stringify(this.style.free); this.style.free.some((v, i) => { if (v.name == input) { - pptBio.style = i + 5; + bioSet.style = i + 5; return true; } }); @@ -1217,7 +1217,7 @@ class PanelBio { }; const caption = 'Rename Current Style'; const prompt = `Rename style: ${this.style.name[n]}\n\nEnter new name\n\nContinue?`; - const fallback = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.input(caption, prompt, ok_callback, '', this.style.name[n]) : true; + const fallback = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.input(caption, prompt, ok_callback, '', this.style.name[n]) : true; if (fallback) { let ns = ''; let status = 'ok'; @@ -1234,7 +1234,7 @@ class PanelBio { this.alb.ix = 0; this.lock = 0; this.alb.history = []; - pptBio.albumHistory = JSON.stringify([]); + bioSet.albumHistory = JSON.stringify([]); this.alb.cur = ''; this.getList(true, true); } @@ -1243,7 +1243,7 @@ class PanelBio { this.art.ix = 0; this.lock = 0; this.art.history = []; - pptBio.artistHistory = JSON.stringify([]); + bioSet.artistHistory = JSON.stringify([]); this.art.cur = ''; this.getList(true); } @@ -1251,9 +1251,9 @@ class PanelBio { resetStyle(n) { const continue_confirmation = (status, confirmed) => { if (confirmed) { - if (pptBio.style < 4) pptBio.rel_imgs = 0.65; + if (bioSet.style < 4) bioSet.rel_imgs = 0.65; else { - const obj = pptBio.style == 4 || pptBio.style == 5 ? this.style.overlay : this.style.free[pptBio.style - 6]; + const obj = bioSet.style == 4 || bioSet.style == 5 ? this.style.overlay : this.style.free[bioSet.style - 6]; obj.name = this.style.name[n]; obj.imL = 0; obj.imR = 0; @@ -1263,19 +1263,19 @@ class PanelBio { obj.txR = 0; obj.txT = 0.632; obj.txB = 0; - pptBio.style == 4 || pptBio.style == 5 ? pptBio.styleOverlay = JSON.stringify(this.style.overlay) : pptBio.styleFree = JSON.stringify(this.style.free); + bioSet.style == 4 || bioSet.style == 5 ? bioSet.styleOverlay = JSON.stringify(this.style.overlay) : bioSet.styleFree = JSON.stringify(this.style.free); } - txt.refresh(3); + bio.txt.refresh(3); } }; const caption = 'Reset Current Style'; - const prompt = `Reset to Default ${pptBio.style < 5 ? this.style.name[n] : 'Overlay'} Style.\n\nContinue?`; - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const prompt = `Reset to Default ${bioSet.style < 5 ? this.style.name[n] : 'Overlay'} Style.\n\nContinue?`; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); } sameStyle() { - return pptBio.sameStyle || (pptBio.bioMode == pptBio.revMode && pptBio.bioStyle == pptBio.revStyle); + return bioSet.sameStyle || (bioSet.bioMode == bioSet.revMode && bioSet.bioStyle == bioSet.revStyle); } setBorder(imgFull, bor, refl) { @@ -1283,84 +1283,84 @@ class PanelBio { const value = bor > 1 && !refl ? 10 * $Bio.scale : 0; $Bio.key.forEach(v => this.bor[v] = value); } else { - $Bio.key.forEach(v => this.bor[v] = bor < 2 || refl ? pptBio[`bor${v.toUpperCase()}`] : Math.max(pptBio[`bor${v.toUpperCase()}`], 10 * $Bio.scale)); - this.style.gap = bor < 2 || refl ? pptBio.gap : Math.max(pptBio.gap, 10 * $Bio.scale); + $Bio.key.forEach(v => this.bor[v] = bor < 2 || refl ? bioSet[`bor${v.toUpperCase()}`] : Math.max(bioSet[`bor${v.toUpperCase()}`], 10 * $Bio.scale)); + this.style.gap = bor < 2 || refl ? bioSet.gap : Math.max(bioSet.gap, 10 * $Bio.scale); } } setStyle(bypass) { - this.sbar.offset = [2 + uiBio.sbar.arrowPad, Math.max(Math.floor(uiBio.sbar.but_w * 0.2), 2) + uiBio.sbar.arrowPad * 2, 0][uiBio.sbar.type]; - this.sbar.top_corr = [this.sbar.offset - (uiBio.sbar.but_h - uiBio.sbar.but_w) / 2, this.sbar.offset, 0][uiBio.sbar.type]; - const bot_corr = [(uiBio.sbar.but_h - uiBio.sbar.but_w) / 2 - this.sbar.offset, -this.sbar.offset, 0][uiBio.sbar.type]; + this.sbar.offset = [2 + bio.ui.sbar.arrowPad, Math.max(Math.floor(bio.ui.sbar.but_w * 0.2), 2) + bio.ui.sbar.arrowPad * 2, 0][bio.ui.sbar.type]; + this.sbar.top_corr = [this.sbar.offset - (bio.ui.sbar.but_h - bio.ui.sbar.but_w) / 2, this.sbar.offset, 0][bio.ui.sbar.type]; + const bot_corr = [(bio.ui.sbar.but_h - bio.ui.sbar.but_w) / 2 - this.sbar.offset, -this.sbar.offset, 0][bio.ui.sbar.type]; this.clip = false; - if (!pptBio.sameStyle) { + if (!bioSet.sameStyle) { switch (true) { - case pptBio.artistView: - if (pptBio.bioMode === 1) { - pptBio.img_only = true; - pptBio.text_only = false; - } else if (pptBio.bioMode === 2) { - pptBio.img_only = false; - pptBio.text_only = true; + case bioSet.artistView: + if (bioSet.bioMode === 1) { + bioSet.img_only = true; + bioSet.text_only = false; + } else if (bioSet.bioMode === 2) { + bioSet.img_only = false; + bioSet.text_only = true; } else { - pptBio.img_only = false; - pptBio.text_only = false; - pptBio.style = pptBio.bioStyle; + bioSet.img_only = false; + bioSet.text_only = false; + bioSet.style = bioSet.bioStyle; } break; - case !pptBio.artistView: - if (pptBio.revMode === 1) { - pptBio.img_only = true; - pptBio.text_only = false; - } else if (pptBio.revMode === 2) { - pptBio.img_only = false; - pptBio.text_only = true; + case !bioSet.artistView: + if (bioSet.revMode === 1) { + bioSet.img_only = true; + bioSet.text_only = false; + } else if (bioSet.revMode === 2) { + bioSet.img_only = false; + bioSet.text_only = true; } else { - pptBio.img_only = false; - pptBio.text_only = false; - pptBio.style = pptBio.revStyle; + bioSet.img_only = false; + bioSet.text_only = false; + bioSet.style = bioSet.revStyle; } break; } - if (pptBio.text_only) seeker.upd(true); + if (bioSet.text_only) bio.seeker.upd(true); } const sp1 = SCALE(10 * $Bio.scale); - const sp2 = sp1 + (this.filmStripSize.r && !pptBio.filmStripOverlay ? ((SCALE(pref.layout === 'artwork' ? 12 : 9) * $Bio.scale)) : 0); - const filmStripRight = pptBio.artistView && pptBio.showFilmStrip && pptBio.filmStripPos === 1; - const filmStripLeft = pptBio.artistView && pptBio.showFilmStrip && pptBio.filmStripPos === 3; - const RES_4K_Corr = RES_4K ? uiBio.heading.linePad * 0.5 : 0; - const biographyFontSize = pptBio[`baseFontSizeBio_${pref.layout}`]; + const sp2 = sp1 + (this.filmStripSize.r && !bioSet.filmStripOverlay ? ((SCALE(grSet.layout === 'artwork' ? 12 : 9) * $Bio.scale)) : 0); + const filmStripRight = bioSet.artistView && bioSet.showFilmStrip && bioSet.filmStripPos === 1; + const filmStripLeft = bioSet.artistView && bioSet.showFilmStrip && bioSet.filmStripPos === 3; + const RES_4K_Corr = RES._4K ? bio.ui.heading.linePad * 0.5 : 0; + const biographyFontSize = bioSet[`baseFontSizeBio_${grSet.layout}`]; switch (true) { - case pptBio.img_only: { // img_only + case bioSet.img_only: { // img_only $Bio.key.forEach(v => this.img[v] = this.bor[v]); - const autoFill = pptBio.artistView && pptBio.artStyleImgOnly == 1 || !pptBio.artistView && pptBio.covStyleImgOnly == 1; - if (!autoFill && !pptBio.filmStripOverlay) { - const v = $Bio.key[pptBio.filmStripPos]; + const autoFill = bioSet.artistView && bioSet.artStyleImgOnly == 1 || !bioSet.artistView && bioSet.covStyleImgOnly == 1; + if (!autoFill && !bioSet.filmStripOverlay) { + const v = $Bio.key[bioSet.filmStripPos]; this.img[v] += this.filmStripSize[v]; this.style.imgSize = $Bio.clamp(this.h - this.img.t - this.img.b, 10, this.w - this.img.l - this.img.r); } else this.style.imgSize = $Bio.clamp(this.h - this.bor.t - this.bor.b, 10, this.w - this.bor.l - this.bor.r); break; } - case pptBio.text_only: { // text_only - const textWidthCorr = filmStripRight ? pptBio.filmStripOverlay ? this.text.r + this.filmStripSize.r * 0.5 - this.style.gap : this.text.r : this.text.r * 2; + case bioSet.text_only: { // text_only + const textWidthCorr = filmStripRight ? bioSet.filmStripOverlay ? this.text.r + this.filmStripSize.r * 0.5 - this.style.gap : this.text.r : this.text.r * 2; const textWidthCorr2 = filmStripLeft ? this.text.r - this.style.gap - RES_4K_Corr : 0; - const sbarScrollCorr = filmStripRight ? pptBio.filmStripOverlay ? this.text.r - (this.filmStripSize.r + this.style.gap) : 0 : 0; + const sbarScrollCorr = filmStripRight ? bioSet.filmStripOverlay ? this.text.r - (this.filmStripSize.r + this.style.gap) : 0 : 0; - this.lines_drawn = Math.max(Math.floor((this.h - pptBio.textT - pptBio.textB - uiBio.heading.h - this.filmStripSize.t - this.filmStripSize.b) / uiBio.font.main_h), 0); - this.text.l = pptBio.textL + this.filmStripSize.l - textWidthCorr2; - this.text.r = (pptBio.sbarShow ? Math.max(pptBio.textR, uiBio.sbar.sp + sp2) : pptBio.textR) + this.filmStripSize.r; - /** MOD */ this.text.t = !pptBio.topAlign ? uiBio.y + pptBio.textT + (this.h - pptBio.textT + this.filmStripSize.t - pptBio.textB - this.filmStripSize.b - this.lines_drawn * uiBio.font.main_h + uiBio.heading.h) / 2 : pptBio.textT + uiBio.heading.h + this.filmStripSize.t; + this.lines_drawn = Math.max(Math.floor((this.h - bioSet.textT - bioSet.textB - bio.ui.heading.h - this.filmStripSize.t - this.filmStripSize.b) / bio.ui.font.main_h), 0); + this.text.l = bioSet.textL + this.filmStripSize.l - textWidthCorr2; + this.text.r = (bioSet.sbarShow ? Math.max(bioSet.textR, bio.ui.sbar.sp + sp2) : bioSet.textR) + this.filmStripSize.r; + /** MOD */ this.text.t = !bioSet.topAlign ? bio.ui.y + bioSet.textT + (this.h - bioSet.textT + this.filmStripSize.t - bioSet.textB - this.filmStripSize.b - this.lines_drawn * bio.ui.font.main_h + bio.ui.heading.h) / 2 : bioSet.textT + bio.ui.heading.h + this.filmStripSize.t; /** MOD */ this.text.w = this.w - this.text.l - textWidthCorr; - this.text.h = this.lines_drawn * uiBio.font.main_h; - this.heading.x = !this.style.fullWidthHeading ? this.text.l : pptBio.textL; - this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - pptBio.textR; - if (pptBio.sbarShow) { - /** MOD */ this.sbar.x = (!this.filmStripSize.r || pptBio.filmStripOverlay ? this.w - uiBio.sbar.sp - this.text.r : this.text.l + this.text.w + sp1) + sbarScrollCorr; - this.sbar.y = (uiBio.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.r || this.filmStripSize.b ? this.text.t : 0) + this.sbar.top_corr; - this.sbar.h = (uiBio.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.r || this.filmStripSize.b ? uiBio.font.main_h * this.lines_drawn + bot_corr : this.h - this.sbar.y) + bot_corr; + this.text.h = this.lines_drawn * bio.ui.font.main_h; + this.heading.x = !this.style.fullWidthHeading ? this.text.l : bioSet.textL; + this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - bioSet.textR; + if (bioSet.sbarShow) { + /** MOD */ this.sbar.x = (!this.filmStripSize.r || bioSet.filmStripOverlay ? this.w - bio.ui.sbar.sp - this.text.r : this.text.l + this.text.w + sp1) + sbarScrollCorr; + this.sbar.y = (bio.ui.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.r || this.filmStripSize.b ? this.text.t : 0) + this.sbar.top_corr; + this.sbar.h = (bio.ui.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.r || this.filmStripSize.b ? bio.ui.font.main_h * this.lines_drawn + bot_corr : this.h - this.sbar.y) + bot_corr; } this.repaint.x = this.text.l; this.repaint.y = 0; @@ -1369,125 +1369,125 @@ class PanelBio { break; } - case pptBio.style === 0: { // top - const textWidthCorr = filmStripRight && !pptBio.filmStripOverlay ? this.text.r + RES_4K_Corr : this.text.r * 2; - const textWidthCorr2 = filmStripLeft && !pptBio.filmStripOverlay ? this.filmStripSize.l - this.text.r + this.style.gap + RES_4K_Corr : 0; + case bioSet.style === 0: { // top + const textWidthCorr = filmStripRight && !bioSet.filmStripOverlay ? this.text.r + RES_4K_Corr : this.text.r * 2; + const textWidthCorr2 = filmStripLeft && !bioSet.filmStripOverlay ? this.filmStripSize.l - this.text.r + this.style.gap + RES_4K_Corr : 0; - $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 'b' ? (!pptBio.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); - /** MOD */ let txt_h = Math.round((this.h - this.img.t - pptBio.textB - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0)) * (1 - pptBio.rel_imgs) + biographyFontSize); - this.lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); - this.text.h = this.lines_drawn * uiBio.font.main_h + biographyFontSize; + $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 'b' ? (!bioSet.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); + /** MOD */ let txt_h = Math.round((this.h - this.img.t - bioSet.textB - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0)) * (1 - bioSet.rel_imgs) + biographyFontSize); + this.lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); + this.text.h = this.lines_drawn * bio.ui.font.main_h + biographyFontSize; txt_h = this.text.h + this.style.gap; - /** MOD */ this.style.imgSize = Math.max(this.h - txt_h - this.img.t - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) - pptBio.textB - uiBio.heading.h * 0.75, 10); - /** MOD */ this.text.l = pptBio.textL + textWidthCorr2; - this.text.r = (pptBio.sbarShow ? Math.max(pptBio.textR, uiBio.sbar.sp + sp2) : pptBio.textR) + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0); - /** MOD */ this.text.t = this.img.t + this.style.imgSize + this.style.gap + uiBio.heading.h * 2; + /** MOD */ this.style.imgSize = Math.max(this.h - txt_h - this.img.t - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) - bioSet.textB - bio.ui.heading.h * 0.75, 10); + /** MOD */ this.text.l = bioSet.textL + textWidthCorr2; + this.text.r = (bioSet.sbarShow ? Math.max(bioSet.textR, bio.ui.sbar.sp + sp2) : bioSet.textR) + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0); + /** MOD */ this.text.t = this.img.t + this.style.imgSize + this.style.gap + bio.ui.heading.h * 2; /** MOD */ this.text.w = this.w - this.text.l - textWidthCorr; - this.heading.x = (!this.style.fullWidthHeading ? this.text.l : pptBio.textL); - this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - pptBio.textL - pptBio.textR; - /** MOD */ this.sbar.x = (!this.filmStripSize.r || pptBio.filmStripOverlay ? this.w - uiBio.sbar.sp - this.text.r : this.text.l + this.text.w + sp1); - this.sbar.y = (uiBio.sbar.type < this.sbar.style || pptBio.heading || this.filmStripSize.b ? this.text.t : this.img.t + this.style.imgSize) + this.sbar.top_corr; - this.sbar.h = (uiBio.sbar.type < this.sbar.style || this.filmStripSize.b ? uiBio.font.main_h * this.lines_drawn + bot_corr : this.h - this.sbar.y) + bot_corr; + this.heading.x = (!this.style.fullWidthHeading ? this.text.l : bioSet.textL); + this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - bioSet.textL - bioSet.textR; + /** MOD */ this.sbar.x = (!this.filmStripSize.r || bioSet.filmStripOverlay ? this.w - bio.ui.sbar.sp - this.text.r : this.text.l + this.text.w + sp1); + this.sbar.y = (bio.ui.sbar.type < this.sbar.style || bioSet.heading || this.filmStripSize.b ? this.text.t : this.img.t + this.style.imgSize) + this.sbar.top_corr; + this.sbar.h = (bio.ui.sbar.type < this.sbar.style || this.filmStripSize.b ? bio.ui.font.main_h * this.lines_drawn + bot_corr : this.h - this.sbar.y) + bot_corr; this.repaint.x = this.text.l; this.repaint.y = this.text.t; - /** MOD */ this.repaint.w = this.w - this.repaint.x - (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0) - (pptBio.filmStripPos === 1 ? 0 : this.text.r); - /** MOD */ this.repaint.h = this.h - this.repaint.y - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) + uiBio.y; + /** MOD */ this.repaint.w = this.w - this.repaint.x - (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0) - (bioSet.filmStripPos === 1 ? 0 : this.text.r); + /** MOD */ this.repaint.h = this.h - this.repaint.y - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) + bio.ui.y; break; } - case pptBio.style === 1: { // right - const textWidthCorr1 = filmStripLeft && !pptBio.filmStripOverlay ? this.style.gap + SCALE(uiBio.heading.linePad) - SCALE(pref.layout === 'artwork' ? 10 : 0) : 0; - const textWidthCorr2 = filmStripRight && !pptBio.filmStripOverlay ? this.style.gap + SCALE(uiBio.heading.linePad) - SCALE(pref.layout === 'artwork' ? 10 : 0) : 0; - - $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 'l' ? (!pptBio.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); - let txt_sp = Math.round((this.w - pptBio.textL - (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0) - this.img.r) * (1 - pptBio.rel_imgs)); - const txt_h = this.h - pptBio.textT - pptBio.textB - (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0) - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0); - this.lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); - this.style.imgSize = Math.max(this.w - txt_sp - this.img.r - pptBio.textL - (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0) - this.style.gap, 10); - if (pptBio.sbarShow) txt_sp -= (uiBio.sbar.sp + sp1); - /** MOD */ this.text.l = pptBio.textL + (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0) - textWidthCorr1; - this.text.r = pptBio.sbarShow ? Math.max(pptBio.textR + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0), uiBio.sbar.sp + sp1) : pptBio.textR + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0); - /** MOD */ this.text.t = !pptBio.topAlign ? uiBio.y + pptBio.textT + (this.h - pptBio.textT - pptBio.textB + (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0) - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) - this.lines_drawn * uiBio.font.main_h + uiBio.heading.h) / 2 : pptBio.textT + uiBio.heading.h + (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0); + case bioSet.style === 1: { // right + const textWidthCorr1 = filmStripLeft && !bioSet.filmStripOverlay ? this.style.gap + SCALE(bio.ui.heading.linePad) - SCALE(grSet.layout === 'artwork' ? 10 : 0) : 0; + const textWidthCorr2 = filmStripRight && !bioSet.filmStripOverlay ? this.style.gap + SCALE(bio.ui.heading.linePad) - SCALE(grSet.layout === 'artwork' ? 10 : 0) : 0; + + $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 'l' ? (!bioSet.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); + let txt_sp = Math.round((this.w - bioSet.textL - (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0) - this.img.r) * (1 - bioSet.rel_imgs)); + const txt_h = this.h - bioSet.textT - bioSet.textB - (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0) - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0); + this.lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); + this.style.imgSize = Math.max(this.w - txt_sp - this.img.r - bioSet.textL - (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0) - this.style.gap, 10); + if (bioSet.sbarShow) txt_sp -= (bio.ui.sbar.sp + sp1); + /** MOD */ this.text.l = bioSet.textL + (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0) - textWidthCorr1; + this.text.r = bioSet.sbarShow ? Math.max(bioSet.textR + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0), bio.ui.sbar.sp + sp1) : bioSet.textR + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0); + /** MOD */ this.text.t = !bioSet.topAlign ? bio.ui.y + bioSet.textT + (this.h - bioSet.textT - bioSet.textB + (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0) - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) - this.lines_drawn * bio.ui.font.main_h + bio.ui.heading.h) / 2 : bioSet.textT + bio.ui.heading.h + (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0); /** MOD */ this.text.w = txt_sp + textWidthCorr1 + textWidthCorr2; - this.text.h = this.lines_drawn * uiBio.font.main_h; - this.heading.x = !this.style.fullWidthHeading ? this.text.l : pptBio.textL; + this.text.h = this.lines_drawn * bio.ui.font.main_h; + this.heading.x = !this.style.fullWidthHeading ? this.text.l : bioSet.textL; this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - this.bor.r; - /** MOD */ if (this.style.fullWidthHeading) this.img.t = this.text.t - uiBio.y + (RES_4K ? uiBio.heading.linePad * 1.5 : uiBio.heading.linePad * 0.5); - /** MOD */ this.img.l = pptBio.textL + txt_sp + (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0) + this.style.gap + (pptBio.sbarShow ? uiBio.sbar.sp + sp1 : 0) + textWidthCorr2; + /** MOD */ if (this.style.fullWidthHeading) this.img.t = this.text.t - bio.ui.y + (RES._4K ? bio.ui.heading.linePad * 1.5 : bio.ui.heading.linePad * 0.5); + /** MOD */ this.img.l = bioSet.textL + txt_sp + (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0) + this.style.gap + (bioSet.sbarShow ? bio.ui.sbar.sp + sp1 : 0) + textWidthCorr2; /** MOD */ this.sbar.x = this.text.l + this.text.w + sp1 - RES_4K_Corr; - this.sbar.y = (uiBio.sbar.type < this.sbar.style || pptBio.heading || this.filmStripSize.t || this.filmStripSize.b ? this.text.t : 0) + this.sbar.top_corr; - this.sbar.h = uiBio.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.b ? uiBio.font.main_h * this.lines_drawn + bot_corr * 2 : this.h - this.sbar.y + bot_corr; + this.sbar.y = (bio.ui.sbar.type < this.sbar.style || bioSet.heading || this.filmStripSize.t || this.filmStripSize.b ? this.text.t : 0) + this.sbar.top_corr; + this.sbar.h = bio.ui.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.b ? bio.ui.font.main_h * this.lines_drawn + bot_corr * 2 : this.h - this.sbar.y + bot_corr; this.repaint.x = this.text.l; this.repaint.y = this.text.t; this.repaint.w = this.img.l - this.repaint.x - this.style.gap; - /** MOD */ this.repaint.h = this.h - this.repaint.y - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) + uiBio.y; + /** MOD */ this.repaint.h = this.h - this.repaint.y - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) + bio.ui.y; break; } - case pptBio.style === 2: { // bottom - const textWidthCorr = filmStripRight && !pptBio.filmStripOverlay ? this.text.r + RES_4K_Corr : this.text.r * 2; - const textWidthCorr2 = filmStripLeft && !pptBio.filmStripOverlay ? this.filmStripSize.l - this.text.r + this.style.gap + RES_4K_Corr : 0; + case bioSet.style === 2: { // bottom + const textWidthCorr = filmStripRight && !bioSet.filmStripOverlay ? this.text.r + RES_4K_Corr : this.text.r * 2; + const textWidthCorr2 = filmStripLeft && !bioSet.filmStripOverlay ? this.filmStripSize.l - this.text.r + this.style.gap + RES_4K_Corr : 0; - $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 't' && v != 'b' ? (!pptBio.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); - /** MOD */ let txt_h = Math.round((this.h - pptBio.textT - this.img.b - (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0) - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0)) * (1 - pptBio.rel_imgs) + biographyFontSize); - this.lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); - this.text.h = this.lines_drawn * uiBio.font.main_h; + $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 't' && v != 'b' ? (!bioSet.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); + /** MOD */ let txt_h = Math.round((this.h - bioSet.textT - this.img.b - (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0) - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0)) * (1 - bioSet.rel_imgs) + biographyFontSize); + this.lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); + this.text.h = this.lines_drawn * bio.ui.font.main_h; txt_h = this.text.h + this.style.gap; - this.style.imgSize = Math.max(this.h - txt_h - pptBio.textT - this.img.b - (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0) - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) - uiBio.heading.h, 10); - this.img.t = this.h - this.bor.b - this.style.imgSize - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0); + this.style.imgSize = Math.max(this.h - txt_h - bioSet.textT - this.img.b - (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0) - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) - bio.ui.heading.h, 10); + this.img.t = this.h - this.bor.b - this.style.imgSize - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0); /** MOD */ this.img.l = this.text.l; - /** MOD */ this.text.l = pptBio.textL + textWidthCorr2; - this.text.r = (pptBio.sbarShow ? Math.max(pptBio.textR, uiBio.sbar.sp + sp2) : pptBio.textR) + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0); - /** MOD */ this.text.t = uiBio.y + this.img.t - txt_h; + /** MOD */ this.text.l = bioSet.textL + textWidthCorr2; + this.text.r = (bioSet.sbarShow ? Math.max(bioSet.textR, bio.ui.sbar.sp + sp2) : bioSet.textR) + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0); + /** MOD */ this.text.t = bio.ui.y + this.img.t - txt_h; /** MOD */ this.text.w = this.w - this.text.l - textWidthCorr; - this.heading.x = (!this.style.fullWidthHeading ? this.text.l : pptBio.textL); - this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - pptBio.textL - pptBio.textR; - /** MOD */ this.sbar.x = (!this.filmStripSize.r || pptBio.filmStripOverlay ? this.w - uiBio.sbar.sp - this.text.r : this.text.l + this.text.w + sp1); - this.sbar.y = (uiBio.sbar.type < this.sbar.style || pptBio.heading ? this.text.t : 0) + this.sbar.top_corr; - this.sbar.h = uiBio.sbar.type < this.sbar.style ? uiBio.font.main_h * this.lines_drawn + bot_corr * 2 : this.img.t - this.sbar.y + bot_corr; + this.heading.x = (!this.style.fullWidthHeading ? this.text.l : bioSet.textL); + this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - bioSet.textL - bioSet.textR; + /** MOD */ this.sbar.x = (!this.filmStripSize.r || bioSet.filmStripOverlay ? this.w - bio.ui.sbar.sp - this.text.r : this.text.l + this.text.w + sp1); + this.sbar.y = (bio.ui.sbar.type < this.sbar.style || bioSet.heading ? this.text.t : 0) + this.sbar.top_corr; + this.sbar.h = bio.ui.sbar.type < this.sbar.style ? bio.ui.font.main_h * this.lines_drawn + bot_corr * 2 : this.img.t - this.sbar.y + bot_corr; this.repaint.x = this.text.l; this.repaint.y = this.text.t; - /** MOD */ this.repaint.w = this.w - this.repaint.x - (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0) - (pptBio.filmStripPos === 1 ? 0 : this.text.r); - /** MOD */ this.repaint.h = this.img.t - this.repaint.y + uiBio.y; + /** MOD */ this.repaint.w = this.w - this.repaint.x - (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0) - (bioSet.filmStripPos === 1 ? 0 : this.text.r); + /** MOD */ this.repaint.h = this.img.t - this.repaint.y + bio.ui.y; break; } - case pptBio.style === 3: { // left - const textWidthCorr1 = filmStripLeft && !pptBio.filmStripOverlay ? this.style.gap - (pref.layout === 'artwork' ? RES_4K_Corr * 2 : RES_4K ? -uiBio.heading.linePad * 1.5 : -uiBio.heading.linePad) : -RES_4K_Corr; - const textWidthCorr2 = filmStripLeft && !pptBio.filmStripOverlay ? this.text.r - this.style.gap - RES_4K_Corr : 0; - const textWidthCorr3 = pptBio.artistView && pptBio.showFilmStrip && !pptBio.filmStripOverlay ? pptBio.filmStripPos === 1 ? RES_4K_Corr * 2 : pptBio.filmStripPos === 3 ? this.style.gap : this.text.r : this.text.r; + case bioSet.style === 3: { // left + const textWidthCorr1 = filmStripLeft && !bioSet.filmStripOverlay ? this.style.gap - (grSet.layout === 'artwork' ? RES_4K_Corr * 2 : RES._4K ? -bio.ui.heading.linePad * 1.5 : -bio.ui.heading.linePad) : -RES_4K_Corr; + const textWidthCorr2 = filmStripLeft && !bioSet.filmStripOverlay ? this.text.r - this.style.gap - RES_4K_Corr : 0; + const textWidthCorr3 = bioSet.artistView && bioSet.showFilmStrip && !bioSet.filmStripOverlay ? bioSet.filmStripPos === 1 ? RES_4K_Corr * 2 : bioSet.filmStripPos === 3 ? this.style.gap : this.text.r : this.text.r; - $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 'r' ? (!pptBio.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); - this.text.r = (pptBio.sbarShow ? Math.max(pptBio.textR, uiBio.sbar.sp + sp2) : pptBio.textR) + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0); - const txt_sp = Math.round((this.w - this.img.l - this.text.r) * (1 - pptBio.rel_imgs)); - const txt_h = this.h - pptBio.textT - pptBio.textB - (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0) - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0); - this.lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); + $Bio.key.forEach(v => this.img[v] = this.bor[v] + (v != 'r' ? (!bioSet.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); + this.text.r = (bioSet.sbarShow ? Math.max(bioSet.textR, bio.ui.sbar.sp + sp2) : bioSet.textR) + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0); + const txt_sp = Math.round((this.w - this.img.l - this.text.r) * (1 - bioSet.rel_imgs)); + const txt_h = this.h - bioSet.textT - bioSet.textB - (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0) - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0); + this.lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); this.style.imgSize = Math.max(this.w - txt_sp - this.img.l - this.text.r - this.style.gap, 10); /** MOD */ this.text.l = this.img.l + this.style.imgSize + this.style.gap - textWidthCorr1; - /** MOD */ this.text.t = !pptBio.topAlign ? uiBio.y + pptBio.textT + (this.h - pptBio.textT - pptBio.textB + (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0) - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) - this.lines_drawn * uiBio.font.main_h + uiBio.heading.h) / 2 : pptBio.textT + uiBio.heading.h + (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0); + /** MOD */ this.text.t = !bioSet.topAlign ? bio.ui.y + bioSet.textT + (this.h - bioSet.textT - bioSet.textB + (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0) - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) - this.lines_drawn * bio.ui.font.main_h + bio.ui.heading.h) / 2 : bioSet.textT + bio.ui.heading.h + (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0); /** MOD */ this.text.w = txt_sp - textWidthCorr3; - this.text.h = this.lines_drawn * uiBio.font.main_h; + this.text.h = this.lines_drawn * bio.ui.font.main_h; this.heading.x = !this.style.fullWidthHeading ? this.text.l : this.bor.l; - this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - pptBio.textR; - /** MOD */ if (this.style.fullWidthHeading) this.img.t = this.text.t - uiBio.y + (RES_4K ? uiBio.heading.linePad * 1.5 : uiBio.heading.linePad * 0.5); + this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - bioSet.textR; + /** MOD */ if (this.style.fullWidthHeading) this.img.t = this.text.t - bio.ui.y + (RES._4K ? bio.ui.heading.linePad * 1.5 : bio.ui.heading.linePad * 0.5); /** MOD */ this.img.l -= textWidthCorr2; - /** MOD */ this.sbar.x = (!this.filmStripSize.r || pptBio.filmStripOverlay ? this.w - uiBio.sbar.sp - this.text.r : this.text.l + this.text.w + sp1); - this.sbar.y = (uiBio.sbar.type < this.sbar.style || pptBio.heading || this.filmStripSize.t || this.filmStripSize.b ? this.text.t : 0) + this.sbar.top_corr; - this.sbar.h = uiBio.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.b ? uiBio.font.main_h * this.lines_drawn + bot_corr * 2 : this.h - this.sbar.y + bot_corr; + /** MOD */ this.sbar.x = (!this.filmStripSize.r || bioSet.filmStripOverlay ? this.w - bio.ui.sbar.sp - this.text.r : this.text.l + this.text.w + sp1); + this.sbar.y = (bio.ui.sbar.type < this.sbar.style || bioSet.heading || this.filmStripSize.t || this.filmStripSize.b ? this.text.t : 0) + this.sbar.top_corr; + this.sbar.h = bio.ui.sbar.type < this.sbar.style || this.filmStripSize.t || this.filmStripSize.b ? bio.ui.font.main_h * this.lines_drawn + bot_corr * 2 : this.h - this.sbar.y + bot_corr; this.repaint.x = this.text.l; this.repaint.y = this.text.t; - /** MOD */ this.repaint.w = this.w - this.repaint.x - (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0) - (pptBio.showFilmStrip ? 0 : this.text.r); - /** MOD */ this.repaint.h = this.h - this.repaint.y - (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0) + uiBio.y; + /** MOD */ this.repaint.w = this.w - this.repaint.x - (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0) - (bioSet.showFilmStrip ? 0 : this.text.r); + /** MOD */ this.repaint.h = this.h - this.repaint.y - (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0) + bio.ui.y; break; } - case pptBio.style > 3: { - const textWidthCorr = filmStripRight && !pptBio.filmStripOverlay ? this.text.r + this.style.gap + RES_4K_Corr : this.text.r * 2; - const textWidthCorr2 = filmStripLeft ? pptBio.filmStripOverlay && pptBio.style === 4 ? this.filmStripSize.l : pptBio.filmStripOverlay && pptBio.style === 5 ? 0 : this.text.r - this.style.gap - RES_4K_Corr : 0; - const sbarScrollCorr = filmStripRight ? pptBio.filmStripOverlay ? 0 : pptBio.style === 4 ? this.text.r - this.filmStripSize.r - this.style.gap - RES_4K_Corr : pptBio.style === 5 ? this.text.r - this.style.gap - RES_4K_Corr : 0 : 0; + case bioSet.style > 3: { + const textWidthCorr = filmStripRight && !bioSet.filmStripOverlay ? this.text.r + this.style.gap + RES_4K_Corr : this.text.r * 2; + const textWidthCorr2 = filmStripLeft ? bioSet.filmStripOverlay && bioSet.style === 4 ? this.filmStripSize.l : bioSet.filmStripOverlay && bioSet.style === 5 ? 0 : this.text.r - this.style.gap - RES_4K_Corr : 0; + const sbarScrollCorr = filmStripRight ? bioSet.filmStripOverlay ? 0 : bioSet.style === 4 ? this.text.r - this.filmStripSize.r - this.style.gap - RES_4K_Corr : bioSet.style === 5 ? this.text.r - this.style.gap - RES_4K_Corr : 0 : 0; - if (pptBio.style - 6 >= this.style.free.length) this.getStyleFallback(); - const obj = pptBio.style === 4 || pptBio.style === 5 ? this.style.overlay : this.style.free[pptBio.style - 6]; + if (bioSet.style - 6 >= this.style.free.length) this.getStyleFallback(); + const obj = bioSet.style === 4 || bioSet.style === 5 ? this.style.overlay : this.style.free[bioSet.style - 6]; if (!bypass) { this.im.l = $Bio.clamp(obj.imL, 0, 1); this.im.r = $Bio.clamp(obj.imR, 0, 1); @@ -1498,78 +1498,78 @@ class PanelBio { this.tx.t = $Bio.clamp(obj.txT, 0, 1); this.tx.b = $Bio.clamp(obj.txB, 0, 1); } - const imL = Math.round(this.im.l * this.w) + (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0); - const imR = Math.round(this.im.r * this.w) + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0); - const imT = Math.round(this.im.t * this.h) + (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0); - const imB = Math.round(this.im.b * this.h) + (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0); - const txL = pptBio.style === 4 ? 0 : Math.round(this.tx.l * this.w) + (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0); - const txR = pptBio.style === 4 ? 0 : Math.round(this.tx.r * this.w) + (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0); - /** MOD */ const txT = pptBio.style === 4 ? uiBio.y : Math.round(this.tx.t * this.h) + (!pptBio.filmStripOverlay ? this.filmStripSize.t : 0); - const txB = pptBio.style === 4 ? 0 : Math.round(this.tx.b * this.h) + (!pptBio.filmStripOverlay ? this.filmStripSize.b : 0); + const imL = Math.round(this.im.l * this.w) + (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0); + const imR = Math.round(this.im.r * this.w) + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0); + const imT = Math.round(this.im.t * this.h) + (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0); + const imB = Math.round(this.im.b * this.h) + (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0); + const txL = bioSet.style === 4 ? 0 : Math.round(this.tx.l * this.w) + (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0); + const txR = bioSet.style === 4 ? 0 : Math.round(this.tx.r * this.w) + (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0); + /** MOD */ const txT = bioSet.style === 4 ? bio.ui.y : Math.round(this.tx.t * this.h) + (!bioSet.filmStripOverlay ? this.filmStripSize.t : 0); + const txB = bioSet.style === 4 ? 0 : Math.round(this.tx.b * this.h) + (!bioSet.filmStripOverlay ? this.filmStripSize.b : 0); this.ibox.l = Math.max(imL, 0); this.ibox.t = Math.max(imT, 0); this.ibox.w = this.w - imL - imR; this.ibox.h = this.h - imT - imB; - /** MOD */ this.img.l = pptBio.style === 4 ? 0 : this.bor.l; // this.img.l = pptBio.style === 4 ? 0 : imL + this.bor.l; - /** MOD */ this.img.r = pptBio.style === 4 ? 0 : this.bor.r; // this.img.r = pptBio.style === 4 ? 0 : imR + this.bor.r; - this.img.t = pptBio.style === 4 ? 0 : imT + this.bor.t; - this.img.b = pptBio.style === 4 ? 0 : imB + this.bor.b; - const t_l = (pptBio.style === 4 ? this.filmStripSize.l : 0) + pptBio.textL + uiBio.overlay.borderWidth; - const t_t = (pptBio.style === 4 ? this.filmStripSize.t : 0) + pptBio.textT + uiBio.overlay.borderWidth; - let t_r = (pptBio.style === 4 ? this.filmStripSize.r : 0) + pptBio.textR + uiBio.overlay.borderWidth; - let t_b = (pptBio.style === 4 ? this.filmStripSize.b : 0) + pptBio.textB + uiBio.overlay.borderWidth; - if ((pptBio.typeOverlay === 2 || pptBio.typeOverlay === 4) && pptBio.style !== 4) { + /** MOD */ this.img.l = bioSet.style === 4 ? 0 : this.bor.l; // this.img.l = bioSet.style === 4 ? 0 : imL + this.bor.l; + /** MOD */ this.img.r = bioSet.style === 4 ? 0 : this.bor.r; // this.img.r = bioSet.style === 4 ? 0 : imR + this.bor.r; + this.img.t = bioSet.style === 4 ? 0 : imT + this.bor.t; + this.img.b = bioSet.style === 4 ? 0 : imB + this.bor.b; + const t_l = (bioSet.style === 4 ? this.filmStripSize.l : 0) + bioSet.textL + bio.ui.overlay.borderWidth; + const t_t = (bioSet.style === 4 ? this.filmStripSize.t : 0) + bioSet.textT + bio.ui.overlay.borderWidth; + let t_r = (bioSet.style === 4 ? this.filmStripSize.r : 0) + bioSet.textR + bio.ui.overlay.borderWidth; + let t_b = (bioSet.style === 4 ? this.filmStripSize.b : 0) + bioSet.textB + bio.ui.overlay.borderWidth; + if ((bioSet.typeOverlay === 2 || bioSet.typeOverlay === 4) && bioSet.style !== 4) { t_r += 1; t_b += 1; } - /** MOD */ const txt_h = Math.round((this.h - txT - txB - t_t - t_b) + uiBio.heading.h); - this.lines_drawn = Math.max(Math.floor((txt_h - uiBio.heading.h) / uiBio.font.main_h), 0); + /** MOD */ const txt_h = Math.round((this.h - txT - txB - t_t - t_b) + bio.ui.heading.h); + this.lines_drawn = Math.max(Math.floor((txt_h - bio.ui.heading.h) / bio.ui.font.main_h), 0); /** MOD */ this.text.l = txL + t_l - textWidthCorr2; - this.text.r = txR + (pptBio.sbarShow ? Math.max(t_r, uiBio.sbar.sp + sp1) : t_r); - /** MOD */ this.text.t = uiBio.y + txT + t_t; + this.text.r = txR + (bioSet.sbarShow ? Math.max(t_r, bio.ui.sbar.sp + sp1) : t_r); + /** MOD */ this.text.t = bio.ui.y + txT + t_t; /** MOD */ this.text.w = this.w - this.text.l - textWidthCorr; - this.text.h = this.lines_drawn * uiBio.font.main_h; - /** MOD */ this.heading.x = !this.style.fullWidthHeading ? this.text.l : pptBio.style === 4 ? pptBio.textL : Math.min(this.img.l, this.text.l, (!pptBio.filmStripOverlay ? this.filmStripSize.l : 0) ? filmStrip.x : this.w); - /** MOD */ this.heading.w = !this.style.fullWidthHeading ? this.text.w : pptBio.style === 4 ? this.w - this.heading.x * 2 : this.w - this.heading.x - Math.min(this.img.r, this.text.r, (!pptBio.filmStripOverlay ? this.filmStripSize.r : 0) ? this.w - filmStrip.x - filmStrip.w : this.w); + this.text.h = this.lines_drawn * bio.ui.font.main_h; + /** MOD */ this.heading.x = !this.style.fullWidthHeading ? this.text.l : bioSet.style === 4 ? bioSet.textL : Math.min(this.img.l, this.text.l, (!bioSet.filmStripOverlay ? this.filmStripSize.l : 0) ? bio.filmStrip.x : this.w); + /** MOD */ this.heading.w = !this.style.fullWidthHeading ? this.text.w : bioSet.style === 4 ? this.w - this.heading.x * 2 : this.w - this.heading.x - Math.min(this.img.r, this.text.r, (!bioSet.filmStripOverlay ? this.filmStripSize.r : 0) ? this.w - bio.filmStrip.x - bio.filmStrip.w : this.w); this.tbox.l = Math.max(txL, 0); - /** MOD */ this.tbox.t = pptBio.style === 4 ? 0 : Math.max(txT, 0) - uiBio.heading.h * 0.5; + /** MOD */ this.tbox.t = bioSet.style === 4 ? 0 : Math.max(txT, 0) - bio.ui.heading.h * 0.5; this.tbox.w = this.w - Math.max(txL, 0) - Math.max(txR, 0); this.tbox.h = this.h - Math.max(txT, 0) - Math.max(txB, 0); - this.style.minH = uiBio.font.main_h + uiBio.heading.h + t_t + t_b; - if (pptBio.typeOverlay === 2 && pptBio.style !== 4) uiBio.overlay.borderWidth = Math.max(Math.min(uiBio.overlay.borderWidth, this.tbox.w / 3, this.tbox.h / 3), 1); - if (pptBio.typeOverlay && pptBio.style !== 4) this.arc = Math.max(Math.min(uiBio.font.main_h / 1.5, this.tbox.w / 3, this.tbox.h / 3), 1); + this.style.minH = bio.ui.font.main_h + bio.ui.heading.h + t_t + t_b; + if (bioSet.typeOverlay === 2 && bioSet.style !== 4) bio.ui.overlay.borderWidth = Math.max(Math.min(bio.ui.overlay.borderWidth, this.tbox.w / 3, this.tbox.h / 3), 1); + if (bioSet.typeOverlay && bioSet.style !== 4) this.arc = Math.max(Math.min(bio.ui.font.main_h / 1.5, this.tbox.w / 3, this.tbox.h / 3), 1); this.clip = this.ibox.t + 100 < this.tbox.t && this.tbox.t < this.ibox.t + this.ibox.h && (this.tbox.l < this.ibox.l + this.ibox.w || this.tbox.l + this.tbox.w < this.ibox.l + this.ibox.w); this.style.imgSize = this.clip ? this.tbox.t - this.ibox.t : Math.min(this.h - imT - imB - this.bor.t - this.bor.b, this.w - imL - imR - this.bor.l - this.bor.r); - /** MOD */ this.sbar.x = this.tbox.l + this.tbox.w - uiBio.sbar.sp - uiBio.overlay.borderWidth - this.text.r + (pptBio.style === 4 && pptBio.showFilmStrip && pptBio.filmStripOverlay ? this.filmStripSize.r : sbarScrollCorr); + /** MOD */ this.sbar.x = this.tbox.l + this.tbox.w - bio.ui.sbar.sp - bio.ui.overlay.borderWidth - this.text.r + (bioSet.style === 4 && bioSet.showFilmStrip && bioSet.filmStripOverlay ? this.filmStripSize.r : sbarScrollCorr); this.sbar.y = this.text.t + this.sbar.top_corr; - this.sbar.h = uiBio.font.main_h * this.lines_drawn + bot_corr * 2; + this.sbar.h = bio.ui.font.main_h * this.lines_drawn + bot_corr * 2; this.repaint.x = this.tbox.l; - /** MOD */ this.repaint.y = this.tbox.t + uiBio.y; + /** MOD */ this.repaint.y = this.tbox.t + bio.ui.y; this.repaint.w = this.tbox.w; this.repaint.h = this.tbox.h; break; } } - if (uiBio.sbar.type === 2) { + if (bio.ui.sbar.type === 2) { this.sbar.y += 1; this.sbar.h -= 2; } this.text.w = Math.max(this.text.w, 10); - this.style.max_y = this.lines_drawn * uiBio.font.main_h + this.text.t - uiBio.font.main_h * 0.9; - if (!this.id.init) filmStrip.check(); + this.style.max_y = this.lines_drawn * bio.ui.font.main_h + this.text.t - bio.ui.font.main_h * 0.9; + if (!this.id.init) bio.filmStrip.check(); this.id.init = false; } setSummary() { this.summary = { - date: pptBio.summaryShow && pptBio.summaryDate, - genre: pptBio.summaryShow && pptBio.summaryGenre, - latest: pptBio.summaryShow && pptBio.summaryLatest, - locale: pptBio.summaryShow && pptBio.summaryLocale, - other: pptBio.summaryShow && pptBio.summaryOther, - popNow: pptBio.summaryShow && pptBio.summaryPopNow, - show: pptBio.summaryShow + date: bioSet.summaryShow && bioSet.summaryDate, + genre: bioSet.summaryShow && bioSet.summaryGenre, + latest: bioSet.summaryShow && bioSet.summaryLatest, + locale: bioSet.summaryShow && bioSet.summaryLocale, + other: bioSet.summaryShow && bioSet.summaryOther, + popNow: bioSet.summaryShow && bioSet.summaryPopNow, + show: bioSet.summaryShow }; } @@ -1587,22 +1587,22 @@ class PanelBio { } simTagTopLookUp() { - const li = pptBio.artistView ? this.art : this.alb; + const li = bioSet.artistView ? this.art : this.alb; return li.ix && li.list[li.ix] && li.list[li.ix].type != 'history'; } stndItem() { - return !this.art.ix && pptBio.artistView || !this.alb.ix && !pptBio.artistView; + return !this.art.ix && bioSet.artistView || !this.alb.ix && !bioSet.artistView; } tfBio(n, artist, focus) { - n = n.replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_artist%/gi, '$&#@!%path%#@!').replace(/%bio_artist%/gi, $Bio.tfEscape(artist)).replace(/%bio_album%/gi, cfg.tf.album).replace(/%bio_title%/gi, cfg.tf.title); + n = n.replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_artist%/gi, '$&#@!%path%#@!').replace(/%bio_artist%/gi, $Bio.tfEscape(artist)).replace(/%bio_album%/gi, bioCfg.tf.album).replace(/%bio_title%/gi, bioCfg.tf.title); n = $Bio.eval(n, focus); return n.replace(/#@!.*?#@!/g, ''); } tfRev(n, albumArtist, album, focus) { - n = n.replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*(%bio_albumartist%|%bio_album%)/gi, '$&#@!%path%#@!').replace(/%bio_albumartist%/gi, $Bio.tfEscape(albumArtist)).replace(/%bio_album%/gi, $Bio.tfEscape(album)).replace(/%bio_title%/gi, cfg.tf.title); + n = n.replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*(%bio_albumartist%|%bio_album%)/gi, '$&#@!%path%#@!').replace(/%bio_albumartist%/gi, $Bio.tfEscape(albumArtist)).replace(/%bio_album%/gi, $Bio.tfEscape(album)).replace(/%bio_title%/gi, bioCfg.tf.title); n = $Bio.eval(n, focus); return n.replace(/#@!.*?#@!/g, ''); } @@ -1636,22 +1636,22 @@ class PanelBio { updateNeeded() { switch (true) { - case pptBio.artistView: + case bioSet.artistView: this.id.curArtist = this.id.artist; this.id.artist = $Bio.eval(this.art.fields, this.id.focus); return !this.id.lookUp ? true : this.id.artist != this.id.curArtist || !this.art.list.length || !this.art.ix; - case !pptBio.artistView: + case !bioSet.artistView: this.id.curAlb = this.id.alb; - this.id.alb = name.albID(this.id.focus, 'simple'); + this.id.alb = bio.name.albID(this.id.focus, 'simple'); if (this.style.inclTrackRev) { this.id.curTr = this.id.tr; - this.id.tr = name.trackID(this.id.focus); + this.id.tr = bio.name.trackID(this.id.focus); } else this.id.curTr = this.id.tr = ''; return !this.id.lookUp ? true : this.id.alb != this.id.curAlb || this.id.tr != this.id.curTr || !this.alb.list.length || !this.alb.ix; } } zoom() { - return vkBio.k('shift') || vkBio.k('ctrl'); + return bio.vk.k('shift') || bio.vk.k('ctrl'); } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-popupbox.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-popupbox.js index 47836447..4930a3c4 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-popupbox.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-popupbox.js @@ -1,6 +1,6 @@ 'use strict'; -class PopUpBoxBio { +class BioPopUpBox { constructor() { this.getHtmlCode(); this.ok = true; @@ -16,32 +16,32 @@ class PopUpBoxBio { } getHtmlCode() { - let cssPath = `${my_utilsBio.packagePath}/assets/html/`; + let cssPath = `${bio_my_utils.packagePath}/assets/html/`; cssPath += this.getWindowsVersion() === '6.1' ? 'styles7.css' : 'styles10.css'; - this.configHtmlCode = my_utilsBio.getAsset('\\html\\config.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); - this.inputHtmlCode = my_utilsBio.getAsset('\\html\\input.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); - this.confirmHtmlCode = my_utilsBio.getAsset('\\html\\confirm.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.configHtmlCode = bio_my_utils.getAsset('\\html\\config.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.inputHtmlCode = bio_my_utils.getAsset('\\html\\input.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.confirmHtmlCode = bio_my_utils.getAsset('\\html\\confirm.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); } getWindowsVersion() { let version = ''; try { - version = (WshShellBio.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString(); + version = (bioWshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString(); version += '.'; - version += (WshShellBio.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString(); + version += (bioWshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString(); return version; } catch (e) {} try { - version = WshShellBio.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion'); + version = bioWshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion'); return version; } catch (e) {} return '6.1'; } - config(ppt, cfg, dialogWindow, ok_callback, lang, recycler) { + config(libSet, cfg, dialogWindow, ok_callback, lang, recycler) { utils.ShowHtmlDialog(0, this.configHtmlCode, { - data: [ppt, cfg, dialogWindow, window.IsTransparent, ok_callback, this.tf_callback, lang, recycler], + data: [libSet, cfg, dialogWindow, window.IsTransparent, ok_callback, this.tf_callback, lang, recycler], resizable: true }); } @@ -53,26 +53,26 @@ class PopUpBoxBio { } isHtmlDialogSupported() { - if (pptBio.isHtmlDialogSupported != 2) return pptBio.isHtmlDialogSupported; + if (bioSet.isHtmlDialogSupported != 2) return bioSet.isHtmlDialogSupported; - if (typeof docBio === 'undefined' || !docBio) { + if (typeof bioDoc === 'undefined' || !bioDoc) { this.soFeat.gecko = false; } if (this.soFeat.gecko) { let cache = null; let clText = 'test'; try { - cache = docBio.parentWindow.clipboardData.getData('Text'); + cache = bioDoc.parentWindow.clipboardData.getData('Text'); } catch (e) {} try { - docBio.parentWindow.clipboardData.setData('Text', clText); - clText = docBio.parentWindow.clipboardData.getData('Text'); + bioDoc.parentWindow.clipboardData.setData('Text', clText); + clText = bioDoc.parentWindow.clipboardData.getData('Text'); } catch (e) { this.soFeat.clipboard = false; } if (cache) { // Just in case previous clipboard data is needed try { - docBio.parentWindow.clipboardData.setData('Text', cache); + bioDoc.parentWindow.clipboardData.setData('Text', cache); } catch (e) {} } if (clText !== 'test') { @@ -82,8 +82,8 @@ class PopUpBoxBio { this.soFeat.clipboard = false; } - pptBio.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard || this.isIEInstalled() ? 1 : 0; - if (!pptBio.isHtmlDialogSupported) { + bioSet.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard || this.isIEInstalled() ? 1 : 0; + if (!bioSet.isHtmlDialogSupported) { const caption = 'Show HTML Dialog'; const prompt = `A feature check indicates that Spider Monkey Panel show html dialog isn't supported by the current operating system. @@ -95,15 +95,15 @@ Supported-1; unsupported-0`; let ns = ''; let status = 'ok' try { - ns = utils.InputBox(0, prompt, caption, pptBio.isHtmlDialogSupported, true); + ns = utils.InputBox(0, prompt, caption, bioSet.isHtmlDialogSupported, true); } catch (e) { status = 'cancel'; } if (status != 'cancel') { - pptBio.isHtmlDialogSupported = ns == 0 ? 0 : 1; + bioSet.isHtmlDialogSupported = ns == 0 ? 0 : 1; } } - return pptBio.isHtmlDialogSupported; + return bioSet.isHtmlDialogSupported; } isIEInstalled() { @@ -117,6 +117,6 @@ Supported-1; unsupported-0`; } tf_callback(tf, tfAll, type, sFind, sReplace) { - return cfg.preview(tf, tfAll, type, sFind, sReplace); + return bioCfg.preview(tf, tfAll, type, sFind, sReplace); } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-properties.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-properties.js index 8aaaeeb7..2d8ae313 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-properties.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-properties.js @@ -1,10 +1,10 @@ 'use strict'; -class PanelPropertyBio { +class BioPanelProperty { constructor(name, default_value) { this.name = name; this.default_value = default_value; - this.value = pptBio.get(this.name, default_value); + this.value = bioSet.get(this.name, default_value); } // * METHODS * // @@ -15,13 +15,13 @@ class PanelPropertyBio { set(new_value) { if (this.value !== new_value) { - pptBio.set(this.name, new_value); + bioSet.set(this.name, new_value); this.value = new_value; } } } -class PanelPropertiesBio { +class BioPanelProperties { constructor() { // this.name_list = {}; debug } @@ -57,7 +57,7 @@ class PanelPropertiesBio { add(item) { // this.name_list[item[0]] = 1; debug - this[`${item[2]}_internal`] = new PanelPropertyBio(item[0], item[1]); + this[`${item[2]}_internal`] = new BioPanelProperty(item[0], item[1]); Object.defineProperty(this, item[2], { get() { @@ -82,7 +82,8 @@ class PanelPropertiesBio { } } -let propertiesBio = [ +/** @global @type {Array.} */ +let bioProperties = [ ['Panel Biography - - Show Html Dialog Unsupported-0 Supported-1 Autocheck-2', 2, 'isHtmlDialogSupported'], ['Panel Biography - Album History', JSON.stringify([]), 'albumHistory'], ['Panel Biography - Artist History', JSON.stringify([]), 'artistHistory'], @@ -434,31 +435,36 @@ let propertiesBio = [ ['Panel Biography - Zoom Tooltip (%)', 100, 'zoomTooltip'] ]; -const pptBio = new PanelPropertiesBio(); -pptBio.init('auto', propertiesBio); -propertiesBio = undefined; - -if (pptBio.get('Panel Biography - Update Properties', true)) { // ~22.7.22 - pptBio.nmTxtReader7 = 'item properties'; - pptBio.pthTxtReader7 = '%storage_folder%\\item_properties.json'; - pptBio.lyricsTxtReader7 = false; - if (pptBio.summary == '128,228,0') pptBio.summary = '128,228,27'; +/** + * The instance of `BioPanelProperties` class for biography panel property settings. + * @typedef {BioPanelProperties} + * @global + */ +const bioSet = new BioPanelProperties(); +bioSet.init('auto', bioProperties); +bioProperties = undefined; + +if (bioSet.get('Panel Biography - Update Properties', true)) { // ~22.7.22 + bioSet.nmTxtReader7 = 'item properties'; + bioSet.pthTxtReader7 = '%storage_folder%\\item_properties.json'; + bioSet.lyricsTxtReader7 = false; + if (bioSet.summary == '128,228,0') bioSet.summary = '128,228,27'; const oldProperties = ['Stub Path: Front [No Title Format Except %profile%]', 'Stub Path: Back [No Title Format Except %profile%]', 'Stub Path: Disc [No Title Format Except %profile%]', 'Stub Path: Icon [No Title Format Except %profile%]', 'Stub Path: Artist [No Title Format Except %profile%]']; const props = ['panelFrontStub', 'panelBackStub', 'panelDiscStub', 'panelIconStub', 'panelArtStub']; - oldProperties.forEach((v, i) => { const value = window.GetProperty(v); if (value) pptBio[props[i]] = value; window.SetProperty(v, null); }); + oldProperties.forEach((v, i) => { const value = window.GetProperty(v); if (value) bioSet[props[i]] = value; window.SetProperty(v, null); }); window.SetProperty('Lock Rev', null); - pptBio.set('Panel Biography - Update Properties', false); + bioSet.set('Panel Biography - Update Properties', false); } -if (pptBio.get('Panel Biography - Reset Track Review', true)) { - pptBio.inclTrackRev = 0; - pptBio.set('Panel Biography - Reset Track Review', false); +if (bioSet.get('Panel Biography - Reset Track Review', true)) { + bioSet.inclTrackRev = 0; + bioSet.set('Panel Biography - Reset Track Review', false); } -if (pptBio.get('Panel Biography - Remove Old Properties', true)) { +if (bioSet.get('Panel Biography - Remove Old Properties', true)) { const oldProperties = ['Allmusic Alb', 'Allmusic Bio', 'Both Bio', 'Both Rev', 'Heading', 'Heading BtnName Biography [AllMusic]', 'Heading BtnName Biography [Last.fm]', 'Heading BtnName Biography [Wikipedia]', 'Heading BtnName Review [AllMusic]', 'Heading BtnName Review [Last.fm]', 'Heading BtnName Review [Wikipedia]', 'Heading Title Format Album Review [AllMusic]', 'Heading Title Format Album Review [Last.fm]', 'Heading Title Format Biography [AllMusic]', 'Heading Title Format Biography [Last.fm]', 'Heading Title Format Track Review [AllMusic]', 'Heading Title Format Track Review [Last.fm]', 'Layout Dual Image+Text', 'Image Seeker Dots', 'Subheading [Source] Text Biography [AllMusic]: Heading|No Heading', 'Subheading [Source] Text Biography [Last.fm]: Heading|No Heading', 'Subheading [Source] Text Biography [Wikipedia]: Heading|No Heading', 'Subheading [Source] Text Review [AllMusic]: Heading|No Heading', 'Subheading [Source] Text Review [Last.fm]: Heading|No Heading', 'Subheading Source Hide-0 Show-1', 'Subheading [Source] Text Review [Wikipedia]: Heading|No Heading', 'Subheading [Track Review] Title Format [AllMusic]', 'Subheading [Track Review] Title Format [Last.fm]', 'Summary First', 'Tagger Last.fm Genre Find>Replace', 'Tagger Last.fm Genre Number Clean Up', 'Tagger Last.fm Genre Run Find>Replace', 'Tagger Last.fm Genre Strip Artist+Album Names', 'Text Album + Track Auto Optimise', 'Text Reader Source 0 Name', 'Text Reader Source 1 Name', 'Text Reader Source 2 Name', 'Text Reader Source 3 Name', 'Text Reader Source 4 Name', 'Text Reader Source 5 Name', 'Text Reader Source 6 Name', 'Text Reader Source 7 Name', 'Text Reader Source 1 (field or full path)', 'Text Reader Source 2 (field or full path)', 'Text Reader Source 3 (field or full path)', 'Text Reader Source 4 (field or full path)', 'Text Reader Source 5 (field or full path)', 'Text Reader Source 6 (field or full path)', 'Text Reader Source 7 (field or full path)', 'Text Reader Source 8 (field or full path)', 'Text Reader Source 1 Lyrics', 'Text Reader Source 2 Lyrics', 'Text Reader Source 3 Lyrics', 'Text Reader Source 4 Lyrics', 'Text Reader Source 5 Lyrics', 'Text Reader Source 6 Lyrics', 'Text Reader Source 7 Lyrics', 'Text Reader Source 8 Lyrics']; oldProperties.forEach(v => window.SetProperty(v, null)); - pptBio.set('Panel Biography - Remove Old Properties', false); + bioSet.set('Panel Biography - Remove Old Properties', false); } window.SetProperty('Panel Biography - Scrollbar Type Default-0 Styled-1 Windows-2', null); diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-resize.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-resize.js index 3225534d..23d33a5d 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-resize.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-resize.js @@ -1,9 +1,9 @@ 'use strict'; -class ResizeHandler { +class BioResizeHandler { constructor() { this.down = false; - this.editorFont = gdi.Font(fontDefault, 15 * $Bio.scale, 1); + this.editorFont = gdi.Font(grFont.fontDefault, 15 * $Bio.scale, 1); this.focus = true; this.lc = StringFormat(0, 1); this.init_x = 0; @@ -33,99 +33,99 @@ class ResizeHandler { // * METHODS * // drawEd(gr) { - if (vkBio.k('ctrl') && this.focus && panelBio.m.y != -1 || panelBio.style.new) { - const ed = gr.MeasureString(this.editText(), this.editorFont, 15, 15, panelBio.w - 15, panelBio.h - 15, this.lc); - gr.FillSolidRect(10, 10, ed.Width + 10, ed.Height + 10, uiBio.col.edBg); - if (!pptBio.text_only && !pptBio.img_only) { - if (pptBio.style > 4) { - if (!vkBio.k('shift')) gr.DrawRect(panelBio.ibox.l + 2, panelBio.ibox.t + 2, panelBio.ibox.w - 4, panelBio.ibox.h - 4, 5, RGB(0, 255, 0)); - if (!vkBio.k('alt')) gr.DrawRect(panelBio.tbox.l + 2, panelBio.tbox.t + 2, panelBio.tbox.w - 4, panelBio.tbox.h - 4, 5, RGB(255, 0, 0)); - } else if (pptBio.style < 4) { - switch (pptBio.style) { + if (bio.vk.k('ctrl') && this.focus && bio.panel.m.y != -1 || bio.panel.style.new) { + const ed = gr.MeasureString(this.editText(), this.editorFont, 15, 15, bio.panel.w - 15, bio.panel.h - 15, this.lc); + gr.FillSolidRect(10, 10, ed.Width + 10, ed.Height + 10, bio.ui.col.edBg); + if (!bioSet.text_only && !bioSet.img_only) { + if (bioSet.style > 4) { + if (!bio.vk.k('shift')) gr.DrawRect(bio.panel.ibox.l + 2, bio.panel.ibox.t + 2, bio.panel.ibox.w - 4, bio.panel.ibox.h - 4, 5, RGB(0, 255, 0)); + if (!bio.vk.k('alt')) gr.DrawRect(bio.panel.tbox.l + 2, bio.panel.tbox.t + 2, bio.panel.tbox.w - 4, bio.panel.tbox.h - 4, 5, RGB(255, 0, 0)); + } else if (bioSet.style < 4) { + switch (bioSet.style) { case 0: - gr.FillSolidRect(0, panelBio.img.t + panelBio.style.imgSize, panelBio.w, 5, RGB(255, 128, 0)); + gr.FillSolidRect(0, bio.panel.img.t + bio.panel.style.imgSize, bio.panel.w, 5, RGB(255, 128, 0)); break; case 1: - gr.FillSolidRect(panelBio.img.l - 5, 0, 5, panelBio.h, RGB(255, 128, 0)); + gr.FillSolidRect(bio.panel.img.l - 5, 0, 5, bio.panel.h, RGB(255, 128, 0)); break; case 2: - gr.FillSolidRect(0, panelBio.img.t - 5, panelBio.w, 5, RGB(255, 128, 0)); + gr.FillSolidRect(0, bio.panel.img.t - 5, bio.panel.w, 5, RGB(255, 128, 0)); break; case 3: - gr.FillSolidRect(panelBio.img.l + panelBio.style.imgSize, 0, 5, panelBio.h, RGB(255, 128, 0)); + gr.FillSolidRect(bio.panel.img.l + bio.panel.style.imgSize, 0, 5, bio.panel.h, RGB(255, 128, 0)); break; } } } - if (panelBio.style.showFilmStrip) { - switch (pptBio.filmStripPos) { + if (bio.panel.style.showFilmStrip) { + switch (bioSet.filmStripPos) { case 0: - gr.FillSolidRect(0, filmStrip.y + filmStrip.h, panelBio.w, 5, RGB(0, 255, 255)); + gr.FillSolidRect(0, bio.filmStrip.y + bio.filmStrip.h, bio.panel.w, 5, RGB(0, 255, 255)); break; case 1: - gr.FillSolidRect(filmStrip.x - 5, 0, 5, panelBio.h, RGB(0, 255, 255)); + gr.FillSolidRect(bio.filmStrip.x - 5, 0, 5, bio.panel.h, RGB(0, 255, 255)); break; case 2: - gr.FillSolidRect(0, filmStrip.y - 5, panelBio.w, 5, RGB(0, 255, 255)); + gr.FillSolidRect(0, bio.filmStrip.y - 5, bio.panel.w, 5, RGB(0, 255, 255)); break; case 3: - gr.FillSolidRect(filmStrip.x + filmStrip.w, 0, 5, panelBio.h, RGB(0, 255, 255)); + gr.FillSolidRect(bio.filmStrip.x + bio.filmStrip.w, 0, 5, bio.panel.h, RGB(0, 255, 255)); break; } } gr.SetTextRenderingHint(5); - gr.DrawString(this.editText(), this.editorFont, uiBio.col.shadow, 16, 16, ed.Width, ed.Height, this.lc); - gr.DrawString(this.editText(), this.editorFont, uiBio.col.text_h, 15, 15, ed.Width, ed.Height, this.lc); + gr.DrawString(this.editText(), this.editorFont, bio.ui.col.shadow, 16, 16, ed.Width, ed.Height, this.lc); + gr.DrawString(this.editText(), this.editorFont, bio.ui.col.text_h, 15, 15, ed.Width, ed.Height, this.lc); } } editText() { - return `${(pptBio.text_only ? `Type: Text Only${panelBio.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : ''}` : (pptBio.img_only ? `Type: Image Only${panelBio.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : ''}` : `Name: ${panelBio.style.name[pptBio.style]}${pptBio.style < 4 ? `\n\nType: Auto\n - Layout Adjust: Drag Line${panelBio.style.showFilmStrip && !pptBio.img_only && pptBio.style != 4 ? 's' : ''}\n - Image Strength: Shift + Wheel Over Text` : pptBio.style == 4 ? `\n\nType: Auto${panelBio.style.showFilmStrip && !pptBio.img_only ? '\n - Layout Adjust: Drag Line' : ''}\n - Image Strength: Shift + Wheel Over Text` : '\n\nType: Freestyle\n - Layout Adjust: Drag Lines or Boxes: Ctrl (Any), Ctrl + Alt (Image), Ctrl + Shift (Text) or Ctrl + Alt + Shift (Filmstrip)\n - Overlay Strength: Shift + Wheel Over Text'}`)) + (imgBio.isType('Refl') && !pptBio.text_only && pptBio.style != 4 ? '\n - Reflection Strength: Shift + Wheel Over Main Image' : '') + (!pptBio.img_only ? '\n - Text Size: Ctrl + Wheel Over Text' : '')}\n - ${pptBio.style != 4 ? '' : 'Text '}Padding: Display Tab`; + return `${(bioSet.text_only ? `Type: Text Only${bio.panel.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : ''}` : (bioSet.img_only ? `Type: Image Only${bio.panel.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : ''}` : `Name: ${bio.panel.style.name[bioSet.style]}${bioSet.style < 4 ? `\n\nType: Auto\n - Layout Adjust: Drag Line${bio.panel.style.showFilmStrip && !bioSet.img_only && bioSet.style != 4 ? 's' : ''}\n - Image Strength: Shift + Wheel Over Text` : bioSet.style == 4 ? `\n\nType: Auto${bio.panel.style.showFilmStrip && !bioSet.img_only ? '\n - Layout Adjust: Drag Line' : ''}\n - Image Strength: Shift + Wheel Over Text` : '\n\nType: Freestyle\n - Layout Adjust: Drag Lines or Boxes: Ctrl (Any), Ctrl + Alt (Image), Ctrl + Shift (Text) or Ctrl + Alt + Shift (Filmstrip)\n - Overlay Strength: Shift + Wheel Over Text'}`)) + (bio.img.isType('Refl') && !bioSet.text_only && bioSet.style != 4 ? '\n - Reflection Strength: Shift + Wheel Over Main Image' : '') + (!bioSet.img_only ? '\n - Text Size: Ctrl + Wheel Over Text' : '')}\n - ${bioSet.style != 4 ? '' : 'Text '}Padding: Display Tab`; } filmMove(x, y) { - if (!this.focus || !panelBio.style.showFilmStrip || !vkBio.k('ctrl')) return; + if (!this.focus || !bio.panel.style.showFilmStrip || !bio.vk.k('ctrl')) return; if (!this.down) { - switch (pptBio.filmStripPos) { + switch (bioSet.filmStripPos) { case 0: - this.sf = y > filmStrip.y + filmStrip.h && y < filmStrip.y + filmStrip.h + 5; + this.sf = y > bio.filmStrip.y + bio.filmStrip.h && y < bio.filmStrip.y + bio.filmStrip.h + 5; if (this.sf) window.SetCursor(32645); break; case 1: - this.sf = x > filmStrip.x - 5 && x < filmStrip.x; + this.sf = x > bio.filmStrip.x - 5 && x < bio.filmStrip.x; if (this.sf) window.SetCursor(32644); break; case 2: - this.sf = y > filmStrip.y - 5 && y < filmStrip.y; + this.sf = y > bio.filmStrip.y - 5 && y < bio.filmStrip.y; if (this.sf) window.SetCursor(32645); break; case 3: - this.sf = x > filmStrip.x + filmStrip.w && x < filmStrip.x + filmStrip.w + 5; + this.sf = x > bio.filmStrip.x + bio.filmStrip.w && x < bio.filmStrip.x + bio.filmStrip.w + 5; if (this.sf) window.SetCursor(32644); break; } } if (!this.down || !this.sf) return; - switch (pptBio.filmStripPos) { + switch (bioSet.filmStripPos) { case 0: - pptBio.filmStripSize = (pptBio.filmStripSize * panelBio.h + y - this.y_start) / panelBio.h; - pptBio.filmStripSize = $Bio.clamp(parseFloat(pptBio.filmStripSize.toFixed(15)), 0.02, filmStrip.max_sz / panelBio.h); + bioSet.filmStripSize = (bioSet.filmStripSize * bio.panel.h + y - this.y_start) / bio.panel.h; + bioSet.filmStripSize = $Bio.clamp(parseFloat(bioSet.filmStripSize.toFixed(15)), 0.02, bio.filmStrip.max_sz / bio.panel.h); break; case 1: - pptBio.filmStripSize = (pptBio.filmStripSize * panelBio.w + this.x_start - x) / panelBio.w; - pptBio.filmStripSize = $Bio.clamp(parseFloat(pptBio.filmStripSize.toFixed(15)), 0.02, filmStrip.max_sz / panelBio.w); + bioSet.filmStripSize = (bioSet.filmStripSize * bio.panel.w + this.x_start - x) / bio.panel.w; + bioSet.filmStripSize = $Bio.clamp(parseFloat(bioSet.filmStripSize.toFixed(15)), 0.02, bio.filmStrip.max_sz / bio.panel.w); break; case 2: - pptBio.filmStripSize = (pptBio.filmStripSize * panelBio.h + this.y_start - y) / panelBio.h; - pptBio.filmStripSize = $Bio.clamp(parseFloat(pptBio.filmStripSize.toFixed(15)), 0.02, filmStrip.max_sz / panelBio.h); + bioSet.filmStripSize = (bioSet.filmStripSize * bio.panel.h + this.y_start - y) / bio.panel.h; + bioSet.filmStripSize = $Bio.clamp(parseFloat(bioSet.filmStripSize.toFixed(15)), 0.02, bio.filmStrip.max_sz / bio.panel.h); break; case 3: - pptBio.filmStripSize = (pptBio.filmStripSize * panelBio.w + x - this.x_start) / panelBio.w; - pptBio.filmStripSize = $Bio.clamp(parseFloat(pptBio.filmStripSize.toFixed(15)), 0.02, filmStrip.max_sz / panelBio.w); + bioSet.filmStripSize = (bioSet.filmStripSize * bio.panel.w + x - this.x_start) / bio.panel.w; + bioSet.filmStripSize = $Bio.clamp(parseFloat(bioSet.filmStripSize.toFixed(15)), 0.02, bio.filmStrip.max_sz / bio.panel.w); break; } - filmStrip.logScrollPos(); - filmStrip.setSize(); + bio.filmStrip.logScrollPos(); + bio.filmStrip.setSize(); window.Repaint(); this.updFilm = true; this.x_start = x; @@ -133,8 +133,8 @@ class ResizeHandler { } lbtn_dn(x, y) { - panelBio.style.new = false; - if (!vkBio.k('ctrl')) return; + bio.panel.style.new = false; + if (!bio.vk.k('ctrl')) return; this.down = true; this.updFilm = false; this.init_x = x; @@ -149,177 +149,177 @@ class ResizeHandler { if (!this.down || !this.focus) return; window.SetCursor(32512); this.down = false; - imgBio.mask.reset = true; - if (pptBio.style > 3) { - const obj = pptBio.style == 4 || pptBio.style == 5 ? panelBio.style.overlay : panelBio.style.free[pptBio.style - 6]; - const imL = Math.round(panelBio.im.l * panelBio.w); - const imR = Math.round(panelBio.im.r * panelBio.w); - const imT = Math.round(panelBio.im.t * panelBio.h); - const imB = Math.round(panelBio.im.b * panelBio.h); - const txL = Math.round(panelBio.tx.l * panelBio.w); - const txR = Math.round(panelBio.tx.r * panelBio.w); - const txT = Math.round(panelBio.tx.t * panelBio.h); - const txB = Math.round(panelBio.tx.b * panelBio.h); + bio.img.mask.reset = true; + if (bioSet.style > 3) { + const obj = bioSet.style == 4 || bioSet.style == 5 ? bio.panel.style.overlay : bio.panel.style.free[bioSet.style - 6]; + const imL = Math.round(bio.panel.im.l * bio.panel.w); + const imR = Math.round(bio.panel.im.r * bio.panel.w); + const imT = Math.round(bio.panel.im.t * bio.panel.h); + const imB = Math.round(bio.panel.im.b * bio.panel.h); + const txL = Math.round(bio.panel.tx.l * bio.panel.w); + const txR = Math.round(bio.panel.tx.r * bio.panel.w); + const txT = Math.round(bio.panel.tx.t * bio.panel.h); + const txB = Math.round(bio.panel.tx.b * bio.panel.h); let sv = false; - if (panelBio.h > txB + txT + pptBio.textT + pptBio.textB + 10 && panelBio.w > txR + txL + pptBio.textL + pptBio.textR + 10) { - obj.txL = panelBio.tx.l; - obj.txR = panelBio.tx.r; - obj.txT = panelBio.tx.t; - obj.txB = panelBio.tx.b; + if (bio.panel.h > txB + txT + bioSet.textT + bioSet.textB + 10 && bio.panel.w > txR + txL + bioSet.textL + bioSet.textR + 10) { + obj.txL = bio.panel.tx.l; + obj.txR = bio.panel.tx.r; + obj.txT = bio.panel.tx.t; + obj.txB = bio.panel.tx.b; sv = true; } - if (panelBio.h > imB + imT + panelBio.bor.t + panelBio.bor.b + 10 && panelBio.w > imR + imL + panelBio.bor.l + panelBio.bor.r + 10) { - obj.imL = panelBio.im.l; - obj.imR = panelBio.im.r; - obj.imT = panelBio.im.t; - obj.imB = panelBio.im.b; + if (bio.panel.h > imB + imT + bio.panel.bor.t + bio.panel.bor.b + 10 && bio.panel.w > imR + imL + bio.panel.bor.l + bio.panel.bor.r + 10) { + obj.imL = bio.panel.im.l; + obj.imR = bio.panel.im.r; + obj.imT = bio.panel.im.t; + obj.imB = bio.panel.im.b; sv = true; } if (sv) { - pptBio.style == 4 ? pptBio.styleOverlay = JSON.stringify(panelBio.style.overlay) : pptBio.styleFree = JSON.stringify(panelBio.style.free); + bioSet.style == 4 ? bioSet.styleOverlay = JSON.stringify(bio.panel.style.overlay) : bioSet.styleFree = JSON.stringify(bio.panel.style.free); } else { - panelBio.im.l = $Bio.clamp(obj.imL, 0, 1); - panelBio.im.r = $Bio.clamp(obj.imR, 0, 1); - panelBio.im.t = $Bio.clamp(obj.imT, 0, 1); - panelBio.im.b = $Bio.clamp(obj.imB, 0, 1); - panelBio.tx.l = $Bio.clamp(obj.txL, 0, 1); - panelBio.tx.r = $Bio.clamp(obj.txR, 0, 1); - panelBio.tx.t = $Bio.clamp(obj.txT, 0, 1); - panelBio.tx.b = $Bio.clamp(obj.txB, 0, 1); + bio.panel.im.l = $Bio.clamp(obj.imL, 0, 1); + bio.panel.im.r = $Bio.clamp(obj.imR, 0, 1); + bio.panel.im.t = $Bio.clamp(obj.imT, 0, 1); + bio.panel.im.b = $Bio.clamp(obj.imB, 0, 1); + bio.panel.tx.l = $Bio.clamp(obj.txL, 0, 1); + bio.panel.tx.r = $Bio.clamp(obj.txR, 0, 1); + bio.panel.tx.t = $Bio.clamp(obj.txT, 0, 1); + bio.panel.tx.b = $Bio.clamp(obj.txB, 0, 1); } } - filmStrip.clearCache(); - if (panelBio.style.showFilmStrip && pptBio.filmStripOverlay) filmStrip.set(pptBio.filmStripPos); - txt.refresh(this.updFilm ? 0 : 3); - filmStrip.paint(); + bio.filmStrip.clearCache(); + if (bio.panel.style.showFilmStrip && bioSet.filmStripOverlay) bio.filmStrip.set(bioSet.filmStripPos); + bio.txt.refresh(this.updFilm ? 0 : 3); + bio.filmStrip.paint(); } imgMove(x, y) { - if (!this.focus || pptBio.img_only || pptBio.text_only || pptBio.style == 4) return; + if (!this.focus || bioSet.img_only || bioSet.text_only || bioSet.style == 4) return; switch (true) { - case pptBio.style > 3: { - if (!vkBio.k('ctrl') || vkBio.k('shift')) break; + case bioSet.style > 3: { + if (!bio.vk.k('ctrl') || bio.vk.k('shift')) break; if (!this.down) { - this.si = y > panelBio.ibox.t - 5 && y < panelBio.ibox.t + 5 && x > panelBio.ibox.l + 10 && x < panelBio.ibox.l + panelBio.ibox.w - 10 ? 'top' : - y > panelBio.ibox.t - 5 && y < panelBio.ibox.t + 15 && x > panelBio.ibox.l && x < panelBio.ibox.l + 10 ? 'nw' : - y > panelBio.ibox.t - 5 && y < panelBio.ibox.t + 15 && x > panelBio.ibox.l + panelBio.ibox.w - 10 && x < panelBio.ibox.l + panelBio.ibox.w ? 'ne' : - y > panelBio.ibox.t + panelBio.ibox.h - 5 && y < panelBio.ibox.t + panelBio.ibox.h + 5 && x > panelBio.ibox.l + 10 && x < panelBio.ibox.l + panelBio.ibox.w - 5 ? 'bottom' : - y > panelBio.ibox.t + panelBio.ibox.h - 15 && y < panelBio.ibox.t + panelBio.ibox.h + 5 && x > panelBio.ibox.l && x < panelBio.ibox.l + 10 ? 'sw' : - y > panelBio.ibox.t + panelBio.ibox.h - 15 && y < panelBio.ibox.t + panelBio.ibox.h + 5 && x > panelBio.ibox.l + panelBio.ibox.w - 10 && x < panelBio.ibox.l + panelBio.ibox.w ? 'se' : - y > panelBio.ibox.t && y < panelBio.ibox.t + panelBio.ibox.h && x > panelBio.ibox.l - 5 && x < panelBio.ibox.l + 5 ? 'left' : - y > panelBio.ibox.t && y < panelBio.ibox.t + panelBio.ibox.h && x > panelBio.ibox.l + panelBio.ibox.w - 5 && x < panelBio.ibox.l + panelBio.ibox.w + 5 ? 'right' : - y > panelBio.ibox.t + 20 && y < panelBio.ibox.t + panelBio.ibox.h - 20 && x > panelBio.ibox.l + 20 && x < panelBio.ibox.l + panelBio.ibox.w - 20 ? 'all' : ''; + this.si = y > bio.panel.ibox.t - 5 && y < bio.panel.ibox.t + 5 && x > bio.panel.ibox.l + 10 && x < bio.panel.ibox.l + bio.panel.ibox.w - 10 ? 'top' : + y > bio.panel.ibox.t - 5 && y < bio.panel.ibox.t + 15 && x > bio.panel.ibox.l && x < bio.panel.ibox.l + 10 ? 'nw' : + y > bio.panel.ibox.t - 5 && y < bio.panel.ibox.t + 15 && x > bio.panel.ibox.l + bio.panel.ibox.w - 10 && x < bio.panel.ibox.l + bio.panel.ibox.w ? 'ne' : + y > bio.panel.ibox.t + bio.panel.ibox.h - 5 && y < bio.panel.ibox.t + bio.panel.ibox.h + 5 && x > bio.panel.ibox.l + 10 && x < bio.panel.ibox.l + bio.panel.ibox.w - 5 ? 'bottom' : + y > bio.panel.ibox.t + bio.panel.ibox.h - 15 && y < bio.panel.ibox.t + bio.panel.ibox.h + 5 && x > bio.panel.ibox.l && x < bio.panel.ibox.l + 10 ? 'sw' : + y > bio.panel.ibox.t + bio.panel.ibox.h - 15 && y < bio.panel.ibox.t + bio.panel.ibox.h + 5 && x > bio.panel.ibox.l + bio.panel.ibox.w - 10 && x < bio.panel.ibox.l + bio.panel.ibox.w ? 'se' : + y > bio.panel.ibox.t && y < bio.panel.ibox.t + bio.panel.ibox.h && x > bio.panel.ibox.l - 5 && x < bio.panel.ibox.l + 5 ? 'left' : + y > bio.panel.ibox.t && y < bio.panel.ibox.t + bio.panel.ibox.h && x > bio.panel.ibox.l + bio.panel.ibox.w - 5 && x < bio.panel.ibox.l + bio.panel.ibox.w + 5 ? 'right' : + y > bio.panel.ibox.t + 20 && y < bio.panel.ibox.t + bio.panel.ibox.h - 20 && x > bio.panel.ibox.l + 20 && x < bio.panel.ibox.l + bio.panel.ibox.w - 20 ? 'all' : ''; this.setCursor(this.si); } if (!this.down || !this.si) return; const filmStrip = { - t: !pptBio.filmStripOverlay ? panelBio.filmStripSize.t : 0, - b: !pptBio.filmStripOverlay ? panelBio.filmStripSize.b : 0, - l: !pptBio.filmStripOverlay ? panelBio.filmStripSize.l : 0, - r: !pptBio.filmStripOverlay ? panelBio.filmStripSize.r : 0 + t: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.t : 0, + b: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.b : 0, + l: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.l : 0, + r: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.r : 0 }; - let imT = Math.round(panelBio.im.t * panelBio.h) + filmStrip.t; - let imB = Math.round(panelBio.im.b * panelBio.h) + filmStrip.b; - let imL = Math.round(panelBio.im.l * panelBio.w) + filmStrip.l; - let imR = Math.round(panelBio.im.r * panelBio.w) + filmStrip.r; + let imT = Math.round(bio.panel.im.t * bio.panel.h) + filmStrip.t; + let imB = Math.round(bio.panel.im.b * bio.panel.h) + filmStrip.b; + let imL = Math.round(bio.panel.im.l * bio.panel.w) + filmStrip.l; + let imR = Math.round(bio.panel.im.r * bio.panel.w) + filmStrip.r; switch (this.si) { case 'top': - if (y > panelBio.h - imB - 30) break; - panelBio.im.t = $Bio.clamp((y - filmStrip.t) / panelBio.h, 0, 1); + if (y > bio.panel.h - imB - 30) break; + bio.panel.im.t = $Bio.clamp((y - filmStrip.t) / bio.panel.h, 0, 1); break; case 'nw': - if (y < panelBio.h - imB - 30) panelBio.im.t = $Bio.clamp((y - filmStrip.t) / panelBio.h, 0, 1); - if (x > panelBio.w - imR - 30) break; - panelBio.im.l = $Bio.clamp((x - filmStrip.l) / panelBio.w, 0, 1); + if (y < bio.panel.h - imB - 30) bio.panel.im.t = $Bio.clamp((y - filmStrip.t) / bio.panel.h, 0, 1); + if (x > bio.panel.w - imR - 30) break; + bio.panel.im.l = $Bio.clamp((x - filmStrip.l) / bio.panel.w, 0, 1); break; case 'ne': - if (y < panelBio.h - imB - 30) panelBio.im.t = $Bio.clamp((y - filmStrip.t) / panelBio.h, 0, 1); + if (y < bio.panel.h - imB - 30) bio.panel.im.t = $Bio.clamp((y - filmStrip.t) / bio.panel.h, 0, 1); if (x < imL + 30) break; - panelBio.im.r = $Bio.clamp((panelBio.w - x - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.im.r = $Bio.clamp((bio.panel.w - x - filmStrip.r) / bio.panel.w, 0, 1); break; case 'left': - if (x > panelBio.w - imR - 30) break; - panelBio.im.l = $Bio.clamp((x - filmStrip.l) / panelBio.w, 0, 1); + if (x > bio.panel.w - imR - 30) break; + bio.panel.im.l = $Bio.clamp((x - filmStrip.l) / bio.panel.w, 0, 1); break; case 'bottom': if (y < imT + 30) break; - panelBio.im.b = $Bio.clamp((panelBio.h - y - filmStrip.b) / panelBio.h, 0, 1); + bio.panel.im.b = $Bio.clamp((bio.panel.h - y - filmStrip.b) / bio.panel.h, 0, 1); break; case 'sw': - if (x < panelBio.w - imR - 30) panelBio.im.l = $Bio.clamp((x - filmStrip.l) / panelBio.w, 0, 1); + if (x < bio.panel.w - imR - 30) bio.panel.im.l = $Bio.clamp((x - filmStrip.l) / bio.panel.w, 0, 1); if (y < imT + 30) break; - panelBio.im.b = $Bio.clamp((panelBio.h - y - filmStrip.b) / panelBio.h, 0, 1); + bio.panel.im.b = $Bio.clamp((bio.panel.h - y - filmStrip.b) / bio.panel.h, 0, 1); break; case 'se': - if (y > imT + 30) panelBio.im.b = $Bio.clamp((panelBio.h - y - filmStrip.b) / panelBio.h, 0, 1); + if (y > imT + 30) bio.panel.im.b = $Bio.clamp((bio.panel.h - y - filmStrip.b) / bio.panel.h, 0, 1); if (x < imL + 30) break; - panelBio.im.r = $Bio.clamp((panelBio.w - x - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.im.r = $Bio.clamp((bio.panel.w - x - filmStrip.r) / bio.panel.w, 0, 1); break; case 'right': if (x < imL + 30) break; - panelBio.im.r = $Bio.clamp((panelBio.w - x - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.im.r = $Bio.clamp((bio.panel.w - x - filmStrip.r) / bio.panel.w, 0, 1); break; case 'all': if (imT <= filmStrip.t && y - this.init_y < 0 || imB <= filmStrip.b && y - this.init_y > 0 || imL <= filmStrip.l && x - this.init_x < 0 || imR <= filmStrip.r && x - this.init_x > 0) break; imT += (y - this.y_init); - panelBio.im.t = $Bio.clamp((imT - filmStrip.t) / panelBio.h, 0, 1); - imB = panelBio.h - Math.max(imT, 0) - panelBio.ibox.h; - panelBio.im.b = $Bio.clamp((imB - filmStrip.b) / panelBio.h, 0, 1); + bio.panel.im.t = $Bio.clamp((imT - filmStrip.t) / bio.panel.h, 0, 1); + imB = bio.panel.h - Math.max(imT, 0) - bio.panel.ibox.h; + bio.panel.im.b = $Bio.clamp((imB - filmStrip.b) / bio.panel.h, 0, 1); imL += (x - this.x_init); - panelBio.im.l = $Bio.clamp((imL - filmStrip.l) / panelBio.w, 0, 1); - imR = panelBio.w - Math.max(imL, 0) - panelBio.ibox.w; - panelBio.im.r = $Bio.clamp((imR - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.im.l = $Bio.clamp((imL - filmStrip.l) / bio.panel.w, 0, 1); + imR = bio.panel.w - Math.max(imL, 0) - bio.panel.ibox.w; + bio.panel.im.r = $Bio.clamp((imR - filmStrip.r) / bio.panel.w, 0, 1); break; } this.sizes(true); break; } - case pptBio.style < 4: { - if (!vkBio.k('ctrl')) break; + case bioSet.style < 4: { + if (!bio.vk.k('ctrl')) break; if (!this.down) { - switch (pptBio.style) { + switch (bioSet.style) { case 0: - this.si = y > panelBio.img.t + panelBio.style.imgSize && y < panelBio.img.t + panelBio.style.imgSize + 5; + this.si = y > bio.panel.img.t + bio.panel.style.imgSize && y < bio.panel.img.t + bio.panel.style.imgSize + 5; if (this.si) window.SetCursor(32645); break; case 1: - this.si = x > panelBio.img.l - 5 && x < panelBio.img.l; + this.si = x > bio.panel.img.l - 5 && x < bio.panel.img.l; if (this.si) window.SetCursor(32644); break; case 2: - this.si = y > panelBio.img.t - 5 && y < panelBio.img.t; + this.si = y > bio.panel.img.t - 5 && y < bio.panel.img.t; if (this.si) window.SetCursor(32645); break; case 3: - this.si = x > panelBio.img.l + panelBio.style.imgSize && x < panelBio.img.l + panelBio.style.imgSize + 5; + this.si = x > bio.panel.img.l + bio.panel.style.imgSize && x < bio.panel.img.l + bio.panel.style.imgSize + 5; if (this.si) window.SetCursor(32644); break; } } if (!this.down || !this.si) return; const filmStrip = { - t: !pptBio.filmStripOverlay ? panelBio.filmStripSize.t : 0, - b: !pptBio.filmStripOverlay ? panelBio.filmStripSize.b : 0, - l: !pptBio.filmStripOverlay ? panelBio.filmStripSize.l : 0, - r: !pptBio.filmStripOverlay ? panelBio.filmStripSize.r : 0 + t: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.t : 0, + b: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.b : 0, + l: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.l : 0, + r: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.r : 0 }; - const ph = panelBio.h - filmStrip.t - filmStrip.b; - const pw = panelBio.w - filmStrip.l - filmStrip.r; - switch (pptBio.style) { + const ph = bio.panel.h - filmStrip.t - filmStrip.b; + const pw = bio.panel.w - filmStrip.l - filmStrip.r; + switch (bioSet.style) { case 0: - pptBio.rel_imgs = (pptBio.rel_imgs * ph + y - this.y_init) / ph; + bioSet.rel_imgs = (bioSet.rel_imgs * ph + y - this.y_init) / ph; break; case 1: - pptBio.rel_imgs = (pptBio.rel_imgs * pw + this.x_init - x) / pw; + bioSet.rel_imgs = (bioSet.rel_imgs * pw + this.x_init - x) / pw; break; case 2: - pptBio.rel_imgs = (pptBio.rel_imgs * ph + this.y_init - y) / ph; + bioSet.rel_imgs = (bioSet.rel_imgs * ph + this.y_init - y) / ph; break; case 3: - pptBio.rel_imgs = (pptBio.rel_imgs * pw + x - this.x_init) / pw; + bioSet.rel_imgs = (bioSet.rel_imgs * pw + x - this.x_init) / pw; break; } - pptBio.rel_imgs = $Bio.clamp(parseFloat(pptBio.rel_imgs.toFixed(15)), 0.1, 0.9); + bioSet.rel_imgs = $Bio.clamp(parseFloat(bioSet.rel_imgs.toFixed(15)), 0.1, 0.9); this.sizes(); break; } @@ -329,77 +329,77 @@ class ResizeHandler { } move(x, y) { - if (pptBio.style < 4 || pptBio.img_only || pptBio.text_only || !vkBio.k('ctrl') || vkBio.k('alt') || !this.focus || pptBio.style == 4) return; + if (bioSet.style < 4 || bioSet.img_only || bioSet.text_only || !bio.vk.k('ctrl') || bio.vk.k('alt') || !this.focus || bioSet.style == 4) return; if (!this.down) { - this.st = y > panelBio.tbox.t - 5 && y < panelBio.tbox.t + 5 && x > panelBio.tbox.l + 10 && x < panelBio.tbox.l + panelBio.tbox.w - 10 ? 'top' : - y > panelBio.tbox.t - 5 && y < panelBio.tbox.t + 15 && x > panelBio.tbox.l && x < panelBio.tbox.l + 10 ? 'nw' : - y > panelBio.tbox.t - 5 && y < panelBio.tbox.t + 15 && x > panelBio.tbox.l + panelBio.tbox.w - 10 && x < panelBio.tbox.l + panelBio.tbox.w ? 'ne' : - y > panelBio.tbox.t + panelBio.tbox.h - 5 && y < panelBio.tbox.t + panelBio.tbox.h + 5 && x > panelBio.tbox.l + 10 && x < panelBio.tbox.l + panelBio.tbox.w - 10 ? 'bottom' : - y > panelBio.tbox.t + panelBio.tbox.h - 15 && y < panelBio.tbox.t + panelBio.tbox.h + 5 && x > panelBio.tbox.l && x < panelBio.tbox.l + 10 ? 'sw' : - y > panelBio.tbox.t + panelBio.tbox.h - 15 && y < panelBio.tbox.t + panelBio.tbox.h + 5 && x > panelBio.tbox.l + panelBio.tbox.w - 10 && x < panelBio.tbox.l + panelBio.tbox.w ? 'se' : - y > panelBio.tbox.t + 10 && y < panelBio.tbox.t + panelBio.tbox.h && x > panelBio.tbox.l - 5 && x < panelBio.tbox.l + 5 ? 'left' : - y > panelBio.tbox.t + 10 && y < panelBio.tbox.t + panelBio.tbox.h && x > panelBio.tbox.l + panelBio.tbox.w - 5 && x < panelBio.tbox.l + panelBio.tbox.w + 5 ? 'right' : - y > panelBio.tbox.t + 20 && y < panelBio.tbox.t + panelBio.tbox.h - 20 && x > panelBio.tbox.l + 20 && x < panelBio.tbox.l + panelBio.tbox.w - 20 ? 'all' : ''; + this.st = y > bio.panel.tbox.t - 5 && y < bio.panel.tbox.t + 5 && x > bio.panel.tbox.l + 10 && x < bio.panel.tbox.l + bio.panel.tbox.w - 10 ? 'top' : + y > bio.panel.tbox.t - 5 && y < bio.panel.tbox.t + 15 && x > bio.panel.tbox.l && x < bio.panel.tbox.l + 10 ? 'nw' : + y > bio.panel.tbox.t - 5 && y < bio.panel.tbox.t + 15 && x > bio.panel.tbox.l + bio.panel.tbox.w - 10 && x < bio.panel.tbox.l + bio.panel.tbox.w ? 'ne' : + y > bio.panel.tbox.t + bio.panel.tbox.h - 5 && y < bio.panel.tbox.t + bio.panel.tbox.h + 5 && x > bio.panel.tbox.l + 10 && x < bio.panel.tbox.l + bio.panel.tbox.w - 10 ? 'bottom' : + y > bio.panel.tbox.t + bio.panel.tbox.h - 15 && y < bio.panel.tbox.t + bio.panel.tbox.h + 5 && x > bio.panel.tbox.l && x < bio.panel.tbox.l + 10 ? 'sw' : + y > bio.panel.tbox.t + bio.panel.tbox.h - 15 && y < bio.panel.tbox.t + bio.panel.tbox.h + 5 && x > bio.panel.tbox.l + bio.panel.tbox.w - 10 && x < bio.panel.tbox.l + bio.panel.tbox.w ? 'se' : + y > bio.panel.tbox.t + 10 && y < bio.panel.tbox.t + bio.panel.tbox.h && x > bio.panel.tbox.l - 5 && x < bio.panel.tbox.l + 5 ? 'left' : + y > bio.panel.tbox.t + 10 && y < bio.panel.tbox.t + bio.panel.tbox.h && x > bio.panel.tbox.l + bio.panel.tbox.w - 5 && x < bio.panel.tbox.l + bio.panel.tbox.w + 5 ? 'right' : + y > bio.panel.tbox.t + 20 && y < bio.panel.tbox.t + bio.panel.tbox.h - 20 && x > bio.panel.tbox.l + 20 && x < bio.panel.tbox.l + bio.panel.tbox.w - 20 ? 'all' : ''; this.setCursor(this.st); } if (!this.down || !this.st) return; const filmStrip = { - t: !pptBio.filmStripOverlay ? panelBio.filmStripSize.t : 0, - b: !pptBio.filmStripOverlay ? panelBio.filmStripSize.b : 0, - l: !pptBio.filmStripOverlay ? panelBio.filmStripSize.l : 0, - r: !pptBio.filmStripOverlay ? panelBio.filmStripSize.r : 0 + t: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.t : 0, + b: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.b : 0, + l: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.l : 0, + r: !bioSet.filmStripOverlay ? bio.panel.filmStripSize.r : 0 }; - let txT = Math.round(panelBio.tx.t * panelBio.h) + filmStrip.t; - let txB = Math.round(panelBio.tx.b * panelBio.h) + filmStrip.b; - let txL = Math.round(panelBio.tx.l * panelBio.w) + filmStrip.l; - let txR = Math.round(panelBio.tx.r * panelBio.w) + filmStrip.r; + let txT = Math.round(bio.panel.tx.t * bio.panel.h) + filmStrip.t; + let txB = Math.round(bio.panel.tx.b * bio.panel.h) + filmStrip.b; + let txL = Math.round(bio.panel.tx.l * bio.panel.w) + filmStrip.l; + let txR = Math.round(bio.panel.tx.r * bio.panel.w) + filmStrip.r; switch (this.st) { case 'top': - if (y > panelBio.h - txB - panelBio.style.minH) break; - panelBio.tx.t = $Bio.clamp((y - filmStrip.t) / panelBio.h, 0, 1); + if (y > bio.panel.h - txB - bio.panel.style.minH) break; + bio.panel.tx.t = $Bio.clamp((y - filmStrip.t) / bio.panel.h, 0, 1); break; case 'nw': - if (y < panelBio.h - txB - panelBio.style.minH) panelBio.tx.t = $Bio.clamp((y - filmStrip.t) / panelBio.h, 0, 1); - if (x > panelBio.w - txR - 30) break; - panelBio.tx.l = $Bio.clamp((x - filmStrip.l) / panelBio.w, 0, 1); + if (y < bio.panel.h - txB - bio.panel.style.minH) bio.panel.tx.t = $Bio.clamp((y - filmStrip.t) / bio.panel.h, 0, 1); + if (x > bio.panel.w - txR - 30) break; + bio.panel.tx.l = $Bio.clamp((x - filmStrip.l) / bio.panel.w, 0, 1); break; case 'ne': - if (y < panelBio.h - txB - panelBio.style.minH) panelBio.tx.t = $Bio.clamp((y - filmStrip.t) / panelBio.h, 0, 1); + if (y < bio.panel.h - txB - bio.panel.style.minH) bio.panel.tx.t = $Bio.clamp((y - filmStrip.t) / bio.panel.h, 0, 1); if (x < txL + 30) break; - panelBio.tx.r = $Bio.clamp((panelBio.w - x - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.tx.r = $Bio.clamp((bio.panel.w - x - filmStrip.r) / bio.panel.w, 0, 1); break; case 'left': - if (x > panelBio.w - txR - 30) break; - panelBio.tx.l = $Bio.clamp((x - filmStrip.l) / panelBio.w, 0, 1); + if (x > bio.panel.w - txR - 30) break; + bio.panel.tx.l = $Bio.clamp((x - filmStrip.l) / bio.panel.w, 0, 1); break; case 'bottom': - if (y < txT + panelBio.style.minH) break; - panelBio.tx.b = $Bio.clamp((panelBio.h - y - filmStrip.b) / panelBio.h, 0, 1); + if (y < txT + bio.panel.style.minH) break; + bio.panel.tx.b = $Bio.clamp((bio.panel.h - y - filmStrip.b) / bio.panel.h, 0, 1); break; case 'sw': - if (x < panelBio.w - txR - 30) panelBio.tx.l = $Bio.clamp((x - filmStrip.l) / panelBio.w, 0, 1); - if (y < txT + panelBio.style.minH) break; - panelBio.tx.b = $Bio.clamp((panelBio.h - y - filmStrip.b) / panelBio.h, 0, 1); + if (x < bio.panel.w - txR - 30) bio.panel.tx.l = $Bio.clamp((x - filmStrip.l) / bio.panel.w, 0, 1); + if (y < txT + bio.panel.style.minH) break; + bio.panel.tx.b = $Bio.clamp((bio.panel.h - y - filmStrip.b) / bio.panel.h, 0, 1); break; case 'se': - if (y > txT + panelBio.style.minH) panelBio.tx.b = $Bio.clamp((panelBio.h - y - filmStrip.b) / panelBio.h, 0, 1); + if (y > txT + bio.panel.style.minH) bio.panel.tx.b = $Bio.clamp((bio.panel.h - y - filmStrip.b) / bio.panel.h, 0, 1); if (x < txL + 30) break; - panelBio.tx.r = $Bio.clamp((panelBio.w - x - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.tx.r = $Bio.clamp((bio.panel.w - x - filmStrip.r) / bio.panel.w, 0, 1); break; case 'right': if (x < txL + 30) break; - panelBio.tx.r = $Bio.clamp((panelBio.w - x - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.tx.r = $Bio.clamp((bio.panel.w - x - filmStrip.r) / bio.panel.w, 0, 1); break; case 'all': if (txT <= filmStrip.t && y - this.init_y < 0 || txB <= filmStrip.b && y - this.init_y > 0 || txL <= filmStrip.l && x - this.init_x < 0 || txR <= filmStrip.r && x - this.init_x > 0) break; txT += (y - this.init_y); - panelBio.tx.t = $Bio.clamp((txT - filmStrip.t) / panelBio.h, 0, 1); + bio.panel.tx.t = $Bio.clamp((txT - filmStrip.t) / bio.panel.h, 0, 1); txL += (x - this.init_x); - panelBio.tx.l = $Bio.clamp((txL - filmStrip.l) / panelBio.w, 0, 1); - txB = panelBio.h - Math.max(txT, 0) - panelBio.tbox.h; - panelBio.tx.b = $Bio.clamp((txB - filmStrip.b) / panelBio.h, 0, 1); - txR = panelBio.w - Math.max(txL, 0) - panelBio.tbox.w; - panelBio.tx.r = $Bio.clamp((txR - filmStrip.r) / panelBio.w, 0, 1); + bio.panel.tx.l = $Bio.clamp((txL - filmStrip.l) / bio.panel.w, 0, 1); + txB = bio.panel.h - Math.max(txT, 0) - bio.panel.tbox.h; + bio.panel.tx.b = $Bio.clamp((txB - filmStrip.b) / bio.panel.h, 0, 1); + txR = bio.panel.w - Math.max(txL, 0) - bio.panel.tbox.w; + bio.panel.tx.r = $Bio.clamp((txR - filmStrip.r) / bio.panel.w, 0, 1); break; } this.sizes(true); @@ -413,8 +413,8 @@ class ResizeHandler { } sizes(bypass) { - panelBio.setStyle(bypass); - butBio.check(); - txt.paint(); + bio.panel.setStyle(bypass); + bio.but.check(); + bio.txt.paint(); } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-scrollbar.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-scrollbar.js index e8826b8f..9a58a1de 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-scrollbar.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-scrollbar.js @@ -1,6 +1,6 @@ 'use strict'; -class ScrollbarBio { +class BioScrollbar { constructor() { this.active = true; this.alpha = 255; @@ -55,7 +55,7 @@ class ScrollbarBio { }; this.narrow = { - show: pptBio.sbarShow == 1, + show: bioSet.sbarShow == 1, x: 0 }; @@ -91,8 +91,8 @@ class ScrollbarBio { this.duration = { drag: 200, - inertia: pptBio.durationTouchFlick, - full: pptBio.durationScroll + inertia: bioSet.durationTouchFlick, + full: bioSet.durationScroll }; this.duration.scroll = Math.round(this.duration.full * 0.8); @@ -122,7 +122,7 @@ class ScrollbarBio { return; } this.narrow.show = true; - if (pptBio.sbarShow == 1) butBio.setScrollBtnsHide(true, this.type); + if (bioSet.sbarShow == 1) bio.but.setScrollBtnsHide(true, this.type); this.scrollbar.cur_zone = this.scrollbar.zone; this.hover = false; this.cur_hover = false; @@ -148,7 +148,7 @@ class ScrollbarBio { const b = $Bio.clamp(new_scroll, 0, this.max_scroll); if (b == this.scroll) return; this.scroll = b; - if (pptBio.smooth) { + if (bioSet.smooth) { this.event = type || 'scroll'; this.start = this.delta; if (this.event != 'drag') { @@ -166,68 +166,68 @@ class ScrollbarBio { if (this.drawBar && this.active) { let sbar_x = this.x; let sbar_w = this.w; - if (pptBio.sbarShow == 1) { + if (bioSet.sbarShow == 1) { sbar_x = !this.narrow.show ? this.x : this.narrow.x; - sbar_w = !this.narrow.show ? this.w : uiBio.sbar.type == 1 ? 0 : SCALE(uiBio.narrowSbarWidth); + sbar_w = !this.narrow.show ? this.w : bio.ui.sbar.type == 1 ? 0 : SCALE(bio.ui.narrowSbarWidth); } // Non-Reborn/Random theme scrollbar colors - uiBio.col.sbarNormalRGBA = RGBtoRGBA(uiBio.col.sbarNormal, this.alpha2 + this.alpha); - uiBio.col.sbarHoveredRGBA = RGBtoRGBA(uiBio.col.sbarHovered, this.alpha); - uiBio.col.sbarDragRGBA = RGBtoRGBA(uiBio.col.sbarDrag, this.alpha2); + bio.ui.col.sbarNormalRGBA = RGBtoRGBA(bio.ui.col.sbarNormal, this.alpha2 + this.alpha); + bio.ui.col.sbarHoveredRGBA = RGBtoRGBA(bio.ui.col.sbarHovered, this.alpha); + bio.ui.col.sbarDragRGBA = RGBtoRGBA(bio.ui.col.sbarDrag, this.alpha2); // Reborn/Random theme scrollbar colors - black text color - uiBio.col.darkAccentRGBA_75 = RGBtoRGBA(col.darkAccent_75, this.alpha - 50); - uiBio.col.darkAccentRGBA_50 = this.hover ? RGBtoRGBA(col.lightAccent_50, this.alpha) : RGBtoRGBA(col.darkAccent_75, this.alpha); + bio.ui.col.darkAccentRGBA_75 = RGBtoRGBA(grCol.darkAccent_75, this.alpha - 50); + bio.ui.col.darkAccentRGBA_50 = this.hover ? RGBtoRGBA(grCol.lightAccent_50, this.alpha) : RGBtoRGBA(grCol.darkAccent_75, this.alpha); // Reborn/Random theme scrollbar colors - white text color - uiBio.col.lightAccentRGBA_80 = RGBtoRGBA(col.lightAccent_80, this.alpha); - uiBio.col.lightAccentRGBA_50 = RGBtoRGBA(col.lightAccent_50, this.alpha); - uiBio.col.lightAccentRGBA2_50 = RGBtoRGBA(col.lightAccent_50, this.alpha2); + bio.ui.col.lightAccentRGBA_80 = RGBtoRGBA(grCol.lightAccent_80, this.alpha); + bio.ui.col.lightAccentRGBA_50 = RGBtoRGBA(grCol.lightAccent_50, this.alpha); + bio.ui.col.lightAccentRGBA2_50 = RGBtoRGBA(grCol.lightAccent_50, this.alpha2); // Reborn/Random theme scrollbar colors - for nearly white album art - uiBio.col.lightAccentRGBA_100 = RGBA(140, 140, 140, this.alpha2 - this.alpha); - uiBio.col.lightAccentRGBA2_100 = RGBA(255, 255, 255, this.alpha); - uiBio.col.lightAccentRGBA3_100 = RGBA(255, 255, 255, this.alpha2); + bio.ui.col.lightAccentRGBA_100 = RGBA(140, 140, 140, this.alpha2 - this.alpha); + bio.ui.col.lightAccentRGBA2_100 = RGBA(255, 255, 255, this.alpha); + bio.ui.col.lightAccentRGBA3_100 = RGBA(255, 255, 255, this.alpha2); - let thumbColors = [uiBio.col.sbarNormalRGBA, uiBio.col.sbarHoveredRGBA, uiBio.col.sbarDragRGBA]; + let thumbColors = [bio.ui.col.sbarNormalRGBA, bio.ui.col.sbarHoveredRGBA, bio.ui.col.sbarDragRGBA]; - if (g_pl_colors.bg !== RGB(255, 255, 255) && !pref.styleRebornFusion && !pref.styleRebornFusion2) { - if ((pref.theme === 'reborn' || pref.theme === 'random') && lightBg) { - thumbColors = [uiBio.col.darkAccentRGBA_75, uiBio.col.darkAccentRGBA_50, uiBio.col.lightAccentRGBA2_50]; + if (pl.col.bg !== RGB(255, 255, 255) && !grSet.styleRebornFusion && !grSet.styleRebornFusion2) { + if ((grSet.theme === 'reborn' || grSet.theme === 'random') && grCol.lightBg) { + thumbColors = [bio.ui.col.darkAccentRGBA_75, bio.ui.col.darkAccentRGBA_50, bio.ui.col.lightAccentRGBA2_50]; } - else if ((pref.theme === 'reborn' || pref.theme === 'random') && !lightBg) { - thumbColors = [uiBio.col.lightAccentRGBA_80, uiBio.col.lightAccentRGBA_50, uiBio.col.lightAccentRGBA2_50]; + else if ((grSet.theme === 'reborn' || grSet.theme === 'random') && !grCol.lightBg) { + thumbColors = [bio.ui.col.lightAccentRGBA_80, bio.ui.col.lightAccentRGBA_50, bio.ui.col.lightAccentRGBA2_50]; } - if ((pref.theme === 'reborn' || pref.theme === 'random') && (imgBrightness > 230)) { - thumbColors = [uiBio.col.lightAccentRGBA_100, uiBio.col.lightAccentRGBA2_100, uiBio.col.lightAccentRGBA3_100]; + if ((grSet.theme === 'reborn' || grSet.theme === 'random') && (grCol.imgBrightness > 230)) { + thumbColors = [bio.ui.col.lightAccentRGBA_100, bio.ui.col.lightAccentRGBA2_100, bio.ui.col.lightAccentRGBA3_100]; } } // Theme Light scrollbar colors - if (pptBio.theme == 3) thumbColors = [RGBA(100, 100, 100, this.alpha2 + this.alpha), RGBA(60, 60, 60, this.alpha), RGBA(60, 60, 60, this.alpha2)]; + if (bioSet.theme == 3) thumbColors = [RGBA(100, 100, 100, this.alpha2 + this.alpha), RGBA(60, 60, 60, this.alpha), RGBA(60, 60, 60, this.alpha2)]; - gr.SetSmoothingMode(this.narrow.show || pptBio.sbarType == 2 || this.arc < 1 ? 3 : 4); - switch (uiBio.sbar.type) { + gr.SetSmoothingMode(this.narrow.show || bioSet.sbarType == 2 || this.arc < 1 ? 3 : 4); + switch (bio.ui.sbar.type) { case 0: if (this.arc > 0 && !this.narrow.show) gr.FillRoundRect(sbar_x - 0.5, this.y + this.bar.y, sbar_w, this.bar.h, this.arc, this.arc, this.narrow.show ? this.col[this.alpha2] : !this.bar.isDragging ? this.col[this.alpha] : this.col.max); - else gr.FillSolidRect(sbar_x + (this.narrow.show ? uiBio.sbar.narrowWidth + (RES_4K ? -4 : 1) : 0), this.y + this.bar.y, sbar_w, this.bar.h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); + else gr.FillSolidRect(sbar_x + (this.narrow.show ? bio.ui.sbar.narrowWidth + (RES._4K ? -4 : 1) : 0), this.y + this.bar.y, sbar_w, this.bar.h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); break; case 1: if (this.arc > 0 && !this.narrow.show) gr.FillRoundRect(sbar_x - 0.5, this.y + this.bar.y, sbar_w, this.bar.h, this.arc, this.arc, this.narrow.show ? this.col[this.alpha2] : !this.bar.isDragging ? this.col[this.alpha] : this.col.max); else gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); - if (pref.theme.startsWith('custom')) { - gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, uiBio.col.sbarHoveredRGBA); + if (grSet.theme.startsWith('custom')) { + gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, bio.ui.col.sbarHoveredRGBA); } break; case 2: // windows - switch (pptBio.sbarType) { + switch (bioSet.sbarType) { case 2: // light mode - uiBio.theme.SetPartAndStateID(6, 1); - if (!this.narrow.show || pptBio.sbarShow != 1) uiBio.theme.DrawThemeBackground(gr, sbar_x, this.y, sbar_w, this.h); - uiBio.theme.SetPartAndStateID(3, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3); - uiBio.theme.DrawThemeBackground(gr, sbar_x, this.y + this.bar.y, sbar_w, this.bar.h); + bio.ui.theme.SetPartAndStateID(6, 1); + if (!this.narrow.show || bioSet.sbarShow != 1) bio.ui.theme.DrawThemeBackground(gr, sbar_x, this.y, sbar_w, this.h); + bio.ui.theme.SetPartAndStateID(3, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3); + bio.ui.theme.DrawThemeBackground(gr, sbar_x, this.y + this.bar.y, sbar_w, this.bar.h); break; case 3: // dark mode - if (!this.narrow.show || pptBio.sbarShow != 1) gr.FillSolidRect(sbar_x, this.y - panelBio.sbar.offset, this.w, this.h + panelBio.sbar.offset * 2, this.col.bg); - if (this.arc > 0 && !this.narrow.show) gr.FillRoundRect(sbar_x + (this.narrow.show ? 0 : uiBio.style.l_w) - 0.5, this.y + this.bar.y, sbar_w - (this.narrow.show ? 0 : uiBio.style.l_w * 2), this.bar.h, this.arc, this.arc, this.narrow.show ? RGB(77, 77, 77) : !this.bar.isDragging ? this.col[this.alpha] : this.col.max); - else gr.FillSolidRect(sbar_x + (this.narrow.show ? 0 : uiBio.style.l_w), this.y + this.bar.y, sbar_w - (this.narrow.show ? 0 : uiBio.style.l_w * 2), this.bar.h, this.narrow.show ? RGB(77, 77, 77) : !this.bar.isDragging ? this.col[this.alpha] : this.col.max); + if (!this.narrow.show || bioSet.sbarShow != 1) gr.FillSolidRect(sbar_x, this.y - bio.panel.sbar.offset, this.w, this.h + bio.panel.sbar.offset * 2, this.col.bg); + if (this.arc > 0 && !this.narrow.show) gr.FillRoundRect(sbar_x + (this.narrow.show ? 0 : bio.ui.style.l_w) - 0.5, this.y + this.bar.y, sbar_w - (this.narrow.show ? 0 : bio.ui.style.l_w * 2), this.bar.h, this.arc, this.arc, this.narrow.show ? RGB(77, 77, 77) : !this.bar.isDragging ? this.col[this.alpha] : this.col.max); + else gr.FillSolidRect(sbar_x + (this.narrow.show ? 0 : bio.ui.style.l_w), this.y + this.bar.y, sbar_w - (this.narrow.show ? 0 : bio.ui.style.l_w * 2), this.bar.h, this.narrow.show ? RGB(77, 77, 77) : !this.bar.isDragging ? this.col[this.alpha] : this.col.max); break; } gr.SetSmoothingMode(0); @@ -248,14 +248,14 @@ class ScrollbarBio { lbtn_dn(p_x, p_y) { this.onSbar = false; - if ((!pptBio.sbarShow || panelBio.trace.film) && pptBio.touchControl) return this.tap(p_x, p_y); + if ((!bioSet.sbarShow || bio.panel.trace.film) && bioSet.touchControl) return this.tap(p_x, p_y); if (this.type == 'film') return; const x = p_x - this.x; const y = p_y - this.y; let dir; if (x > this.w || y < 0 || y > this.h || this.row.count <= this.rows_drawn) return; if (x < 0) { - if (!pptBio.touchControl) return; + if (!bioSet.touchControl) return; else return this.tap(p_x, p_y); } this.onSbar = true; @@ -265,7 +265,7 @@ class ScrollbarBio { if (y < this.bar.y || y > this.bar.y + this.bar.h) this.shiftPage(dir, this.nearest(y)); else { // on bar this.bar.isDragging = true; - butBio.Dn = true; + bio.but.Dn = true; this.paint(); this.initial.drag.y = y - this.bar.y + this.but_h; } @@ -277,7 +277,7 @@ class ScrollbarBio { clearInterval(this.touch.ticker); if (!this.touch.counter) this.track(true); if (Math.abs(this.touch.velocity) > this.touch.min && Date.now() - this.touch.startTime < 300) { - this.touch.amplitude = pptBio.flickDistance * this.touch.velocity * pptBio.touchStep; + this.touch.amplitude = bioSet.flickDistance * this.touch.velocity * bioSet.touchStep; this.touch.timestamp = Date.now(); this.checkScroll(Math.round((this.scroll + this.touch.amplitude) / this.row.h) * this.row.h, 'inertia'); } @@ -285,16 +285,16 @@ class ScrollbarBio { } lbtn_up() { // not called by film type - if (panelBio.clicked) { - if (pptBio.sbarShow == 1 && this.narrow.show) butBio.setScrollBtnsHide(true, this.type); + if (bio.panel.clicked) { + if (bioSet.sbarShow == 1 && this.narrow.show) bio.but.setScrollBtnsHide(true, this.type); return; } - if (butBio.Dn == 'heading') return; + if (bio.but.Dn == 'heading') return; if (!this.hover && this.bar.isDragging) this.transitionPaint(); else this.paint(); if (this.bar.isDragging) { this.bar.isDragging = false; - butBio.Dn = false; + bio.but.Dn = false; } this.initial.drag.y = 0; if (this.timer_but) { @@ -306,8 +306,8 @@ class ScrollbarBio { leave() { if (this.touch.dn) this.touch.dn = false; - if (!menBio.right_up) this.scrollbar.zone = false; - if (this.bar.isDragging || pptBio.sbarShow == 1 || this.type == 'film') return; + if (!bio.men.right_up) this.scrollbar.zone = false; + if (this.bar.isDragging || bioSet.sbarShow == 1 || this.type == 'film') return; this.hover = !this.hover; this.transitionPaint(); this.hover = false; @@ -322,14 +322,14 @@ class ScrollbarBio { this.rows_drawn = rows_drawn; this.row.h = row_h; this.horizontal = horizontal; - this.but_h = uiBio.sbar.but_h; - this.scrollStep = $Bio.clamp(pptBio.scrollStep, 0, 10); + this.but_h = bio.ui.sbar.but_h; + this.scrollStep = $Bio.clamp(bioSet.scrollStep, 0, 10); if (this.type == 'film' && this.scrollStep != 0) this.scrollStep = Math.max(Math.round(this.scrollStep /= 3), 1); // draw info this.scrollbar.height = Math.round(this.h - this.but_h * 2); - this.bar.h = Math.max(Math.round(this.scrollbar.height * this.rows_drawn / this.row.count), $Bio.clamp(this.scrollbar.height / 2, 5, pptBio.sbarShow == 2 ? pptBio.sbarGripHeight : pptBio.sbarGripHeight * 2)); - let min_w = Math.min(this.w, this.bar.h); if (pptBio.sbarType == 3) min_w -= uiBio.style.l_w * 2; - this.arc = !pptBio.sbarGripRounded ? 0 : Math.floor(min_w / 2); + this.bar.h = Math.max(Math.round(this.scrollbar.height * this.rows_drawn / this.row.count), $Bio.clamp(this.scrollbar.height / 2, 5, bioSet.sbarShow == 2 ? bioSet.sbarGripHeight : bioSet.sbarGripHeight * 2)); + let min_w = Math.min(this.w, this.bar.h); if (bioSet.sbarType == 3) min_w -= bio.ui.style.l_w * 2; + this.arc = !bioSet.sbarGripRounded ? 0 : Math.floor(min_w / 2); this.scrollbar.travel = this.scrollbar.height - this.bar.h; // scrolling info this.scrollable_lines = this.rows_drawn > 0 ? this.row.count - this.rows_drawn : 0; @@ -338,9 +338,9 @@ class ScrollbarBio { this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); this.drag_distance_per_row = this.scrollbar.travel / this.scrollable_lines; // panel info - this.narrow.x = this.x + this.w - $Bio.clamp(uiBio.narrowSbarWidth, 5, this.w) - (pptBio.sbarType > 1 ? 1 : 0); + this.narrow.x = this.x + this.w - $Bio.clamp(bio.ui.narrowSbarWidth, 5, this.w) - (bioSet.sbarType > 1 ? 1 : 0); this.max_scroll = this.scrollable_lines * this.row.h; - if (pptBio.sbarShow != 1) butBio.setScrollBtnsHide(); + if (bioSet.sbarShow != 1) bio.but.setScrollBtnsHide(); } move(p_x, p_y) { @@ -350,34 +350,34 @@ class ScrollbarBio { if (this.type != 'film' && x >= 0 && x <= this.w && y >= 0 && y <= this.h) { this.scrollbar.zone = true; this.narrow.show = false; - if (pptBio.sbarShow == 1 && this.scrollbar.zone != this.scrollbar.cur_zone) { - butBio.setScrollBtnsHide(!this.scrollbar.zone || this.scrollable_lines < 1 || pptBio.img_only || txt.lyricsDisplayed(), this.type); + if (bioSet.sbarShow == 1 && this.scrollbar.zone != this.scrollbar.cur_zone) { + bio.but.setScrollBtnsHide(!this.scrollbar.zone || this.scrollable_lines < 1 || bioSet.img_only || bio.txt.lyricsDisplayed(), this.type); this.scrollbar.cur_zone = this.scrollbar.zone; } this.paint(); // Needed for mouse_hover scrollbar height, not scrollbar thumb } else this.scrollbar.zone = false; - if (pptBio.sbarShow == 1) { + if (bioSet.sbarShow == 1) { this.minimiseDebounce(); this.hideDebounce(); } - if (pptBio.touchControl) { + if (bioSet.touchControl) { const delta = this.touch.reference - (this.horizontal ? p_x : p_y); if (delta > this.touch.diff || delta < -this.touch.diff) { this.touch.reference = this.horizontal ? p_x : p_y; - if (pptBio.flickDistance) this.touch.offset = $Bio.clamp(this.touch.offset + delta, 0, this.max_scroll); - if (this.touch.dn) uiBio.id.touch_dn = -1; + if (bioSet.flickDistance) this.touch.offset = $Bio.clamp(this.touch.offset + delta, 0, this.max_scroll); + if (this.touch.dn) bio.ui.id.touch_dn = -1; } } if (this.touch.dn) { - if (butBio.trace('lookUp', panelBio.m.x, panelBio.m.y) || this.type == 'film' && !panelBio.trace.film || this.type != 'film' && !panelBio.trace.text) return; + if (bio.but.trace('lookUp', bio.panel.m.x, bio.panel.m.y) || this.type == 'film' && !bio.panel.trace.film || this.type != 'film' && !bio.panel.trace.text) return; const now = Date.now(); if (now - this.touch.startTime > 300) this.touch.startTime = now; this.touch.lastDn = now; - this.checkScroll(this.initial.scr + (this.horizontal ? this.initial.x - p_x : this.initial.y - p_y) * pptBio.touchStep, pptBio.touchStep == 1 ? 'drag' : 'scroll'); + this.checkScroll(this.initial.scr + (this.horizontal ? this.initial.x - p_x : this.initial.y - p_y) * bioSet.touchStep, bioSet.touchStep == 1 ? 'drag' : 'scroll'); return; } if (this.type != 'film') { - this.hover = !(x < 0 || x > this.w || y > this.bar.y + this.bar.h || y < this.bar.y || butBio.Dn); + this.hover = !(x < 0 || x > this.w || y > this.bar.y + this.bar.h || y < this.bar.y || bio.but.Dn); if (!this.bar.timer && (this.hover != this.cur_hover || this.active != this.cur_active)) { this.init = false; this.transitionPaint(); @@ -402,7 +402,7 @@ class ScrollbarBio { if (Elapsed > Duration) return End; if (Event == 'drag') return; const n = Elapsed / Duration; - return Start + (End - Start) * easeBio[Event](n); + return Start + (End - Start) * bioEase[Event](n); } reset() { @@ -413,12 +413,12 @@ class ScrollbarBio { resetAuto() { this.minimiseDebounce.cancel(); this.hideDebounce.cancel(); - if (!pptBio.sbarShow) butBio.setScrollBtnsHide(true); - if (pptBio.sbarShow == 1) { - butBio.setScrollBtnsHide(true, 'both'); + if (!bioSet.sbarShow) bio.but.setScrollBtnsHide(true); + if (bioSet.sbarShow == 1) { + bio.but.setScrollBtnsHide(true, 'both'); this.narrow.show = true; } - if (pptBio.sbarShow == 2) this.narrow.show = false; + if (bioSet.sbarShow == 2) this.narrow.show = false; } scrollDrag() { @@ -430,22 +430,22 @@ class ScrollbarBio { if (!this.draw_timer) return; this.delta = this.scroll; this.scrollTo(); - txt.logScrollPos(); - filmStrip.logScrollPos(); + bio.txt.logScrollPos(); + bio.filmStrip.logScrollPos(); clearTimeout(this.draw_timer); this.draw_timer = null; } scrollTimer() { this.draw_timer = setInterval(() => { - if (panelBio.w < 1 || !window.IsVisible) return; + if (bio.panel.w < 1 || !window.IsVisible) return; this.smoothScroll(); }, 16); } scrollTo() { this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); - this.type != 'film' ? panelBio.text_paint() : filmStrip.paint(); + this.type != 'film' ? bio.panel.text_paint() : bio.filmStrip.paint(); } scrollToEnd() { @@ -454,47 +454,47 @@ class ScrollbarBio { setCol() { // not called by film type this.alpha = - pptBio.sbarType == 3 ? 140 : - !uiBio.sbar.col ? 75 : - pref.theme.startsWith('custom') ? 0 : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? 220 : - pref.styleBlackAndWhite ? 175 : - pref.styleBlackAndWhite2 ? 152 : - pref.styleBlend ? 175 : + bioSet.sbarType == 3 ? 140 : + !bio.ui.sbar.col ? 75 : + grSet.theme.startsWith('custom') ? 0 : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? 220 : + grSet.styleBlackAndWhite ? 175 : + grSet.styleBlackAndWhite2 ? 152 : + grSet.styleBlend ? 175 : 100; this.alpha1 = this.alpha; - this.alpha2 = pptBio.sbarType == 3 ? 255 : !uiBio.sbar.col ? 128 : 255; - this.inStep = uiBio.sbar.type && uiBio.sbar.col ? 12 : 18; - switch (uiBio.sbar.type) { + this.alpha2 = bioSet.sbarType == 3 ? 255 : !bio.ui.sbar.col ? 128 : 255; + this.inStep = bio.ui.sbar.type && bio.ui.sbar.col ? 12 : 18; + switch (bio.ui.sbar.type) { case 0: - switch (uiBio.sbar.col) { + switch (bio.ui.sbar.col) { case 0: - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, this.alpha + i); - this.col.max = RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, 192); + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, this.alpha + i); + this.col.max = RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, 192); break; case 1: - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = uiBio.col.text & RGBA(255, 255, 255, this.alpha + i); - this.col.max = uiBio.col.text & 0x99ffffff; + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = bio.ui.col.text & RGBA(255, 255, 255, this.alpha + i); + this.col.max = bio.ui.col.text & 0x99ffffff; break; } break; case 1: - switch (uiBio.sbar.col) { + switch (bio.ui.sbar.col) { case 0: - this.col.bg = RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, 15); - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, this.alpha + i); - this.col.max = RGBA(uiBio.col.t, uiBio.col.t, uiBio.col.t, 192); + this.col.bg = RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, 15); + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, this.alpha + i); + this.col.max = RGBA(bio.ui.col.t, bio.ui.col.t, bio.ui.col.t, 192); break; case 1: - this.col.bg = uiBio.col.text & 0x15ffffff; - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = uiBio.col.text & RGBA(255, 255, 255, this.alpha + i); - this.col.max = uiBio.col.text & 0x99ffffff; + this.col.bg = bio.ui.col.text & 0x15ffffff; + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = bio.ui.col.text & RGBA(255, 255, 255, this.alpha + i); + this.col.max = bio.ui.col.text & 0x99ffffff; break; } break; } - if (pptBio.sbarType == 3) { // dark mode + if (bioSet.sbarType == 3) { // dark mode this.col.bg = RGB(23, 23, 23); for (let i = 0; i < 116; i++) { this.col[this.alpha + i] = RGBA(122, 122, 122, 140 + i); @@ -510,7 +510,7 @@ class ScrollbarBio { this.scroll = b; this.delta = this.scroll; this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); - this.type != 'film' ? panelBio.text_paint() : filmStrip.paint(); + this.type != 'film' ? bio.panel.text_paint() : bio.filmStrip.paint(); } setRows(row_count) { @@ -543,8 +543,8 @@ class ScrollbarBio { tap(p_x, p_y) { if (this.type == 'film') { - if (!panelBio.trace.film) return; - } else if (!panelBio.trace.text) return; + if (!bio.panel.trace.film) return; + } else if (!bio.panel.trace.text) return; if (this.touch.amplitude) { this.clock = 0; this.scroll = this.delta; @@ -560,7 +560,7 @@ class ScrollbarBio { if (!this.touch.offset) this.touch.offset = p_y; } this.touch.velocity = this.touch.amplitude = 0; - if (!pptBio.flickDistance) return; + if (!bioSet.flickDistance) return; this.touch.frame = this.touch.offset; this.touch.startTime = this.touch.timestamp = Date.now(); clearInterval(this.touch.ticker); @@ -572,8 +572,8 @@ class ScrollbarBio { this.touch.counter++; const now = Date.now(); if (now - this.touch.lastDn < 10000 && this.touch.counter == 4) { - uiBio.id.touch_dn = -1; - panelBio.id.last_pressed_coord = { + bio.ui.id.touch_dn = -1; + bio.panel.id.last_pressed_coord = { x: -1, y: -1 }; diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-server.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-server.js index 0ad7ec04..5062b07a 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-server.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-server.js @@ -1,6 +1,6 @@ 'use strict'; -class ServerBio { +class BioServer { constructor() { if (!$Bio.server) return; this.albm = ''; @@ -10,21 +10,21 @@ class ServerBio { this.artistMbid = {}; this.artistQid = {}; this.auto_corr = 1; - this.bioCache = `${cfg.storageFolder}cache_bio.json`; + this.bioCache = `${bioCfg.storageFolder}cache_bio.json`; this.disambig = ''; - this.exp = Math.max(panelBio.d * cfg.exp / 28, panelBio.d); - if (!this.exp || isNaN(this.exp)) this.exp = panelBio.d; + this.exp = Math.max(bio.panel.d * bioCfg.exp / 28, bio.panel.d); + if (!this.exp || isNaN(this.exp)) this.exp = bio.panel.d; this.imgToRecycle = []; if ($Bio.file(this.bioCache)) this.imgToRecycle = $Bio.jsonParse(this.bioCache, false, 'file'); this.langFallback = false; this.lastGetTrack = Date.now(); - this.notFound = `${cfg.storageFolder}update_bio.json`; - if (pptBio.updateNotFound) { + this.notFound = `${bioCfg.storageFolder}update_bio.json`; + if (bioSet.updateNotFound) { $Bio.save(this.notFound, JSON.stringify([{ name: 'update', time: Date.now() }], null, 3), true); - pptBio.updateNotFound = false; + bioSet.updateNotFound = false; } this.similar = ['Similar Artists: ', '\u00c4hnliche K\u00fcnstler: ', 'Artistas Similares: ', 'Artistes Similaires: ', 'Artisti Simili: ', '\u4f3c\u3066\u3044\u308b\u30a2\u30fc\u30c6\u30a3\u30b9\u30c8: ', 'Podobni Wykonawcy: ', 'Artistas Parecidos: ', '\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438: ', 'Liknande Artister: ', 'Benzer Sanat\u00e7\u0131lar: ', '\u76f8\u4f3c\u827a\u672f\u5bb6: ']; this.urlRequested = {}; @@ -37,11 +37,11 @@ class ServerBio { }; this.lfm = { def_EN: false, - server: cfg.language.toLowerCase() + server: bioCfg.language.toLowerCase() }; this.url = { am: 'https://www.allmusic.com/search/', - lfm: `https://ws.audioscrobbler.com/2.0/?format=json${panelBio.lfm}`, + lfm: `https://ws.audioscrobbler.com/2.0/?format=json${bio.panel.lfm}`, lfm_sf: 'https://www.songfacts.com/', mb: 'https://musicbrainz.org/ws/2/', wikidata: 'https://www.wikidata.org/w/api.php?action=wbgetentities&utf8&format=json&props=claims|sitelinks/urls&ids=', @@ -103,14 +103,14 @@ class ServerBio { if (tr.artist + tr.title == this.id.track_1 && !tr.force && !tr.menu || tr.artist == '' || tr.title == '') track_done = true; else this.id.track_1 = tr.artist + tr.title; - if ((cfg.dlAmRev || cfg.dlLfmRev || cfg.dlWikiRev) && !track_done) { - if (pptBio.showTrackRevOptions) this.getTrack(tr); + if ((bioCfg.dlAmRev || bioCfg.dlLfmRev || bioCfg.dlWikiRev) && !track_done) { + if (bioSet.showTrackRevOptions) this.getTrack(tr); else window.NotifyOthers('bio_chkTrackRev', tr); } } createImgDlFile() { - const n = `${cfg.storageFolder}foo_lastfm_img.vbs`; + const n = `${bioCfg.storageFolder}foo_lastfm_img.vbs`; if (!$Bio.file(n)) { const dl_im = 'If (WScript.Arguments.Count <> 2) Then\r\nWScript.Quit\r\nEnd If\r\n\r\nurl = WScript.Arguments(0)\r\nfile = WScript.Arguments(1)\r\n\r\nSet objFSO = Createobject("Scripting.FileSystemObject")\r\nIf objFSO.Fileexists(file) Then\r\nSet objFSO = Nothing\r\nWScript.Quit\r\nEnd If\r\n\r\nSet objXMLHTTP = CreateObject("MSXML2.XMLHTTP")\r\nobjXMLHTTP.open "GET", url, false\r\nobjXMLHTTP.send()\r\n\r\nIf objXMLHTTP.Status = 200 Then\r\nSet objADOStream = CreateObject("ADODB.Stream")\r\nobjADOStream.Open\r\nobjADOStream.Type = 1\r\nobjADOStream.Write objXMLHTTP.ResponseBody\r\nobjADOStream.Position = 0\r\nobjADOStream.SaveToFile file\r\nobjADOStream.Close\r\nSet objADOStream = Nothing\r\nEnd If\r\n\r\nSet objFSO = Nothing\r\nSet objXMLHTTP = Nothing'; @@ -123,7 +123,7 @@ class ServerBio { const m = $Bio.jsonParse(this.notFound, false, 'file'); const n = Date.now(); const r = n - exp; - const u = n - panelBio.d / 28; + const u = n - bio.panel.d / 28; let k = m.length; if (m.length && m[0].time < u) { while (k--) { @@ -148,7 +148,7 @@ class ServerBio { let langOK = false; for (i = 0; i < this.similar.length; i++) { if (langCheck.includes(this.similar[i])) { - if (i == cfg.lang.ix) langOK = true; + if (i == bioCfg.lang.ix) langOK = true; else if (this.langFallback && !i) langOK = true; if (!langOK) return true; break; @@ -156,7 +156,7 @@ class ServerBio { } for (i = 0; i < listeners.length; i++) { if (langCheck.includes(listeners[i])) { - if (i == cfg.lang.ix) langOK = true; + if (i == bioCfg.lang.ix) langOK = true; else if (this.langFallback && !i) langOK = true; if (!langOK) return true; break; @@ -168,7 +168,7 @@ class ServerBio { let langOK = false; for (i = 0; i < releaseDate.length; i++) { if (langCheck.includes(releaseDate[i])) { - if (i == cfg.lang.ix) langOK = true; + if (i == bioCfg.lang.ix) langOK = true; else if (this.langFallback && !i) langOK = true; if (!langOK) return true; break; @@ -176,7 +176,7 @@ class ServerBio { } for (i = 0; i < listeners.length; i++) { if (langCheck.includes(listeners[i])) { - if (i == cfg.lang.ix) langOK = true; + if (i == bioCfg.lang.ix) langOK = true; else if (this.langFallback && !i) langOK = true; if (!langOK) return true; break; @@ -186,7 +186,7 @@ class ServerBio { } case 2: { let langOK = false; - if (langCheck.includes(`Wikipedia language: ${cfg.language}`)) langOK = true; + if (langCheck.includes(`Wikipedia language: ${bioCfg.language}`)) langOK = true; else if (this.langFallback && langCheck.includes('Wikipedia language: EN')) langOK = true; if (!langOK) return true; break; @@ -226,45 +226,45 @@ class ServerBio { getBio(force, art, type) { const stndBio = !art.ix || art.ix + 1 > art.arr.length; let artist_done = false; - const new_artist = stndBio ? name.artist(art.focus, true) : art.arr[art.ix].name; + const new_artist = stndBio ? bio.name.artist(art.focus, true) : art.arr[art.ix].name; if (new_artist == this.artist && !force || new_artist == '') artist_done = true; else this.artist = new_artist; - const album = name.album(art.focus, true); - let title = name.title(art.focus, true); + const album = bio.name.album(art.focus, true); + let title = bio.name.title(art.focus, true); - const supCache = !stndBio ? cfg.supCache && !libBio.inLibrary(0, this.artist) : false; + const supCache = !stndBio ? bioCfg.supCache && !bio.lib.inLibrary(0, this.artist) : false; - if (this.expired(`${cfg.storageFolder}lastfm_genre_whitelist.json`, this.exp) || tagBio.genres.length < 701 || force) { - const lfm_genres = new DldLastfmGenresWhitelist(() => lfm_genres.onStateChange()); + if (this.expired(`${bioCfg.storageFolder}lastfm_genre_whitelist.json`, this.exp) || bio.tag.genres.length < 701 || force) { + const lfm_genres = new BioDldLastfmGenresWhitelist(() => lfm_genres.onStateChange()); lfm_genres.search(); } switch (type) { case 0: { - if (cfg.dlLfmBio && !artist_done) { - const lfm_bio = panelBio.getPth('bio', art.focus, this.artist, '', stndBio, supCache, $Bio.clean(this.artist), '', '', 'foLfmBio', true, true); + if (bioCfg.dlLfmBio && !artist_done) { + const lfm_bio = bio.panel.getPth('bio', art.focus, this.artist, '', stndBio, supCache, $Bio.clean(this.artist), '', '', 'foLfmBio', true, true); const text = $Bio.open(lfm_bio.pth); const custBio = text.includes('Custom Biography'); if (this.expired(lfm_bio.pth, this.exp, '', text, 0) && !custBio || force == 2 && !custBio || force == 1) { - const dl_lfm_bio = new DldLastfmBio(() => dl_lfm_bio.onStateChange()); + const dl_lfm_bio = new BioDldLastfm(() => dl_lfm_bio.onStateChange()); dl_lfm_bio.search(this.artist, lfm_bio.fo, lfm_bio.pth, force); } } - if (cfg.dlWikiBio) { + if (bioCfg.dlWikiBio) { const new_disambig = `${title} - ${album}`; let disambig_done = false; if (new_disambig == this.disambig && !force || new_disambig == '') disambig_done = true; else this.disambig = new_disambig; if (stndBio && (!artist_done || !disambig_done) || !stndBio && !artist_done) { - const wiki_bio = panelBio.getPth('bio', art.focus, this.artist, '', stndBio, supCache, $Bio.clean(this.artist), '', '', 'foWikiBio', true, true); + const wiki_bio = bio.panel.getPth('bio', art.focus, this.artist, '', stndBio, supCache, $Bio.clean(this.artist), '', '', 'foWikiBio', true, true); const text = $Bio.open(wiki_bio.pth); const custBio = text.includes('Custom Biography'); if (this.expired(wiki_bio.pth, this.exp, '', text, 2) && !custBio || force == 2 && !custBio || force == 1) { - const dl_wiki_bio = new DldWikipedia(() => dl_wiki_bio.onStateChange()); + const dl_wiki_bio = new BioDldWikipedia(() => dl_wiki_bio.onStateChange()); dl_wiki_bio.search(0, this.artist, '', album, title, stndBio ? 0 : '!stndBio', wiki_bio.fo, wiki_bio.pth, art.focus, force); } } @@ -279,13 +279,13 @@ class ServerBio { }); if (!artist_done) { - if (cfg.dlArtImg) { - const dl_art = new DldArtImages(); + if (bioCfg.dlArtImg) { + const dl_art = new BioDldArtImages(); dl_art.run(this.artist, force, art, stndBio, supCache); - } else timerBio.decelerating(); + } else bio.timer.decelerating(); - if (cfg.lfmSim && stndBio) { - const fo_sim = !panelBio.isRadio(art.focus) ? panelBio.cleanPth(cfg.pth.foLfmSim, art.focus, 'server') : panelBio.cleanPth(cfg.remap.foLfmSim, art.focus, 'remap', this.artist, '', 1); + if (bioCfg.lfmSim && stndBio) { + const fo_sim = !bio.panel.isRadio(art.focus) ? bio.panel.cleanPth(bioCfg.pth.foLfmSim, art.focus, 'server') : bio.panel.cleanPth(bioCfg.remap.foLfmSim, art.focus, 'remap', this.artist, '', 1); const pth_sim = `${fo_sim + $Bio.clean(this.artist)} And Similar Artists.json`; let len = 0; let valid = false; @@ -298,25 +298,25 @@ class ServerBio { } if (this.expired(pth_sim, this.exp) || !valid || force) { - const dl_lfm_sim = new LfmSimilarArtists(() => dl_lfm_sim.onStateChange()); + const dl_lfm_sim = new BioLfmSimilarArtists(() => dl_lfm_sim.onStateChange()); dl_lfm_sim.search(this.artist, '', '', len > 115 ? 249 : 100, fo_sim, pth_sim); } } - if (stndBio && cfg.photoLimit && !panelBio.lock) { // purge imgToRecycle + if (stndBio && bioCfg.photoLimit && !bio.panel.lock) { // purge imgToRecycle let j = this.imgToRecycle.length; while (j--) { - if (this.imgToRecycle[j].a != name.artist(true) && this.imgToRecycle[j].a != name.artist(false)) { + if (this.imgToRecycle[j].a != bio.name.artist(true) && this.imgToRecycle[j].a != bio.name.artist(false)) { try { if ($Bio.file(this.imgToRecycle[j].p)) { - $Bio.create(cfg.photoRecycler); - const fn = `${fsoBio.GetBaseName(this.imgToRecycle[j].p)}.jpg`; - if (!$Bio.file(cfg.photoRecycler + fn)) fsoBio.MoveFile(this.imgToRecycle[j].p, cfg.photoRecycler); + $Bio.create(bioCfg.photoRecycler); + const fn = `${bioFSO.GetBaseName(this.imgToRecycle[j].p)}.jpg`; + if (!$Bio.file(bioCfg.photoRecycler + fn)) bioFSO.MoveFile(this.imgToRecycle[j].p, bioCfg.photoRecycler); else { for (let i = 0; i < 100; i++) { const new_fn = fn.replace('.jpg', `_${i}.jpg`); - if (!$Bio.file(cfg.photoRecycler + new_fn)) { - fsoBio.MoveFile(this.imgToRecycle[j].p, cfg.photoRecycler + new_fn); + if (!$Bio.file(bioCfg.photoRecycler + new_fn)) { + bioFSO.MoveFile(this.imgToRecycle[j].p, bioCfg.photoRecycler + new_fn); break; } } @@ -332,13 +332,13 @@ class ServerBio { break; } case 1: { - if (!cfg.dlAmBio || !this.artist) return; + if (!bioCfg.dlAmBio || !this.artist) return; if (!stndBio) title = ''; - const am_bio = panelBio.getPth('bio', art.focus, this.artist, '', stndBio, supCache, $Bio.clean(this.artist), '', '', 'foAmBio', true, true); + const am_bio = bio.panel.getPth('bio', art.focus, this.artist, '', stndBio, supCache, $Bio.clean(this.artist), '', '', 'foAmBio', true, true); - if (force || this.expired(am_bio.pth, this.exp, `Bio ${cfg.partialMatch} ${this.artist} - ${title}`, false) && !$Bio.open(am_bio.pth).includes('Custom Biography')) { - const dl_am_bio = new DldAllmusicBio(); - const url = title ? `${serverBio.url.am}songs/${encodeURIComponent(`${title} ${this.artist}`)}` : `${serverBio.url.am}artists/${encodeURIComponent(this.artist)}`; + if (force || this.expired(am_bio.pth, this.exp, `Bio ${bioCfg.partialMatch} ${this.artist} - ${title}`, false) && !$Bio.open(am_bio.pth).includes('Custom Biography')) { + const dl_am_bio = new BioDldAllmusic(); + const url = title ? `${bio.server.url.am}songs/${encodeURIComponent(`${title} ${this.artist}`)}` : `${bio.server.url.am}artists/${encodeURIComponent(this.artist)}`; dl_am_bio.init(url, 'https://allmusic.com', title, this.artist, am_bio.fo, am_bio.pth, force); } break; @@ -352,25 +352,25 @@ class ServerBio { const g_img = utils.GetAlbumArtV2(handle, 0, false); if (g_img) return; const covCanBeSaved = !handle.RawPath.startsWith('fy+') && !handle.RawPath.startsWith('3dydfy:') && !handle.RawPath.startsWith('http'); - const sw = cfg.dlLfmCov && covCanBeSaved ? 1 : cfg.dlRevImg ? 0 : 2; + const sw = bioCfg.dlLfmCov && covCanBeSaved ? 1 : bioCfg.dlRevImg ? 0 : 2; let lfm_cov; switch (sw) { case 1: { // cover - const cov = panelBio.getPth('cov', alb.focus, 'server'); + const cov = bio.panel.getPth('cov', alb.focus, 'server'); if (this.done(`${this.albumArtist} - ${this.album} ${this.auto_corr} ${cov.pth}`, this.exp) && !force) return; - if (imgBio.chkPths([cov.pth], '', 2)) return; - if (cfg.cusCov && imgBio.chkPths(cfg.cusCovPaths, '', 2, true)) return; - lfm_cov = new LfmAlbum(() => lfm_cov.onStateChange()); + if (bio.img.chkPths([cov.pth], '', 2)) return; + if (bioCfg.cusCov && bio.img.chkPths(bioCfg.cusCovPaths, '', 2, true)) return; + lfm_cov = new BioLfmAlbum(() => lfm_cov.onStateChange()); lfm_cov.search(this.albumArtist, this.album, false, cov.fo, cov.pth, this.albm, force, false); break; } case 0: { // rev_img - const rev_img = panelBio.getPth('img', alb.focus, this.albumArtist, this.album); + const rev_img = bio.panel.getPth('img', alb.focus, this.albumArtist, this.album); if (this.done(`${this.albumArtist} - ${this.album} ${this.auto_corr} ${rev_img.pth}`, this.exp) && !force) return; - if (imgBio.chkPths([rev_img.pth, panelBio.getPth('cov', alb.focus).pth], '', 2)) return; - if (cfg.cusCov && imgBio.chkPths(cfg.cusCovPaths, '', 2, true)) return; - lfm_cov = new LfmAlbum(() => lfm_cov.onStateChange()); + if (bio.img.chkPths([rev_img.pth, bio.panel.getPth('cov', alb.focus).pth], '', 2)) return; + if (bioCfg.cusCov && bio.img.chkPths(bioCfg.cusCovPaths, '', 2, true)) return; + lfm_cov = new BioLfmAlbum(() => lfm_cov.onStateChange()); lfm_cov.search(this.albumArtist, this.album, false, rev_img.fo, rev_img.pth, this.albm, force, true); break; } @@ -392,15 +392,15 @@ class ServerBio { getRev(force, art, alb, onlyForceLfm) { // also gets bios that depend on rev const stndAlb = !alb.ix || alb.ix + 1 > alb.arr.length; - const new_album_id = stndAlb ? $Bio.eval(cfg.tf.albumArtist + cfg.tf.album, alb.focus, true) : alb.arr[alb.ix].artist + alb.arr[alb.ix].album; - const new_composition_id = stndAlb ? $Bio.eval(cfg.tf.albumArtist + cfg.tf.composition, alb.focus, true) : ''; - const new_title_id = name.title(art.focus, true); + const new_album_id = stndAlb ? $Bio.eval(bioCfg.tf.albumArtist + bioCfg.tf.album, alb.focus, true) : alb.arr[alb.ix].artist + alb.arr[alb.ix].album; + const new_composition_id = stndAlb ? $Bio.eval(bioCfg.tf.albumArtist + bioCfg.tf.composition, alb.focus, true) : ''; + const new_title_id = bio.name.title(art.focus, true); let supCache = false; let title_done = false; if (new_title_id == this.id.title) title_done = true; else this.id.title = new_title_id; - const sameComposition = cfg.classicalModeEnable && !alb.ix ? new_composition_id == this.id.composition : true; + const sameComposition = bioCfg.classicalModeEnable && !alb.ix ? new_composition_id == this.id.composition : true; if (new_album_id == this.id.album && sameComposition && !force) { if (!title_done) { @@ -411,31 +411,31 @@ class ServerBio { this.id.album = new_album_id; this.id.composition = new_composition_id; - this.album = stndAlb ? name.album(alb.focus, true) : alb.arr[alb.ix].album; - this.albm = stndAlb ? name.albm(alb.focus, true) : alb.arr[alb.ix].album; - this.albumArtist = stndAlb ? name.albumArtist(alb.focus, true) : alb.arr[alb.ix].artist; - this.composition = cfg.classicalModeEnable && stndAlb ? name.composition(alb.focus, true) : ''; + this.album = stndAlb ? bio.name.album(alb.focus, true) : alb.arr[alb.ix].album; + this.albm = stndAlb ? bio.name.albm(alb.focus, true) : alb.arr[alb.ix].album; + this.albumArtist = stndAlb ? bio.name.albumArtist(alb.focus, true) : alb.arr[alb.ix].artist; + this.composition = bioCfg.classicalModeEnable && stndAlb ? bio.name.composition(alb.focus, true) : ''; if ((!this.album && !this.composition) || !this.albumArtist) return this.getBio(force, art, 1); - if (!stndAlb) supCache = cfg.supCache && !libBio.inLibrary(1, this.albumArtist, this.album); + if (!stndAlb) supCache = bioCfg.supCache && !bio.lib.inLibrary(1, this.albumArtist, this.album); if (stndAlb) { if (this.albm) this.getCover(force, alb); - } else if (force && cfg.dlRevImg) this.getRevImg(this.albumArtist, this.album, '', '', force); + } else if (force && bioCfg.dlRevImg) this.getRevImg(this.albumArtist, this.album, '', '', force); - const am_rev = panelBio.getPth('rev', alb.focus, this.albumArtist, this.album, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.album), 'foAmRev', true, true); - const artiste = stndAlb ? name.artist(alb.focus, true) : this.albumArtist; - const am_bio = panelBio.getPth('bio', alb.focus, artiste, '', stndAlb, cfg.supCache && !libBio.inLibrary(0, artiste), $Bio.clean(artiste), '', '', 'foAmBio', true, true); - const va = this.albumArtist.toLowerCase() == cfg.va.toLowerCase() || this.albumArtist.toLowerCase() != artiste.toLowerCase(); + const am_rev = bio.panel.getPth('rev', alb.focus, this.albumArtist, this.album, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.album), 'foAmRev', true, true); + const artiste = stndAlb ? bio.name.artist(alb.focus, true) : this.albumArtist; + const am_bio = bio.panel.getPth('bio', alb.focus, artiste, '', stndAlb, bioCfg.supCache && !bio.lib.inLibrary(0, artiste), $Bio.clean(artiste), '', '', 'foAmBio', true, true); + const va = this.albumArtist.toLowerCase() == bioCfg.va.toLowerCase() || this.albumArtist.toLowerCase() != artiste.toLowerCase(); if (this.album) { if (!onlyForceLfm) { - const art_upd = cfg.dlAmBio && (force || this.expired(am_bio.pth, this.exp, `Bio ${cfg.partialMatch} ${am_rev.pth}`, false) && !$Bio.open(am_bio.pth).includes('Custom Biography')); + const art_upd = bioCfg.dlAmBio && (force || this.expired(am_bio.pth, this.exp, `Bio ${bioCfg.partialMatch} ${am_rev.pth}`, false) && !$Bio.open(am_bio.pth).includes('Custom Biography')); let rev_upd = false; - if (cfg.dlAmRev) { + if (bioCfg.dlAmRev) { rev_upd = force; if (!rev_upd) { - rev_upd = !$Bio.file(am_rev.pth) && !this.done(`Rev ${cfg.partialMatch} ${am_rev.pth}`, this.exp); + rev_upd = !$Bio.file(am_rev.pth) && !this.done(`Rev ${bioCfg.partialMatch} ${am_rev.pth}`, this.exp); } } let dn_type = ''; @@ -444,71 +444,71 @@ class ServerBio { else if (rev_upd) dn_type = 'review'; else if (art_upd) dn_type = 'biography'; - const dl_am_rev = new DldAllmusicRev(); - dl_am_rev.init(`${serverBio.url.am}albums/${encodeURIComponent(this.album + (!va ? ` ${this.albumArtist}` : ''))}`, 'https://allmusic.com', this.album, this.albumArtist, artiste, va, dn_type, am_rev.fo, am_rev.pth, am_bio.fo, am_bio.pth, art, force); + const dl_am_rev = new BioDldAllmusicRev(); + dl_am_rev.init(`${bio.server.url.am}albums/${encodeURIComponent(this.album + (!va ? ` ${this.albumArtist}` : ''))}`, 'https://allmusic.com', this.album, this.albumArtist, artiste, va, dn_type, am_rev.fo, am_rev.pth, am_bio.fo, am_bio.pth, art, force); } } } else this.getBio(force, art, 1); - if (cfg.dlAmRev && this.composition && !onlyForceLfm) { - const am_comp = panelBio.getPth('rev', alb.focus, this.albumArtist, this.composition, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.composition), 'foAmRev', true, true); - const comp_upd = !alb.ix && (force || !$Bio.file(am_comp.pth) && !this.done(`Rev ${cfg.partialMatch} ${am_comp.pth}`, this.exp)); + if (bioCfg.dlAmRev && this.composition && !onlyForceLfm) { + const am_comp = bio.panel.getPth('rev', alb.focus, this.albumArtist, this.composition, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.composition), 'foAmRev', true, true); + const comp_upd = !alb.ix && (force || !$Bio.file(am_comp.pth) && !this.done(`Rev ${bioCfg.partialMatch} ${am_comp.pth}`, this.exp)); if (comp_upd) { - const artUpd = cfg.dlAmBio && (force || this.expired(am_bio.pth, this.exp, `Bio ${cfg.partialMatch} ${am_comp.pth}`, false) && !$Bio.open(am_bio.pth).includes('Custom Biography')); + const artUpd = bioCfg.dlAmBio && (force || this.expired(am_bio.pth, this.exp, `Bio ${bioCfg.partialMatch} ${am_comp.pth}`, false) && !$Bio.open(am_bio.pth).includes('Custom Biography')); const dn_type = comp_upd && artUpd ? 'composition+biography' : 'composition'; const amAlbumArtist = this.albumArtist; const amWork = this.composition; setTimeout(() => { - const dl_am_comp = new DldAllmusicRev(); - dl_am_comp.init(`${serverBio.url.am}compositions/${encodeURIComponent(amWork + (!va ? ` ${amAlbumArtist}` : ''))}`, 'https://allmusic.com', amWork, amAlbumArtist, artiste, va, dn_type, am_rev.fo, am_comp.pth, am_bio.fo, am_bio.pth, art, force); + const dl_am_comp = new BioDldAllmusicRev(); + dl_am_comp.init(`${bio.server.url.am}compositions/${encodeURIComponent(amWork + (!va ? ` ${amAlbumArtist}` : ''))}`, 'https://allmusic.com', amWork, amAlbumArtist, artiste, va, dn_type, am_rev.fo, am_comp.pth, am_bio.fo, am_bio.pth, art, force); }, 3200); // throttle } } - if (cfg.dlLfmRev && this.album) { - const lfm_rev = panelBio.getPth('rev', alb.focus, this.albumArtist, this.album, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.album), 'foLfmRev', true, true); + if (bioCfg.dlLfmRev && this.album) { + const lfm_rev = bio.panel.getPth('rev', alb.focus, this.albumArtist, this.album, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.album), 'foLfmRev', true, true); const lfmRev = $Bio.open(lfm_rev.pth); const custRev = lfmRev.includes('Custom Review'); if (this.expired(lfm_rev.pth, this.exp, '', lfmRev, 1) && !custRev || force == 2 && !custRev || force == 1) { // force == 2: identifies newlang: upd if !custRev; force == 1: user force from menu - const lfm_alb = new LfmAlbum(() => lfm_alb.onStateChange()); + const lfm_alb = new BioLfmAlbum(() => lfm_alb.onStateChange()); lfm_alb.search(this.albumArtist, this.album, true, lfm_rev.fo, lfm_rev.pth, '', force, false); } } - if (cfg.dlWikiRev) { + if (bioCfg.dlWikiRev) { if (this.album) { - const wiki_rev = panelBio.getPth('rev', alb.focus, this.albumArtist, this.album, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.album), 'foWikiRev', true, true); + const wiki_rev = bio.panel.getPth('rev', alb.focus, this.albumArtist, this.album, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.album), 'foWikiRev', true, true); const wikiRev = $Bio.open(wiki_rev.pth); const custRev = wikiRev.includes('Custom Review'); const wikiAlbum = this.album; const wikiAlbumArtist = this.albumArtist; const wikiStnd = stndAlb; - const wikiTitle = name.title(alb.focus, true); + const wikiTitle = bio.name.title(alb.focus, true); if (this.expired(wiki_rev.pth, this.exp, '', wikiRev, 2) && !custRev || force == 2 && !custRev || force == 1) { setTimeout(() => { - const wiki_alb = new DldWikipedia(() => wiki_alb.onStateChange()); - wiki_alb.search(0, wikiAlbumArtist.toLowerCase() != cfg.va.toLowerCase() ? wikiAlbumArtist : 'Various Artists', wikiAlbum, wikiAlbum, wikiTitle, wikiStnd ? 1 : '!stndAlb', wiki_rev.fo, wiki_rev.pth, alb.focus, force); + const wiki_alb = new BioDldWikipedia(() => wiki_alb.onStateChange()); + wiki_alb.search(0, wikiAlbumArtist.toLowerCase() != bioCfg.va.toLowerCase() ? wikiAlbumArtist : 'Various Artists', wikiAlbum, wikiAlbum, wikiTitle, wikiStnd ? 1 : '!stndAlb', wiki_rev.fo, wiki_rev.pth, alb.focus, force); }, 1200); // wait for mbid & Qid } } if (this.composition) { - const wiki_comp = panelBio.getPth('rev', alb.focus, this.albumArtist, this.composition, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.composition), 'foWikiRev', true, true); - const wikiAlbum = name.album(alb.focus, true); + const wiki_comp = bio.panel.getPth('rev', alb.focus, this.albumArtist, this.composition, stndAlb, supCache, $Bio.clean(this.albumArtist), $Bio.clean(this.albumArtist), $Bio.clean(this.composition), 'foWikiRev', true, true); + const wikiAlbum = bio.name.album(alb.focus, true); const wikiAlbumArtist = this.albumArtist; const wikiComp = $Bio.open(wiki_comp.pth); const wikiStnd = stndAlb; - const wikiTitle = name.title(alb.focus, true); + const wikiTitle = bio.name.title(alb.focus, true); const wikiWork = this.composition; const custRev = wikiComp.includes('Custom Review'); if ((!this.expired(wiki_comp.pth, this.exp, '', wikiComp, 2) || custRev) && !force || custRev && force == 2) return; setTimeout(() => { - const wk_comp = new DldWikipedia(() => wk_comp.onStateChange()); - wk_comp.search(0, wikiAlbumArtist.toLowerCase() != cfg.va.toLowerCase() ? wikiAlbumArtist : 'Various Artists', wikiWork, wikiAlbum, wikiTitle, wikiStnd ? 2 : '!stndComp', wiki_comp.fo, wiki_comp.pth, alb.focus, force); + const wk_comp = new BioDldWikipedia(() => wk_comp.onStateChange()); + wk_comp.search(0, wikiAlbumArtist.toLowerCase() != bioCfg.va.toLowerCase() ? wikiAlbumArtist : 'Various Artists', wikiWork, wikiAlbum, wikiTitle, wikiStnd ? 2 : '!stndComp', wiki_comp.fo, wiki_comp.pth, alb.focus, force); }, 3600); // wait for mbid & Qid & limit call frequency } } @@ -517,18 +517,18 @@ class ServerBio { getRevImg(a, l, pe, fe, force) { // !stndAlb if (!force) { if (this.done(`${a} - ${l} ${this.auto_corr} ${fe}`, this.exp)) return; - const lfm_cov = new LfmAlbum(() => lfm_cov.onStateChange()); + const lfm_cov = new BioLfmAlbum(() => lfm_cov.onStateChange()); lfm_cov.search(a, l, false, pe, fe, l, false, true); } else { - const metadb = libBio.inLibrary(2, a, l); + const metadb = bio.lib.inLibrary(2, a, l); if (metadb) { const g_img = utils.GetAlbumArtV2(metadb, 0, false); if (g_img) return; } else { - const pth = panelBio.getPth('img', panelBio.id.focus, a, l, '', cfg.supCache); - if (imgBio.chkPths(pth.pe, pth.fe, 2)) return; - const lfm_cov = new LfmAlbum(() => lfm_cov.onStateChange()); - lfm_cov.search(a, l, false, pth.pe[cfg.supCache], pth.pe[cfg.supCache] + pth.fe, l, true, true); + const pth = bio.panel.getPth('img', bio.panel.id.focus, a, l, '', bioCfg.supCache); + if (bio.img.chkPths(pth.pe, pth.fe, 2)) return; + const lfm_cov = new BioLfmAlbum(() => lfm_cov.onStateChange()); + lfm_cov.search(a, l, false, pth.pe[bioCfg.supCache], pth.pe[bioCfg.supCache] + pth.fe, l, true, true); } } } @@ -565,43 +565,43 @@ class ServerBio { this.id.track_2 = tr.artist + tr.title; const trk = tr.title.toLowerCase(); - if (cfg.dlAmRev) { - const amTracks = panelBio.getPth('track', tr.focus, tr.artist, 'Track Reviews', '', '', $Bio.clean(tr.artist), '', 'Track Reviews', 'foAmRev', true, true); - const am_bio = panelBio.getPth('bio', tr.focus, tr.artist, '', true, cfg.supCache && !libBio.inLibrary(0, tr.artist), $Bio.clean(tr.artist), '', '', 'foAmBio', true, true); + if (bioCfg.dlAmRev) { + const amTracks = bio.panel.getPth('track', tr.focus, tr.artist, 'Track Reviews', '', '', $Bio.clean(tr.artist), '', 'Track Reviews', 'foAmRev', true, true); + const am_bio = bio.panel.getPth('bio', tr.focus, tr.artist, '', true, bioCfg.supCache && !bio.lib.inLibrary(0, tr.artist), $Bio.clean(tr.artist), '', '', 'foAmBio', true, true); const amText = $Bio.jsonParse(amTracks.pth, false, 'file'); const amArtist = tr.artist; const amTrk = trk; - let track_upd = !this.done(`Rev ${cfg.partialMatch} ${amTracks.pth} ${trk} ${tr.artist}`, this.exp * 6); + let track_upd = !this.done(`Rev ${bioCfg.partialMatch} ${amTracks.pth} ${trk} ${tr.artist}`, this.exp * 6); track_upd = track_upd && (!amText || !amText[trk] || !amText[trk].wiki && amText[trk].update < Date.now() - this.exp * 6) || tr.force; if (track_upd) { setTimeout(() => { - const dl_am_trk = new DldAllmusicRev(); - dl_am_trk.init(`${serverBio.url.am}songs/${encodeURIComponent(`${amTrk} ${amArtist}`)}`, 'https://allmusic.com', amTrk, amArtist, amArtist, false, 'track', amTracks.fo, amTracks.pth, am_bio.fo, am_bio.pth, [], tr.force); + const dl_am_trk = new BioDldAllmusicRev(); + dl_am_trk.init(`${bio.server.url.am}songs/${encodeURIComponent(`${amTrk} ${amArtist}`)}`, 'https://allmusic.com', amTrk, amArtist, amArtist, false, 'track', amTracks.fo, amTracks.pth, am_bio.fo, am_bio.pth, [], tr.force); }, 1600); // throttle } } - if (cfg.dlLfmRev) { - const lfmTracks = panelBio.getPth('track', tr.focus, tr.artist, 'Track Reviews', '', '', $Bio.clean(tr.artist), '', 'Track Reviews', 'foLfmRev', true, true); + if (bioCfg.dlLfmRev) { + const lfmTracks = bio.panel.getPth('track', tr.focus, tr.artist, 'Track Reviews', '', '', $Bio.clean(tr.artist), '', 'Track Reviews', 'foLfmRev', true, true); const lfmText = $Bio.jsonParse(lfmTracks.pth, false, 'file'); - if (!lfmText || !lfmText[trk] || lfmText[trk].update < Date.now() - this.exp || lfmText[trk].lang != cfg.language || tr.force) { - const dl_lfm_track = new LfmTrack(() => dl_lfm_track.onStateChange()); + if (!lfmText || !lfmText[trk] || lfmText[trk].update < Date.now() - this.exp || lfmText[trk].lang != bioCfg.language || tr.force) { + const dl_lfm_track = new BioLfmTrack(() => dl_lfm_track.onStateChange()); dl_lfm_track.search(tr.artist, trk, lfmTracks.fo, lfmTracks.pth, tr.force); } } - if (cfg.dlWikiRev) { - const wikiTracks = panelBio.getPth('track', tr.focus, tr.artist, 'Track Reviews', '', '', $Bio.clean(tr.artist), '', 'Track Reviews', 'foWikiRev', true, true); + if (bioCfg.dlWikiRev) { + const wikiTracks = bio.panel.getPth('track', tr.focus, tr.artist, 'Track Reviews', '', '', $Bio.clean(tr.artist), '', 'Track Reviews', 'foWikiRev', true, true); const wikiText = $Bio.jsonParse(wikiTracks.pth, false, 'file'); - const wikiAlbum = name.album(tr.focus, true); + const wikiAlbum = bio.name.album(tr.focus, true); const wikiArtist = tr.artist; const wikiTrk = trk; - if (!wikiText || !wikiText[wikiTrk] || wikiText[wikiTrk].update < Date.now() - this.exp * 6 || wikiText[wikiTrk].lang != cfg.language || tr.force) { + if (!wikiText || !wikiText[wikiTrk] || wikiText[wikiTrk].update < Date.now() - this.exp * 6 || wikiText[wikiTrk].lang != bioCfg.language || tr.force) { setTimeout(() => { - const dl_wiki_track = new DldWikipedia(() => dl_wiki_track.onStateChange()); + const dl_wiki_track = new BioDldWikipedia(() => dl_wiki_track.onStateChange()); dl_wiki_track.search(0, wikiArtist, wikiTrk, wikiAlbum, wikiTrk, 3, wikiTracks.fo, wikiTracks.pth, tr.focus, tr.force); }, 2400); // wait for mbid & Qid & limit call frequency } @@ -623,7 +623,7 @@ class ServerBio { list[i].art = $Bio.removeDiacritics(this.tidy(list[i].artist || 'N/A', true)); if (rel == list[i].rel && a == list[i].art) return i; } - if (!cfg.partialMatchEnabled && !force) return -1; + if (!bioCfg.partialMatchEnabled && !force) return -1; switch (true) { case type == 'review': { @@ -636,24 +636,24 @@ class ServerBio { a = stripAmp(a); rel = stripAmp(rel); list.forEach(v => v.rel = stripAmp(v.rel)); - serverBio.getSortedScores(list, rel, 'rel', 'damerauLevenshtein'); - const pm = !force ? cfg.fuzzyMatchReview : Math.min(force, cfg.fuzzyMatchReview); + bio.server.getSortedScores(list, rel, 'rel', 'damerauLevenshtein'); + const pm = !force ? bioCfg.fuzzyMatchReview : Math.min(force, bioCfg.fuzzyMatchReview); for (i = 0; i < list.length; i++) { if (list[i].score * 100 >= pm && a == list[i].art) return i; } break; } case type == 'song': { - serverBio.getSortedScores(list, rel, 'rel', 'damerauLevenshtein'); - const pm = !force ? cfg.fuzzyMatchTrack : Math.min(force, cfg.fuzzyMatchTrack); + bio.server.getSortedScores(list, rel, 'rel', 'damerauLevenshtein'); + const pm = !force ? bioCfg.fuzzyMatchTrack : Math.min(force, bioCfg.fuzzyMatchTrack); for (i = 0; i < list.length; i++) { if (list[i].score * 100 >= pm && a == list[i].art) return i; } break; } case type == 'composition': { - serverBio.getSortedScores(list, p_release, 'title', 'fuzzyset'); - const pm = !force ? cfg.fuzzyMatchComposition : Math.min(force, cfg.fuzzyMatchComposition); + bio.server.getSortedScores(list, p_release, 'title', 'fuzzyset'); + const pm = !force ? bioCfg.fuzzyMatchComposition : Math.min(force, bioCfg.fuzzyMatchComposition); for (i = 0; i < list.length; i++) { if (list[i].score * 100 >= pm && a == list[i].art) return i; } @@ -665,7 +665,7 @@ class ServerBio { res() { window.NotifyOthers('bio_getText', 'bio_getText'); - txt.grab(); + bio.txt.grab(); } setImgRecycler(n) { @@ -680,7 +680,7 @@ class ServerBio { if (lang) this.lfm.server = lang.toLowerCase(); this.lfm.server = this.lfm.server == 'en' ? 'www.last.fm' : `www.last.fm/${this.lfm.server}`; this.lfm.def_EN = this.lfm.server == 'www.last.fm'; - this.langFallback = cfg.languageFallback && !this.lfm.def_EN; + this.langFallback = bioCfg.languageFallback && !this.lfm.def_EN; } tidy(n, cutLeadThe) { diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-settings.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-settings.js index 1e6240ec..a220639e 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-settings.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-settings.js @@ -1,12 +1,12 @@ 'use strict'; -class SettingBio { +class BioSetting { constructor(key, default_value, type) { this.checkImagesTimer = null; this.key = key; this.default_value = default_value; this.type = type; - this.value = cfg.get(this.key, this.default_value, this.type); + this.value = bioCfg.get(this.key, this.default_value, this.type); } // * METHODS * // @@ -33,18 +33,18 @@ class SettingBio { } break; } - cfg.set(this.key, new_value); + bioCfg.set(this.key, new_value); this.value = new_value; } } } -class SettingsBio { +class BioSettings { constructor(name) { // this.key_list = {}; debug this.cfgBaseName = name; - this.storageFolder = pref.customBiographyDir ? `${globals.customBiographyDir}` : `${fb.ProfilePath}cache\\biography\\`; + this.storageFolder = grSet.customBiographyDir ? `${grCfg.customBiographyDir}` : `${fb.ProfilePath}cache\\biography\\`; $Bio.buildPth(this.storageFolder); this.bio = `${this.storageFolder + this.cfgBaseName}.cfg`; this.item_properties = `${this.storageFolder}item_properties.json`; @@ -63,7 +63,7 @@ class SettingsBio { }; this.local = $Bio.file('C:\\check_local\\1450343922.txt'); - if (!this.local) pptBio.themed = false; + if (!this.local) bioSet.themed = false; this.remap = {}; this.pth = {}; this.sup = {}; @@ -72,7 +72,7 @@ class SettingsBio { this.photoRecycler = `${this.storageFolder}oldPhotosForDeletion\\`; this.settingsKeys = []; - this.caPath = pref.customBiographyDir ? `${globals.customBiographyDir}biography-cache\\` : `${this.storageFolder}biography-cache`; + this.caPath = grSet.customBiographyDir ? `${grCfg.customBiographyDir}biography-cache\\` : `${this.storageFolder}biography-cache`; this.cachePath = `${this.caPath}\\`; this.suffix = { @@ -128,7 +128,7 @@ class SettingsBio { add(item) { // this.key_list[item[3]] = 1; debug - this[`${item[3]}_internal`] = new SettingBio(item[3], item[1], item[2]); + this[`${item[3]}_internal`] = new BioSetting(item[3], item[1], item[2]); Object.defineProperty(this, item[3], { get() { @@ -176,28 +176,28 @@ class SettingsBio { } checkCfg() { - if (!$Bio.file(this.item_properties)) $Bio.save(this.item_properties, item_properties, true); - if (!$Bio.file(this.item_properties_alternative_grouping)) $Bio.save(this.item_properties_alternative_grouping, item_properties_alternative_grouping, true); - if (!$Bio.file(this.nowplaying)) $Bio.save(this.nowplaying, nowplaying, true); - if (!$Bio.file(this.radioParser)) $Bio.save(this.radioParser, radioParser, true); + if (!$Bio.file(this.item_properties)) $Bio.save(this.item_properties, bio_item_properties, true); + if (!$Bio.file(this.item_properties_alternative_grouping)) $Bio.save(this.item_properties_alternative_grouping, bio_item_properties_alternative_grouping, true); + if (!$Bio.file(this.nowplaying)) $Bio.save(this.nowplaying, bioNowplaying, true); + if (!$Bio.file(this.radioParser)) $Bio.save(this.radioParser, bioRadioParser, true); if ($Bio.file(this.bio)) return; const orig_cfg = `${fb.ProfilePath}cache\\biography\\biography.cfg`; - const orig_cfg_copied = $Bio.file(`${cfg.storageFolder}foo_lastfm_img.vbs`); + const orig_cfg_copied = $Bio.file(`${bioCfg.storageFolder}foo_lastfm_img.vbs`); if ($Bio.file(orig_cfg) && !orig_cfg_copied) { try { - fsoBio.CopyFile(orig_cfg, this.bio); + bioFSO.CopyFile(orig_cfg, this.bio); this.cfg = $Bio.jsonParse(this.bio, {}, 'file'); const blacklist_image = `${fb.ProfilePath}cache\\biography\\blacklist_image.json`; if ($Bio.file(blacklist_image)) { - fsoBio.CopyFile(blacklist_image, `${cfg.storageFolder}blacklist_image.json`); + bioFSO.CopyFile(blacklist_image, `${bioCfg.storageFolder}blacklist_image.json`); } return true; } catch (e) { - this.createCfg(settingsBio); + this.createCfg(bioSettings); return false; } } else { - this.createCfg(settingsBio); + this.createCfg(bioSettings); return false; } } @@ -205,9 +205,9 @@ class SettingsBio { checkFiles(v) { if (!$Bio.folder(v) || !$Bio.server) return; const foArr = []; - const folder = fsoBio.GetFolder(v); + const folder = bioFSO.GetFolder(v); const subFolders = folder.SubFolders; - if (!subFolders.Count) try { fsoBio.DeleteFolder(folder); return; } catch (e) {} // rem empty last.fm etc + if (!subFolders.Count) try { bioFSO.DeleteFolder(folder); return; } catch (e) {} // rem empty last.fm etc for (const subFolder of subFolders) { foArr.push(v + subFolder.Name); } @@ -217,11 +217,11 @@ class SettingsBio { let files = utils.Glob(`${foArr[i]}\\*`); files.forEach(w => { if (Date.now() - $Bio.lastAccessed(w) > this.cacheTime) { - try { fsoBio.DeleteFile(w); } catch (e) {} + try { bioFSO.DeleteFile(w); } catch (e) {} } }); files = utils.Glob(`${foArr[i]}\\*`); - if (!files.length) try { fsoBio.DeleteFolder(foArr[i]); } catch (e) {} + if (!files.length) try { bioFSO.DeleteFolder(foArr[i]); } catch (e) {} i++; } else { clearInterval(timer); @@ -232,11 +232,11 @@ class SettingsBio { checkImages(v) { if (!$Bio.folder(v) || !$Bio.server) return; const foArr = []; - const folder = fsoBio.GetFolder(v); + const folder = bioFSO.GetFolder(v); let chars = folder.SubFolders; - if (!chars.Count) try { fsoBio.DeleteFolder(folder); return; } catch (e) {} + if (!chars.Count) try { bioFSO.DeleteFolder(folder); return; } catch (e) {} for (const char of chars) { // a - const folder2 = fsoBio.GetFolder(char); // artist + const folder2 = bioFSO.GetFolder(char); // artist const artists = folder2.SubFolders; for (const artist of artists) { foArr.push(`${v + char.Name}\\${artist.Name}`); @@ -246,14 +246,14 @@ class SettingsBio { const timer = setInterval(() => { if ($Bio.server && i < foArr.length) { const files = utils.Glob(`${foArr[i]}\\*`); - if (!files.length) try { fsoBio.DeleteFolder(foArr[i]); } catch (e) {} + if (!files.length) try { bioFSO.DeleteFolder(foArr[i]); } catch (e) {} else { let accessed = 0; files.forEach(w => { accessed = Math.max(accessed, $Bio.lastAccessed(w)); }); if (Date.now() - accessed > this.cacheTime) { - try { fsoBio.DeleteFolder(foArr[i]); } catch (e) {} + try { bioFSO.DeleteFolder(foArr[i]); } catch (e) {} } } i++; @@ -261,9 +261,9 @@ class SettingsBio { clearInterval(timer); chars = folder.SubFolders; for (const char of chars) { // a - const folder2 = fsoBio.GetFolder(char); // artist + const folder2 = bioFSO.GetFolder(char); // artist const artists = folder2.SubFolders; - if (!artists.Count) try { fsoBio.DeleteFolder(folder2); } catch (e) {} // rem empty + if (!artists.Count) try { bioFSO.DeleteFolder(folder2); } catch (e) {} // rem empty } } }, 100); @@ -339,8 +339,8 @@ class SettingsBio { open() { const ok_callback = (new_ppt, new_cfg, type, new_dialogWindow) => { - if (new_dialogWindow) pptBio.set('Panel Biography - Biography Dialog Box', new_dialogWindow); - if (new_ppt) uiBio.updateProp($Bio.jsonParse(new_ppt, {}), 'value'); + if (new_dialogWindow) bioSet.set('Panel Biography - Biography Dialog Box', new_dialogWindow); + if (new_ppt) bio.ui.updateProp($Bio.jsonParse(new_ppt, {}), 'value'); if (new_cfg) { const lang_ix = this.lang.ix; const cache_type = this.autoCache; @@ -349,13 +349,13 @@ class SettingsBio { this.setCacheMode(); return; } - window.NotifyOthers(`bio_newCfg${pptBio.serverName}`, new_cfg); + window.NotifyOthers(`bio_newCfg${bioSet.serverName}`, new_cfg); if (lang_ix != this.lang.ix) this.setLanguage(this.lang.ix); } if (new_ppt || new_cfg) return; switch (type) { case 'resetPanelCfg': - uiBio.updateProp(pptBio, 'default_value'); + bio.ui.updateProp(bioSet, 'default_value'); break; case 'resetServerCfg': this.resetCfg(); @@ -374,7 +374,7 @@ class SettingsBio { break; } }; - let dialogWindow = pptBio.get('Panel Biography - Biography Dialog Box', JSON.stringify({ + let dialogWindow = bioSet.get('Panel Biography - Biography Dialog Box', JSON.stringify({ w: 85, h: 60, def_w: 85, @@ -388,10 +388,10 @@ class SettingsBio { dialogWindow = $Bio.jsonParse(dialogWindow); dialogWindow.version = `v${window.ScriptInfo.Version}`; dialogWindow = JSON.stringify(dialogWindow); - if (!panelBio.id.numServersChecked) panelBio.checkNumServers(); - if (popUpBoxBio.isHtmlDialogSupported()) popUpBoxBio.config(JSON.stringify(pptBio), JSON.stringify(cfg), dialogWindow, ok_callback, this.lang.ix, $Bio.folder(cfg.photoRecycler)); + if (!bio.panel.id.numServersChecked) bio.panel.checkNumServers(); + if (bio.popUpBox.isHtmlDialogSupported()) bio.popUpBox.config(JSON.stringify(bioSet), JSON.stringify(bioCfg), dialogWindow, ok_callback, this.lang.ix, $Bio.folder(bioCfg.photoRecycler)); else { - popUpBoxBio.ok = false; + bio.popUpBox.ok = false; $Bio.trace('the options html dialog doesn\'t appear to be available with the current operating system. All settings in options are available elsewhere: 1) panel settings are in panel properties; 2) server settings that apply to all panels are in the cfg file - default settings should be fine for most users, but can be changed by careful editing in a text editor. Common settings are on the menu.'); } } @@ -422,8 +422,8 @@ class SettingsBio { }); if (!this.classicalModeEnable) { - pptBio.classicalMusicMode = false; - pptBio.classicalAlbFallback = false; + bioSet.classicalMusicMode = false; + bioSet.classicalAlbFallback = false; } this.suffix = { @@ -512,13 +512,13 @@ class SettingsBio { expandPath(pth) { if (/%profile%\\/i.test(pth) && /%storage_folder%\\/i.test(pth)) pth = pth.replace(/%profile%\\/gi, ''); - return pth.replace(/^%profile%\\?/i, $Bio.tfEscape(fb.ProfilePath)).replace(/^%storage_folder%\\?/i, $Bio.tfEscape(cfg.storageFolder)); + return pth.replace(/^%profile%\\?/i, $Bio.tfEscape(fb.ProfilePath)).replace(/^%storage_folder%\\?/i, $Bio.tfEscape(bioCfg.storageFolder)); } preview(n, tfAll, excludeStream, sFind, sReplace, artistView, trackReview) { - if (!tfAll) return txt.tf(n, artistView, trackReview); // heading + if (!tfAll) return bio.txt.tf(n, artistView, trackReview); // heading if (excludeStream) { - const handle = $Bio.handle(panelBio.id.focus, true); + const handle = $Bio.handle(bio.panel.id.focus, true); if (!handle) return; const covCanBeSaved = !handle.RawPath.startsWith('fy+') && !handle.RawPath.startsWith('3dydfy:') && !handle.RawPath.startsWith('http'); if (!covCanBeSaved) return 'Stream: Covers Not Saved'; @@ -541,7 +541,7 @@ class SettingsBio { tfNames.forEach((v, i) => n = n.replace(RegExp(v, 'gi'), tf[i])); const wildCard = /[*?]/.test(n); - return !wildCard ? panelBio.cleanPth(n, false) : panelBio.cleanPth(n.replace(/\*/g, '@!@').replace(/\?/g, '!@!'), false).replace(/@!@/g, '*').replace(/!@!/g, '?'); + return !wildCard ? bio.panel.cleanPth(n, false) : bio.panel.cleanPth(n.replace(/\*/g, '@!@').replace(/\?/g, '!@!'), false).replace(/@!@/g, '*').replace(/!@!/g, '?'); } move(n) { @@ -549,14 +549,14 @@ class SettingsBio { const timestamp = `${[d.getFullYear(), $Bio.padNumber((d.getMonth() + 1), 2), $Bio.padNumber(d.getDate(), 2)].join('-')}_${[$Bio.padNumber(d.getHours(), 2), $Bio.padNumber(d.getMinutes(), 2), $Bio.padNumber(d.getSeconds(), 2)].join('-')}`; try { const fn = `${this.storageFolder + this.cfgBaseName}_old_${timestamp}.cfg`; - if (!$Bio.file(fn)) fsoBio.MoveFile(this.bio, fn); + if (!$Bio.file(fn)) bioFSO.MoveFile(this.bio, fn); } catch (e) { if (n) fb.ShowPopupMessage(`Unable to reset server settings.\n\n${this.cfgBaseName}.cfg is being used by another program.`, 'Biography'); } } resetCfg() { - cfg.move(true); + bioCfg.move(true); window.Reload(); window.NotifyOthers('bio_refresh', 'bio_refresh'); } @@ -571,33 +571,33 @@ class SettingsBio { this.lang.ix = i; this.language = this.lang.arr[this.lang.ix]; } else this.toggle('languageFallback'); - txt.bio.subhead = { - am: [this.amDisplayName, `${this.amDisplayName} ${txt.bio.lang[this.lang.ix]}`], - lfm: [this.lfmDisplayName, `${this.lfmDisplayName} ${txt.bio.lang[this.lang.ix]}`], - wiki: [this.wikiDisplayName, `${this.wikiDisplayName} ${txt.bio.lang[this.lang.ix]}`], + bio.txt.bio.subhead = { + am: [this.amDisplayName, `${this.amDisplayName} ${bio.txt.bio.lang[this.lang.ix]}`], + lfm: [this.lfmDisplayName, `${this.lfmDisplayName} ${bio.txt.bio.lang[this.lang.ix]}`], + wiki: [this.wikiDisplayName, `${this.wikiDisplayName} ${bio.txt.bio.lang[this.lang.ix]}`], txt: ['', ''] }; - txt.rev.subhead = { - am: [this.amDisplayName, `${this.amDisplayName} ${txt.rev.lang[this.lang.ix]}`], - lfm: [this.lfmDisplayName, `${this.lfmDisplayName} ${txt.rev.lang[this.lang.ix]}`], - wiki: [this.wikiDisplayName, `${this.wikiDisplayName} ${txt.rev.lang[this.lang.ix]}`], + bio.txt.rev.subhead = { + am: [this.amDisplayName, `${this.amDisplayName} ${bio.txt.rev.lang[this.lang.ix]}`], + lfm: [this.lfmDisplayName, `${this.lfmDisplayName} ${bio.txt.rev.lang[this.lang.ix]}`], + wiki: [this.wikiDisplayName, `${this.wikiDisplayName} ${bio.txt.rev.lang[this.lang.ix]}`], txt: ['', ''] }; - txt.artistReset(true); - txt.albumReset(true); - txt.albumFlush(); - txt.artistFlush(); - txt.rev.cur = ''; - txt.bio.cur = ''; - - txt.bio.loaded = { + bio.txt.artistReset(true); + bio.txt.albumReset(true); + bio.txt.albumFlush(); + bio.txt.artistFlush(); + bio.txt.rev.cur = ''; + bio.txt.bio.cur = ''; + + bio.txt.bio.loaded = { am: false, lfm: false, wiki: false, txt: false, ix: -1 }; - txt.rev.loaded = { + bio.txt.rev.loaded = { am: false, lfm: false, wiki: false, @@ -605,18 +605,18 @@ class SettingsBio { ix: -1 }; - txt.loadReader(); - txt.getText(true); - butBio.createStars(); - txt.getSubHeadWidths(); - txt.artCalc(); - txt.albCalc(); + bio.txt.loadReader(); + bio.txt.getText(true); + bio.but.createStars(); + bio.txt.getSubHeadWidths(); + bio.txt.artCalc(); + bio.txt.albCalc(); $Bio.server = true; - panelBio.notifyTimestamp = Date.now(); - window.NotifyOthers(`bio_notServer${pptBio.serverName}`, panelBio.notifyTimestamp); + bio.panel.notifyTimestamp = Date.now(); + window.NotifyOthers(`bio_notServer${bioSet.serverName}`, bio.panel.notifyTimestamp); if ($Bio.server) { - serverBio.setLanguage(this.language); - panelBio.callServer(2, panelBio.id.focus, '', 1); + bio.server.setLanguage(this.language); + bio.panel.callServer(2, bio.panel.id.focus, '', 1); window.NotifyOthers('bio_refresh', 'bio_refresh'); } } @@ -627,8 +627,8 @@ class SettingsBio { this.toggle(`tagEnabled${i - 1}`); break; case i == 14: { - const cur = $Bio.jsonParse(pptBio.get('Panel Biography - Biography Dialog Box'), {}); - pptBio.set('Panel Biography - Biography Dialog Box', JSON.stringify({ + const cur = $Bio.jsonParse(bioSet.get('Panel Biography - Biography Dialog Box'), {}); + bioSet.set('Panel Biography - Biography Dialog Box', JSON.stringify({ w: cur.w, h: cur.h, def_w: 85, @@ -642,19 +642,19 @@ class SettingsBio { } case i == 15: { if (!this.taggerConfirm) { - if (this.tagEnabled10 && this.tagEnabled13 < 7) tagBio.check(handles); - else tagBio.write(handles); + if (this.tagEnabled10 && this.tagEnabled13 < 7) bio.tag.check(handles); + else bio.tag.write(handles); break; } const continue_confirmation = (status, confirmed) => { if (confirmed) { - if (this.tagEnabled10 && this.tagEnabled13 < 7) tagBio.check(handles); - else tagBio.write(handles); + if (this.tagEnabled10 && this.tagEnabled13 < 7) bio.tag.check(handles); + else bio.tag.write(handles); } }; const caption = 'Tag Files:'; const prompt = handles.Count < 2000 ? `Update ${handles.Count} ${handles.Count > 1 ? 'tracks' : 'track'}.\n\nContinue?` : `Update ${handles.Count} tracks.\n\nADVISORY: This operation analyses a lot of data.\n\nContinue?`; - const wsh = popUpBoxBio.isHtmlDialogSupported() ? popUpBoxBio.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; + const wsh = bio.popUpBox.isHtmlDialogSupported() ? bio.popUpBox.confirm(caption, prompt, 'OK', 'Cancel', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Bio.wshPopup(prompt, caption)); break; } @@ -663,14 +663,21 @@ class SettingsBio { } updateCfg(new_cfg) { - this.settingsKeys.forEach(v => cfg[v] = this.cfg[v] = new_cfg[`${v}_internal`].value); - tagBio.setGenres(); + this.settingsKeys.forEach(v => bioCfg[v] = this.cfg[v] = new_cfg[`${v}_internal`].value); + bio.tag.setGenres(); this.parse(); } } -const cfg = new SettingsBio(pptBio.serverName); -let settingsBio = [ +/** + * The instance of `BioSettings` class for biography config settings. + * @typedef {BioSettings} + * @global + */ +const bioCfg = new BioSettings(bioSet.serverName); + +/** @global @type {Array.} */ +let bioSettings = [ ['Album Review [Allmusic] Auto-Download', true, 'boolean', 'dlAmRev'], // description (setting[0]) is for info only otherwise it's unused [update from orig_ini is no longer supported] ['Biography [Allmusic] Auto-Download', true, 'boolean', 'dlAmBio'], ['Album Review [Lastfm] Auto-Download', true, 'boolean', 'dlLfmRev'], @@ -782,7 +789,8 @@ let settingsBio = [ ['Show console messages', true, 'boolean', 'showConsoleMessages'] ]; -let item_properties = +/** @global @type {object} */ +let bio_item_properties = `{ "*****HELP*****": [ @@ -914,11 +922,13 @@ let item_properties = "uppercase": "ANV|MB" }` -let item_properties_alternative_grouping = item_properties +/** @global @type {object} */ +let bio_item_properties_alternative_grouping = bio_item_properties .replace(/("Metadata\*":\s{\s*?"show":\s)false/, `$1${true}`) .replace(/(("Metadata"|"Popularity"|"AllMusic"|"Last.fm"|"Wikipedia"):\s{\s*?"show":\s)true/g, `$1${false}`); -let nowplaying = `Artist: %BIO_ARTIST%$crlf() +/** @global @type {string} */ +let bioNowplaying = `Artist: %BIO_ARTIST%$crlf() $crlf() Title: %BIO_TITLE%$crlf() $crlf() @@ -926,7 +936,8 @@ $crlf() $crlf()] $if2(%playback_time%,0:00)[ / %length%]`; -let radioParser = `/* RadioStreamParser is written in javascript and can be user edited with care. +/** @global @type {object} */ +let bioRadioParser = `/* RadioStreamParser is written in javascript and can be user edited with care. It's designed for use with internet radio streams that contain the artist name and song title in a non-standard format. Before editing, make a backup copy in case things go wrong. @@ -937,8 +948,8 @@ Before editing, make a backup copy in case things go wrong. In more complex cases use RegExp or javascript string manipulation functions. Google for syntax. 4. Adjust the format (comment out if unwanted). This is aesthetic. It won't affect searching. 5. Use console.log traces to see what's going on and debug, e.g uncomment those below. -6. If fb2K artist name is required, use, e.g. $Bio.eval('[$trim(' + (typeof cfg !== 'undefined' ? cfg.tf.artist : pptBio.tfArtist) + ')]', focus, ignoreLock) -7. For info, biography uses cfg.tf.artist and cfg.tf.title; Find & Play uses pptBio.tfArtist and pptBio.tfTitle. +6. If fb2K artist name is required, use, e.g. $Bio.eval('[$trim(' + (typeof bioCfg !== 'undefined' ? bioCfg.tf.artist : bioSet.tfArtist) + ')]', focus, ignoreLock) +7. For info, biography uses bioCfg.tf.artist and bioCfg.tf.title; Find & Play uses bioSet.tfArtist and bioSet.tfTitle. This parser is also used by Find & Play provided the biography package id {BA9557CE-7B4B-4E0E-9373-99F511E81252} is unaltered. */ @@ -951,7 +962,7 @@ class radioStreamParser { // don't alter the next 4 lines const path = $Bio.eval('%path%', focus, ignoreLock); let artist = ''; - let item = $Bio.eval('[$trim(' + (typeof cfg !== 'undefined' ? cfg.tf.title : pptBio.tfTitle) + ')]', focus, ignoreLock); + let item = $Bio.eval('[$trim(' + (typeof bioCfg !== 'undefined' ? bioCfg.tf.title : bioSet.tfTitle) + ')]', focus, ignoreLock); let title = ''; switch (path) { @@ -1013,7 +1024,7 @@ class radioStreamParser { case 'https://stream.arrowrockradio.com/arrowrockradio': // artist needs stream name and playing removing - artist = $Bio.eval('[$trim($replace(' + (typeof cfg !== 'undefined' ? cfg.tf.artist : pptBio.tfArtist) + ',Arrow Rock Radio:,,PLAYING:,))]', focus, ignoreLock); + artist = $Bio.eval('[$trim($replace(' + (typeof bioCfg !== 'undefined' ? bioCfg.tf.artist : bioSet.tfArtist) + ',Arrow Rock Radio:,,PLAYING:,))]', focus, ignoreLock); //console.log('artist', artist); // title is correct except it's uppercase: including here means it goes through the titlecase converter @@ -1028,7 +1039,7 @@ class radioStreamParser { artist = item; // item is the title, which is the artist as they're swapped //console.log('artist', artist); - title = $Bio.eval('[$trim(' + (typeof cfg !== 'undefined' ? cfg.tf.artist : pptBio.tfArtist) + ')]', focus, ignoreLock); + title = $Bio.eval('[$trim(' + (typeof bioCfg !== 'undefined' ? bioCfg.tf.artist : bioSet.tfArtist) + ')]', focus, ignoreLock); //console.log('title', title); break; @@ -1064,14 +1075,14 @@ class radioStreamParser { } }` -cfg.init(settingsBio); -settingsBio = undefined; -item_properties = undefined; -item_properties_alternative_grouping = undefined; -nowplaying = undefined; -radioParser = undefined; +bioCfg.init(bioSettings); +bioSettings = undefined; +bio_item_properties = undefined; +bio_item_properties_alternative_grouping = undefined; +bioNowplaying = undefined; +bioRadioParser = undefined; -if ($Bio.file(cfg.radioParser)) { - include(cfg.radioParser); - isRadioStreamParser = true; +if ($Bio.file(bioCfg.radioParser)) { + include(bioCfg.radioParser); + bioIsRadioStreamParser = true; } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-tagger.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-tagger.js index db250074..d83ad10a 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-tagger.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-tagger.js @@ -1,6 +1,6 @@ 'use strict'; -class TaggerBio { +class BioTagger { constructor() { this.arr1 = []; this.arr2 = []; @@ -45,7 +45,7 @@ class TaggerBio { let cur_artist = '####'; let rec = 0; let writeSent = false; - const tf_artist = FbTitleFormat(`$upper(${cfg.tf.artist})`); + const tf_artist = FbTitleFormat(`$upper(${bioCfg.tf.artist})`); const artists = tf_artist.EvalWithMetadbs(handles); const similarArtists = []; const similarArr = []; @@ -55,10 +55,10 @@ class TaggerBio { if (artist != cur_artist) { cur_artist = artist; similarArtists[i] = ''; - const lfmBio = `${panelBio.cleanPth(cfg.pth.foLfmBio, handles[i], 'tag') + $Bio.clean(artist) + cfg.suffix.foLfmBio}.txt`; + const lfmBio = `${bio.panel.cleanPth(bioCfg.pth.foLfmBio, handles[i], 'tag') + $Bio.clean(artist) + bioCfg.suffix.foLfmBio}.txt`; if ($Bio.file(lfmBio)) { const lBio = $Bio.open(lfmBio); - similarArtists[i] = this.getTag(lBio, panelBio.similarArtistsKey).tag; + similarArtists[i] = this.getTag(lBio, bio.panel.similarArtistsKey).tag; if (similarArtists[i].length > 6) similarArr.push(artist); } } @@ -66,23 +66,23 @@ class TaggerBio { if (similarArr.length) { let i = 0; - timerBio.clear(timerBio.sim1); - timerBio.sim1.id = setInterval(() => { + bio.timer.clear(bio.timer.sim1); + bio.timer.sim1.id = setInterval(() => { if (i < similarArr.length) { - const lfm_similar = new LfmSimilarArtists(() => lfm_similar.onStateChange(), lfm_similar_search_done.bind(this)); + const lfm_similar = new BioLfmSimilarArtists(() => lfm_similar.onStateChange(), lfm_similar_search_done.bind(this)); lfm_similar.search(similarArr[i], similarArr.length, handles, 6); i++; - } else timerBio.clear(timerBio.sim1); + } else bio.timer.clear(bio.timer.sim1); }, similarArr.length < 100 ? 20 : 300); } else this.write(handles); const lfm_similar_search_done = (res1, res2, p_done, p_handles) => { rec++; - if (!timerBio.sim2.id) { - timerBio.sim2.id = setTimeout(() => { + if (!bio.timer.sim2.id) { + bio.timer.sim2.id = setTimeout(() => { writeSent = true; this.write(p_handles); - timerBio.sim2.id = null; + bio.timer.sim2.id = null; }, 60000); } this.simList.push({ @@ -90,7 +90,7 @@ class TaggerBio { similar: res2 }); if (p_done == rec && !writeSent) { - timerBio.clear(timerBio.sim2); + bio.timer.clear(bio.timer.sim2); this.write(p_handles); } }; @@ -147,7 +147,7 @@ class TaggerBio { } lfmTidy(n) { - if (!cfg.useWhitelist || this.genres.length < 701) return n; + if (!bioCfg.useWhitelist || this.genres.length < 701) return n; n.forEach((v, i) => { this.arr1.forEach((w, j) => { if (!w.includes('\\b')) { @@ -167,9 +167,9 @@ class TaggerBio { } setGenres() { - this.genres = $Bio.jsonParse(`${cfg.storageFolder}lastfm_genre_whitelist.json`, [], 'file'); - if (cfg.customGenres.length) { - this.customGenres = cfg.customGenres.split(','); + this.genres = $Bio.jsonParse(`${bioCfg.storageFolder}lastfm_genre_whitelist.json`, [], 'file'); + if (bioCfg.customGenres.length) { + this.customGenres = bioCfg.customGenres.split(','); this.customGenres = this.customGenres.map(v => v.trim()); this.genres = [...new Set(this.genres.concat(this.customGenres))]; } @@ -177,7 +177,7 @@ class TaggerBio { this.arr1 = []; this.arr2 = []; - const items = cfg.translate.split(','); + const items = bioCfg.translate.split(','); items.forEach(v => { const w = v.split('>'); this.arr1.push($Bio.strip(w[0])); @@ -223,14 +223,14 @@ class TaggerBio { const cue = []; const force = this.force && notify; const locale = []; - const radioStream = notify && panelBio.isRadio(panelBio.id.focus); + const radioStream = notify && bio.panel.isRadio(bio.panel.id.focus); const rem = []; const similarArtists = []; const tags = []; - const tf_artist = FbTitleFormat(`$upper(${cfg.tf.artist})`); - const tf_albumArtist = FbTitleFormat(`$upper(${cfg.tf.albumArtist})`); + const tf_artist = FbTitleFormat(`$upper(${bioCfg.tf.artist})`); + const tf_albumArtist = FbTitleFormat(`$upper(${bioCfg.tf.albumArtist})`); const tf_cue = FbTitleFormat('$ext(%path%)'); - const tf_l = FbTitleFormat(`$upper(${cfg.tf.album})`); + const tf_l = FbTitleFormat(`$upper(${bioCfg.tf.album})`); const artists = !radioStream ? tf_artist.EvalWithMetadbs(handles) : [tf_artist.Eval()]; const albumArtists = tf_albumArtist.EvalWithMetadbs(handles); const cues = tf_cue.EvalWithMetadbs(handles); @@ -243,25 +243,25 @@ class TaggerBio { albumArtist = albumArtists[i]; cue[i] = cues[i].toLowerCase() == 'cue'; album = albums[i]; - album = !cfg.albStrip ? name.albumTidy(album) : name.albumClean(album); + album = !bioCfg.albStrip ? bio.name.albumTidy(album) : bio.name.albumClean(album); if (artist != cur_artist) { cur_artist = artist; similarArtists[i] = ''; - if (cfg.tagEnabled7 || cfg.tagEnabled8 || cfg.tagEnabled9 || cfg.tagEnabled10 && cfg.tagEnabled13 < 7 || force) { + if (bioCfg.tagEnabled7 || bioCfg.tagEnabled8 || bioCfg.tagEnabled9 || bioCfg.tagEnabled10 && bioCfg.tagEnabled13 < 7 || force) { artTags[i] = ''; artListeners[i] = ''; locale[i] = ''; - const lfmBio = `${panelBio.cleanPth(cfg.pth.foLfmBio, !radioStream ? handles[i] : panelBio.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $Bio.clean(artist) + cfg.suffix.foLfmBio}.txt`; + const lfmBio = `${bio.panel.cleanPth(bioCfg.pth.foLfmBio, !radioStream ? handles[i] : bio.panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $Bio.clean(artist) + bioCfg.suffix.foLfmBio}.txt`; if ($Bio.file(lfmBio)) { const lBio = $Bio.open(lfmBio); - if (cfg.tagEnabled7 || force) { + if (bioCfg.tagEnabled7 || force) { artTags[i] = this.getTag(lBio, 'Top Tags: ').tag; if (artTags[i]) artTags[i] = this.lfmTidy(artTags[i]); } - if (cfg.tagEnabled8) artListeners[i] = this.getTag(lBio, lkw, false, 1, 'artist').tag; - if (cfg.tagEnabled9 || force) locale[i] = this.getTag(lBio, kww).tag; - if ((cfg.tagEnabled10 || force) && cfg.tagEnabled13 < 7) { - similarArtists[i] = this.getTag(lBio, panelBio.similarArtistsKey).tag; + if (bioCfg.tagEnabled8) artListeners[i] = this.getTag(lBio, lkw, false, 1, 'artist').tag; + if (bioCfg.tagEnabled9 || force) locale[i] = this.getTag(lBio, kww).tag; + if ((bioCfg.tagEnabled10 || force) && bioCfg.tagEnabled13 < 7) { + similarArtists[i] = this.getTag(lBio, bio.panel.similarArtistsKey).tag; if (similarArtists[i].length > 6) { similarArtists[i] = ''; this.simList.some(v => { @@ -271,23 +271,23 @@ class TaggerBio { } }); } - if (similarArtists[i]) $Bio.take(similarArtists[i], cfg.tagEnabled13); + if (similarArtists[i]) $Bio.take(similarArtists[i], bioCfg.tagEnabled13); } } } if (!similarArtists[i].length) similarArtists[i] = ''; - if (cfg.tagEnabled12 || !locale[i] && notify || force) { + if (bioCfg.tagEnabled12 || !locale[i] && notify || force) { artGenre_w[i] = ''; - const wikiBio = `${panelBio.cleanPth(cfg.pth.foWikiBio, !radioStream ? handles[i] : panelBio.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $Bio.clean(artist) + cfg.suffix.foWikiBio}.txt`; + const wikiBio = `${bio.panel.cleanPth(bioCfg.pth.foWikiBio, !radioStream ? handles[i] : bio.panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $Bio.clean(artist) + bioCfg.suffix.foWikiBio}.txt`; if ($Bio.file(wikiBio)) { const wBio = $Bio.open(wikiBio); artGenre_w[i] = this.getTag(wBio, 'Genre: ').tag; if (!locale[i] && notify) locale[i] = this.getTag(wBio, kww).tag; } } - if (cfg.tagEnabled4 || !locale[i] && notify || force) { + if (bioCfg.tagEnabled4 || !locale[i] && notify || force) { artGenre_am[i] = ''; - const amBio = `${panelBio.cleanPth(cfg.pth.foAmBio, !radioStream ? handles[i] : panelBio.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $Bio.clean(artist) + cfg.suffix.foAmBio}.txt`; + const amBio = `${bio.panel.cleanPth(bioCfg.pth.foAmBio, !radioStream ? handles[i] : bio.panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $Bio.clean(artist) + bioCfg.suffix.foAmBio}.txt`; if ($Bio.file(amBio)) { const aBio = $Bio.open(amBio); artGenre_am[i] = this.getTag(aBio, 'Genre: ').tag; @@ -309,21 +309,21 @@ class TaggerBio { if (albumArtist + album != cur_albumArtist + cur_album) { cur_albumArtist = albumArtist; cur_album = album; - if (cfg.tagEnabled0 || cfg.tagEnabled1 || cfg.tagEnabled2 || cfg.tagEnabled3 || force) { + if (bioCfg.tagEnabled0 || bioCfg.tagEnabled1 || bioCfg.tagEnabled2 || bioCfg.tagEnabled3 || force) { albGenre_am[i] = ''; amMoods[i] = ''; amRating[i] = ''; amThemes[i] = ''; - let amRev = `${panelBio.cleanPth(cfg.pth.foAmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix.foAmRev}.txt`; + let amRev = `${bio.panel.cleanPth(bioCfg.pth.foAmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix.foAmRev}.txt`; if (amRev.length > 259) { album = $Bio.abbreviate(album); - amRev = `${panelBio.cleanPth(cfg.pth.foAmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix.foAmRev}.txt`; + amRev = `${bio.panel.cleanPth(bioCfg.pth.foAmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix.foAmRev}.txt`; } if ($Bio.file(amRev)) { const aRev = $Bio.open(amRev); - if (cfg.tagEnabled0 || force) albGenre_am[i] = this.getTag(aRev, 'Genre: ').tag; - if (cfg.tagEnabled1 || force) amMoods[i] = this.getTag(aRev, 'Album Moods: ').tag; - if (cfg.tagEnabled2) { + if (bioCfg.tagEnabled0 || force) albGenre_am[i] = this.getTag(aRev, 'Genre: ').tag; + if (bioCfg.tagEnabled1 || force) amMoods[i] = this.getTag(aRev, 'Album Moods: ').tag; + if (bioCfg.tagEnabled2) { const b = aRev.indexOf('>> Album rating: ') + 17; const f = aRev.indexOf(' <<'); if (b != -1 && f != -1 && f > b) { @@ -331,32 +331,32 @@ class TaggerBio { if (amRating[i] == 0) amRating[i] = ''; } } - if (cfg.tagEnabled3 || force) amThemes[i] = this.getTag(aRev, 'Album Themes: ').tag; + if (bioCfg.tagEnabled3 || force) amThemes[i] = this.getTag(aRev, 'Album Themes: ').tag; } } - if (cfg.tagEnabled5 || cfg.tagEnabled6 || force) { + if (bioCfg.tagEnabled5 || bioCfg.tagEnabled6 || force) { albTags[i] = ''; albListeners[i] = ''; - let lfmRev = `${panelBio.cleanPth(cfg.pth.foLfmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix.foLfmRev}.txt`; + let lfmRev = `${bio.panel.cleanPth(bioCfg.pth.foLfmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix.foLfmRev}.txt`; if (lfmRev.length > 259) { album = $Bio.abbreviate(album); - lfmRev = `${panelBio.cleanPth(cfg.pth.foLfmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix.foLfmRev}.txt`; + lfmRev = `${bio.panel.cleanPth(bioCfg.pth.foLfmRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix.foLfmRev}.txt`; } if ($Bio.file(lfmRev)) { const lRev = $Bio.open(lfmRev); - if (cfg.tagEnabled5 || force) { + if (bioCfg.tagEnabled5 || force) { albTags[i] = this.getTag(lRev, 'Top Tags: ').tag; if (albTags[i]) albTags[i] = this.lfmTidy(albTags[i]); } - if (cfg.tagEnabled6) albListeners[i] = this.getTag(lRev, lkw, false, 1, 'album').tag; + if (bioCfg.tagEnabled6) albListeners[i] = this.getTag(lRev, lkw, false, 1, 'album').tag; } } - if (cfg.tagEnabled11 || force) { + if (bioCfg.tagEnabled11 || force) { albGenre_w[i] = ''; - let wikiRev = `${panelBio.cleanPth(cfg.pth.foWikiRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix.foWikiRev}.txt`; + let wikiRev = `${bio.panel.cleanPth(bioCfg.pth.foWikiRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix.foWikiRev}.txt`; if (wikiRev.length > 259) { album = $Bio.abbreviate(album); - wikiRev = `${panelBio.cleanPth(cfg.pth.foWikiRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${cfg.suffix.foWikiRev}.txt`; + wikiRev = `${bio.panel.cleanPth(bioCfg.pth.foWikiRev, handles[i], 'tag') + $Bio.clean(albumArtist)} - ${$Bio.clean(album)}${bioCfg.suffix.foWikiRev}.txt`; } if ($Bio.file(wikiRev)) { const wRev = $Bio.open(wikiRev); @@ -374,7 +374,7 @@ class TaggerBio { } } - for (let j = 0; j < 13; j++) this[`tagName${j}`] = !notify ? cfg[`tagName${j}`] : cfg[`tagName${j}_internal`].default_value; + for (let j = 0; j < 13; j++) this[`tagName${j}`] = !notify ? bioCfg[`tagName${j}`] : bioCfg[`tagName${j}_internal`].default_value; for (let i = 0; i < handles.Count; i++) { if (!cue[i] && (albGenre_am[i] || amMoods[i] || amRating[i] || amThemes[i] || artGenre_am[i] || albTags[i] || albListeners[i] || artTags[i] || artListeners[i] || locale[i] || similarArtists[i] || albGenre_w[i] || artGenre_w[i])) { diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-text.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-text.js index 48b91086..2ee2de12 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-text.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-text.js @@ -1,6 +1,6 @@ 'use strict'; -class Text { +class BioText { constructor() { const DT_CENTER = 0x00000001; const DT_RIGHT = 0x00000002; @@ -15,7 +15,7 @@ class Text { this.albumartist = ''; this.artist = ''; this.composition = ''; - this.countryCodes = `${cfg.storageFolder}country_codes.json`; + this.countryCodes = `${bioCfg.storageFolder}country_codes.json`; this.cur = []; this.cur_artist = ''; this.calc = true; @@ -39,8 +39,8 @@ class Text { this.topTags = ['Tags', 'Tags', 'Tags', 'Tags', 'Tag', '\u30bf\u30b0', 'Tagi', 'Tags', '\u0422\u0435\u0433\u0438', 'Taggar', 'Etiketler', '\u6807\u7b7e']; - pptBio.sourceHeading = $Bio.clamp(pptBio.sourceHeading, 0, 2); - pptBio.trackHeading = $Bio.clamp(pptBio.trackHeading, 0, 2); + bioSet.sourceHeading = $Bio.clamp(bioSet.sourceHeading, 0, 2); + bioSet.trackHeading = $Bio.clamp(bioSet.trackHeading, 0, 2); this.avail = { amalb: -1, @@ -65,7 +65,7 @@ class Text { cur_ix: -1, died: 'Died: |Gestorben: |Fallecido: |D\\u00e9c\\u00e9d\\u00e9(e) le: |Data di morte: |\\u6ca1\\u5e74: |Zmar\\u0142: |Data de falecimento: |\\u0414\\u0430\\u0442\\u0430 \\u0441\\u043c\\u0435\\u0440\\u0442\\u0438: |D\\u00f6d: |\\u00d6l\\u00fcm tarihi: |\\u901d\\u4e16:', drawn: 0, - fallbackText: pptBio.bioFallbackText.split('|'), + fallbackText: bioSet.bioFallbackText.split('|'), flag: null, flagCode: '', flagCountry: '', @@ -94,10 +94,10 @@ class Text { readerTag: false, scrollPos: {}, source: { - am: pptBio.sourcebio == 0, - lfm: pptBio.sourcebio == 1, - wiki: pptBio.sourcebio == 2, - txt: pptBio.sourcebio == 3 + am: bioSet.sourcebio == 0, + lfm: bioSet.sourcebio == 1, + wiki: bioSet.sourcebio == 2, + txt: bioSet.sourcebio == 3 }, sp: 0, subHeading: 0, @@ -121,9 +121,9 @@ class Text { }; this.bio.subhead = { - am: [cfg.amDisplayName, `${cfg.amDisplayName} ${this.bio.lang[cfg.lang.ix]}`], - lfm: [cfg.lfmDisplayName, `${cfg.lfmDisplayName} ${this.bio.lang[cfg.lang.ix]}`], - wiki: [cfg.wikiDisplayName, `${cfg.wikiDisplayName} ${this.bio.lang[cfg.lang.ix]}`], + am: [bioCfg.amDisplayName, `${bioCfg.amDisplayName} ${this.bio.lang[bioCfg.lang.ix]}`], + lfm: [bioCfg.lfmDisplayName, `${bioCfg.lfmDisplayName} ${this.bio.lang[bioCfg.lang.ix]}`], + wiki: [bioCfg.wikiDisplayName, `${bioCfg.wikiDisplayName} ${this.bio.lang[bioCfg.lang.ix]}`], txt: ['textreader', 'textreader'] }; @@ -249,7 +249,7 @@ class Text { cur: '', cur_ix: -1, drawn: 0, - fallbackText: pptBio.revFallbackText.split('|'), + fallbackText: bioSet.revFallbackText.split('|'), flag: null, flagCode: '', flagCountry: '', @@ -279,10 +279,10 @@ class Text { releaseDate: 'Release Date: |Ver\\u00f6ffentlichungsdatum: |Fecha De Lanzamiento: |Date De Sortie: |Data Di Pubblicazione: |\\u30ea\\u30ea\\u30fc\\u30b9\\u65e5: |Data Wydania: |Data De Lan\\u00e7amento: |\\u0414\\u0430\\u0442\\u0430 \\u0440\\u0435\\u043b\\u0438\\u0437\\u0430: |Utgivningsdatum: |Yay\\u0131nlanma Tarihi: |\\u53d1\\u884c\\u65e5\\u671f: ', scrollPos: {}, source: { - am: pptBio.sourcerev == 0, - lfm: pptBio.sourcerev == 1, - wiki: pptBio.sourcerev == 2, - txt: pptBio.sourcerev == 3 + am: bioSet.sourcerev == 0, + lfm: bioSet.sourcerev == 1, + wiki: bioSet.sourcerev == 2, + txt: bioSet.sourcerev == 3 }, subHeading: 0, summaryEnd: 0, @@ -304,24 +304,24 @@ class Text { x1: 0, x2: 0 }, - y: Math.round(Math.max(1, uiBio.font.main_h * 0.05)) + y: Math.round(Math.max(1, bio.ui.font.main_h * 0.05)) }; this.rev.subhead = { - am: [cfg.amDisplayName, `${cfg.amDisplayName} ${this.rev.lang[cfg.lang.ix]}`], - lfm: [cfg.lfmDisplayName, `${cfg.lfmDisplayName} ${this.rev.lang[cfg.lang.ix]}`], - wiki: [cfg.wikiDisplayName, `${cfg.wikiDisplayName} ${this.rev.lang[cfg.lang.ix]}`], + am: [bioCfg.amDisplayName, `${bioCfg.amDisplayName} ${this.rev.lang[bioCfg.lang.ix]}`], + lfm: [bioCfg.lfmDisplayName, `${bioCfg.lfmDisplayName} ${this.rev.lang[bioCfg.lang.ix]}`], + wiki: [bioCfg.wikiDisplayName, `${bioCfg.wikiDisplayName} ${this.rev.lang[bioCfg.lang.ix]}`], txt: ['textreader', 'textreader'] }; this.currentTrackTags = $Bio.debounce(() => { if (!$Bio.server) return; - const handle1 = $Bio.handle(panelBio.id.focus); - if (handle1) tagBio.write(new FbMetadbHandleList([handle1]), true, panelBio.id.focus); - if (tagBio.force) { - const handle2 = $Bio.handle(!panelBio.id.focus); + const handle1 = $Bio.handle(bio.panel.id.focus); + if (handle1) bio.tag.write(new FbMetadbHandleList([handle1]), true, bio.panel.id.focus); + if (bio.tag.force) { + const handle2 = $Bio.handle(!bio.panel.id.focus); const handlesSame = handle1 && handle2 && handle1.Compare(handle2); - if (handle2 && !handlesSame) tagBio.write(new FbMetadbHandleList([handle2]), true, !panelBio.id.focus); + if (handle2 && !handlesSame) bio.tag.write(new FbMetadbHandleList([handle2]), true, !bio.panel.id.focus); } }, 2000, { leading: true, @@ -333,9 +333,9 @@ class Text { // * METHODS * // activateTooltip(value, type) { - if (tooltipBio.Text == value && [this.rev.ix == this.rev.cur_ix, this.bio.ix == this.bio.cur_ix][type]) return; - tooltipBio.Text = value; - tooltipBio.Activate(); + if (bioTooltip.Text == value && [this.rev.ix == this.rev.cur_ix, this.bio.ix == this.bio.cur_ix][type]) return; + bioTooltip.Text = value; + bioTooltip.Activate(); } add(items, text) { @@ -344,10 +344,10 @@ class Text { } albCalc() { - if (!this.rev.text.length || pptBio.img_only || this.lyricsDisplayed()) return; - const font = !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying) || pptBio.sourceAll ? uiBio.font.main : uiBio.font.lyrics; + if (!this.rev.text.length || bioSet.img_only || this.lyricsDisplayed()) return; + const font = !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying) || bioSet.sourceAll ? bio.ui.font.main : bio.ui.font.lyrics; let j = 0; - this.line.h.rev = this.rev.loaded.txt && !pptBio.sourceAll && this.reader.rev.props ? uiBio.font.main_h + 1 * $Bio.scale : !this.reader.rev.txtLyrics || pptBio.sourceAll ? uiBio.font.main_h : uiBio.font.lyrics_h + 4 * $Bio.scale; + this.line.h.rev = this.rev.loaded.txt && !bioSet.sourceAll && this.reader.rev.props ? bio.ui.font.main_h + 1 * $Bio.scale : !this.reader.rev.txtLyrics || bioSet.sourceAll ? bio.ui.font.main_h : bio.ui.font.lyrics_h + 4 * $Bio.scale; this.rev.arr = []; $Bio.gr(1, 1, false, g => { @@ -356,21 +356,21 @@ class Text { let l = []; this.rev.summaryEnd = 0; let v = this.rev.text[k]; - if (panelBio.style.inclTrackRev && !$Bio.isArray(v)) { + if (bio.panel.style.inclTrackRev && !$Bio.isArray(v)) { const ti = v.match(/!\u00a6.+?$/gm); if (ti) { ti.forEach(w => { - if (g.CalcTextWidth(w, uiBio.font.subHeadTrack) > panelBio.text.w) { - const new_ti = `${g.EstimateLineWrap(w, uiBio.font.subHeadTrack, panelBio.text.w - g.CalcTextWidth('... ', uiBio.font.subHeadTrack))[0]}...`; + if (g.CalcTextWidth(w, bio.ui.font.subHeadTrack) > bio.panel.text.w) { + const new_ti = `${g.EstimateLineWrap(w, bio.ui.font.subHeadTrack, bio.panel.text.w - g.CalcTextWidth('... ', bio.ui.font.subHeadTrack))[0]}...`; v = v.replace(RegExp($Bio.regexEscape(w)), new_ti); } }); } } - if (!panelBio.summary.show || !v.includes('\u00a6End\u00a6')) { + if (!bio.panel.summary.show || !v.includes('\u00a6End\u00a6')) { if (!$Bio.isArray(v)) { if (v) { - l = g.EstimateLineWrap(v, font, panelBio.text.w); + l = g.EstimateLineWrap(v, font, bio.panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({ text: l[i].trim() }); } } else if (!this.isLyricsArr('rev', v)) { @@ -381,34 +381,34 @@ class Text { const revSummary = revText[0].trim(); const revMain = revText[1].trim(); if (revSummary) { - l = g.EstimateLineWrap(revSummary, uiBio.font.summary, panelBio.text.w); + l = g.EstimateLineWrap(revSummary, bio.ui.font.summary, bio.panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({ text: l[i].trim() }); for (let i = 0; i < arr.length; i++) arr[i].text = arr[i].text.replace(/^\u2219\s|^\|\s+/, '').replace(/\s*\|$/, ''); this.rev.summaryEnd = arr.length; if (revMain) arr.push({ text: '' }); } if (revMain) { - l = g.EstimateLineWrap(revMain, font, panelBio.text.w); + l = g.EstimateLineWrap(revMain, font, bio.panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({ text: l[i].trim() }); } } - const y = this.rev.loaded.txt && this.reader.rev.nowplaying && !pptBio.sourceAll && pptBio.vCenter ? Math.max(panelBio.text.t + (panelBio.text.h - arr.length * this.line.h.rev) / 2, panelBio.text.t) : panelBio.text.t; + const y = this.rev.loaded.txt && this.reader.rev.nowplaying && !bioSet.sourceAll && bioSet.vCenter ? Math.max(bio.panel.text.t + (bio.panel.text.h - arr.length * this.line.h.rev) / 2, bio.panel.text.t) : bio.panel.text.t; arr.forEach((v, i, ary) => { if (v.sectionHeading) j = 0; - v.align = v.property ? (pptBio.rowStripes ? this.lc : this.lp) : !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying && pptBio.hCenter) || pptBio.sourceAll ? this.l : this.cc; - v.col = v.property && v.sectionHeading ? uiBio.col.accent : this.rev.subHeading && !i ? uiBio.col.source : i < this.rev.summaryEnd ? uiBio.col.summary : uiBio.col.text; - v.dropShadowLevel = !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying) || pptBio.sourceAll ? false : pptBio.dropShadowLevel; + v.align = v.property ? (bioSet.rowStripes ? this.lc : this.lp) : !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying && bioSet.hCenter) || bioSet.sourceAll ? this.l : this.cc; + v.col = v.property && v.sectionHeading ? bio.ui.col.accent : this.rev.subHeading && !i ? bio.ui.col.source : i < this.rev.summaryEnd ? bio.ui.col.summary : bio.ui.col.text; + v.dropShadowLevel = !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying) || bioSet.sourceAll ? false : bioSet.dropShadowLevel; v.dropNegativeShadowLevel = v.dropShadowLevel && v.dropShadowLevel > 1 ? Math.floor(v.dropShadowLevel / 2) : 0; - v.font = !this.rev.subHeading || i ? (i < this.rev.summaryEnd ? uiBio.font.summary : font) : uiBio.font.subHeadSource; + v.font = !this.rev.subHeading || i ? (i < this.rev.summaryEnd ? bio.ui.font.summary : font) : bio.ui.font.subHeadSource; v.h1 = this.line.h.rev; v.h2 = this.line.h.rev + 1; $Bio.source.amLfmWikiTxt.forEach((w, i) => { - v[`${w}Line`] = (!v.property ? v.text : v.name) == this.rev.subhead[w][1] && !(this.ratingPos.line && panelBio.summary.show); + v[`${w}Line`] = (!v.property ? v.text : v.name) == this.rev.subhead[w][1] && !(this.ratingPos.line && bio.panel.summary.show); if (v[`${w}Line`]) { - const v_w = g.CalcTextWidth(`${v.text} `, this.ratingPos.line ? uiBio.font.main : uiBio.font.subHeadSource); - v[`${w}LineX1`] = i < 2 ? panelBio.text.l + v_w + (this.rating[w] >= 0 && (this.ratingPos.subHeading || this.ratingPos.line) ? butBio.rating.w2 : 0) + this.bio.sp : panelBio.text.l + this.rev[`${w}_w`].nohd + this.bio.sp; // noHd - v[`${w}LineX2`] = Math.max(v[`${w}LineX1`], panelBio.text.l + panelBio.text.w); + const v_w = g.CalcTextWidth(`${v.text} `, this.ratingPos.line ? bio.ui.font.main : bio.ui.font.subHeadSource); + v[`${w}LineX1`] = i < 2 ? bio.panel.text.l + v_w + (this.rating[w] >= 0 && (this.ratingPos.subHeading || this.ratingPos.line) ? bio.but.rating.w2 : 0) + this.bio.sp : bio.panel.text.l + this.rev[`${w}_w`].nohd + this.bio.sp; // noHd + v[`${w}LineX2`] = Math.max(v[`${w}LineX1`], bio.panel.text.l + bio.panel.text.w); } }); v.offset = 0; @@ -417,34 +417,34 @@ class Text { this.formatItemProperties(v, j); v.subHeading = this.rev.subHeading && !i ? this.rev.subHeading : 0; ['am', 'lfm'].forEach(w => { - v[`${w}SubHeadingRating`] = this.ratingPos.subHeading && v.text == this.rev.subhead[w][pptBio.heading ? 0 : 1] && this.rating[w] >= 0; - if (v[`${w}SubHeadingRating`]) v[`${w}SubHeadingRatingX`] = panelBio.text.l + (pptBio.heading ? this.rev[`${w}_w`].hd : this.rev[`${w}_w`].nohd); + v[`${w}SubHeadingRating`] = this.ratingPos.subHeading && v.text == this.rev.subhead[w][bioSet.heading ? 0 : 1] && this.rating[w] >= 0; + if (v[`${w}SubHeadingRating`]) v[`${w}SubHeadingRatingX`] = bio.panel.text.l + (bioSet.heading ? this.rev[`${w}_w`].hd : this.rev[`${w}_w`].nohd); - v[`${w}LineRating`] = this.ratingPos.line && this.rating[w] >= 0 && v.text == this.rev.subhead[w][pptBio.heading ? 0 : 1]; + v[`${w}LineRating`] = this.ratingPos.line && this.rating[w] >= 0 && v.text == this.rev.subhead[w][bioSet.heading ? 0 : 1]; if (v[`${w}LineRating`]) { - const v_w = g.CalcTextWidth(`${v.text} `, uiBio.font.main); - v[`${w}LineRatingX`] = panelBio.text.l + v_w; + const v_w = g.CalcTextWidth(`${v.text} `, bio.ui.font.main); + v[`${w}LineRatingX`] = bio.panel.text.l + v_w; } }); if (v.text.startsWith('!\u00a6')) { - v.col = uiBio.col.track; - v.font = uiBio.font.subHeadTrack; + v.col = bio.ui.col.track; + v.font = bio.ui.font.subHeadTrack; v.song = true; - const songLine = !pptBio.heading ? (panelBio.style.inclTrackRev != 2 ? true : !ary[0].subHeading) : false; + const songLine = !bioSet.heading ? (bio.panel.style.inclTrackRev != 2 ? true : !ary[0].subHeading) : false; if (songLine) { v.songLine = true; - const v_w = g.CalcTextWidth(`${v.text} `, uiBio.font.subHeadTrack) - v.songX1 = panelBio.text.l + v_w; - v.songX2 = Math.max(v.songX1, panelBio.text.l + panelBio.text.w); + const v_w = g.CalcTextWidth(`${v.text} `, bio.ui.font.subHeadTrack) + v.songX1 = bio.panel.text.l + v_w; + v.songX2 = Math.max(v.songX1, bio.panel.text.l + bio.panel.text.w); } v.text = v.text.replace('!\u00a6', ''); } - if ((this.rev.loaded.wiki || pptBio.sourceAll) && (v.text.startsWith('==') || v.text.endsWith('=='))) { - v.font = uiBio.font.subHeadWiki; - v.offset = uiBio.font.main_h * 0.125; + if ((this.rev.loaded.wiki || bioSet.sourceAll) && (v.text.startsWith('==') || v.text.endsWith('=='))) { + v.font = bio.ui.font.subHeadWiki; + v.offset = bio.ui.font.main_h * 0.125; v.sectionHeading = true; } - if (this.rev.subHeading && !i && pptBio.heading) v.offset = uiBio.font.main_h * 0.2; + if (this.rev.subHeading && !i && bioSet.heading) v.offset = bio.ui.font.main_h * 0.2; v.y = y; j++; }); @@ -456,12 +456,12 @@ class Text { }); this.tidyWiki('rev'); - butBio.refresh(true); - alb_scrollbar.reset(); - this.line.drawn.rev = this.reader.rev.txtLyrics && !pptBio.sourceAll || this.rev.loaded.txt && !pptBio.sourceAll && this.reader.rev.props ? Math.floor(panelBio.lines_drawn * uiBio.font.main_h / this.line.h.rev) : panelBio.lines_drawn; - alb_scrollbar.metrics(panelBio.sbar.x, panelBio.sbar.y, uiBio.sbar.w, panelBio.sbar.h, this.line.drawn.rev, this.line.h.rev, false); - alb_scrollbar.setRows(this.rev.arr.length); - if (this.ratingPos.subHeading || this.ratingPos.line) this.rating.y = Math.round((uiBio.font.main_h - butBio.rating.h1 / 2) / 1.8); + bio.but.refresh(true); + bio.alb_scrollbar.reset(); + this.line.drawn.rev = this.reader.rev.txtLyrics && !bioSet.sourceAll || this.rev.loaded.txt && !bioSet.sourceAll && this.reader.rev.props ? Math.floor(bio.panel.lines_drawn * bio.ui.font.main_h / this.line.h.rev) : bio.panel.lines_drawn; + bio.alb_scrollbar.metrics(bio.panel.sbar.x, bio.panel.sbar.y, bio.ui.sbar.w, bio.panel.sbar.h, this.line.drawn.rev, this.line.h.rev, false); + bio.alb_scrollbar.setRows(this.rev.arr.length); + if (this.ratingPos.subHeading || this.ratingPos.line) this.rating.y = Math.round((bio.ui.font.main_h - bio.but.rating.h1 / 2) / 1.8); this.getScrollPos(); } @@ -470,49 +470,49 @@ class Text { this.mod.curAmRev = this.mod.curLfmRev = this.mod.curWikiRev = 1; this.rev.checkedTrackSubHead = this.done.amRev = this.done.lfmRev = this.done.wikiRev = false; this.rev.text = []; - butBio.setScrollBtnsHide(); + bio.but.setScrollBtnsHide(); } albumReset(upd) { - if (panelBio.lock) return; + if (bio.panel.lock) return; this.id.curAlbum = this.id.album; - this.id.album = name.albID(panelBio.id.focus, 'simple'); + this.id.album = bio.name.albID(bio.panel.id.focus, 'simple'); const new_album = this.id.album != this.id.curAlbum; if (new_album) this.id.alb = ''; let new_composition = false; - if (pptBio.classicalMusicMode) { + if (bioSet.classicalMusicMode) { this.id.curComp = this.id.composition; - this.id.composition = $Bio.eval(cfg.tf.artist + cfg.tf.albumArtist + cfg.tf.composition, panelBio.id.focus); + this.id.composition = $Bio.eval(bioCfg.tf.artist + bioCfg.tf.albumArtist + bioCfg.tf.composition, bio.panel.id.focus); new_composition = this.id.composition != this.id.curComp; } if (new_album || new_composition || upd) { - this.album = name.album(panelBio.id.focus); - this.albumartist = name.albumArtist(panelBio.id.focus); - this.composition = name.composition(panelBio.id.focus); + this.album = bio.name.album(bio.panel.id.focus); + this.albumartist = bio.name.albumArtist(bio.panel.id.focus); + this.composition = bio.name.composition(bio.panel.id.focus); this.albumFlush(); this.rev.lookUp = false; } - if (panelBio.style.inclTrackRev) { + if (bio.panel.style.inclTrackRev) { this.id.curTr = this.id.tr; - this.id.tr = name.trackID(panelBio.id.focus); + this.id.tr = bio.name.trackID(bio.panel.id.focus); const new_track = this.id.tr != this.id.curTr; if (new_track) { this.rev.checkedTrackSubHead = this.done.amRev = this.done.lfmRev = this.done.wikiRev = false; - if (panelBio.style.inclTrackRev == 1 && !new_album) this.logScrollPos('rev'); + if (bio.panel.style.inclTrackRev == 1 && !new_album) this.logScrollPos('rev'); } } } amBio(a) { - const aBio = panelBio.getPth('bio', panelBio.id.focus, this.artist, '', '', cfg.supCache, a, '', '', 'foAmBio', true).pth; + const aBio = bio.panel.getPth('bio', bio.panel.id.focus, this.artist, '', '', bioCfg.supCache, a, '', '', 'foAmBio', true).pth; if (!$Bio.file(aBio)) return; this.mod.amBio = $Bio.lastModified(aBio) || 0; if (this.mod.amBio == this.mod.curAmBio) return; this.bio.am = $Bio.open(aBio).trim(); this.bio.am = this.bio.am.replace(/, Jr\./g, ' Jr.'); - this.bio.am = this.formatText('amBio', this.bio.am, panelBio.summary.genre ? { limit: 6, list: true, key: 'Genre: ' } : {}, !panelBio.summary.other ? {} : { list: true, key: 'Group Members: ', prefix: true, suffix: true }, panelBio.summary.date ? { key: 'Formed: |Born: ' } : {}, panelBio.summary.date ? { key: 'Died: ' } : {}, panelBio.summary.date ? { key: 'Active: ' } : {}).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); + this.bio.am = this.formatText('amBio', this.bio.am, bio.panel.summary.genre ? { limit: 6, list: true, key: 'Genre: ' } : {}, !bio.panel.summary.other ? {} : { list: true, key: 'Group Members: ', prefix: true, suffix: true }, bio.panel.summary.date ? { key: 'Formed: |Born: ' } : {}, bio.panel.summary.date ? { key: 'Died: ' } : {}, bio.panel.summary.date ? { key: 'Active: ' } : {}).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); this.newText = true; this.mod.curAmBio = this.mod.amBio; } @@ -525,44 +525,44 @@ class Text { let trk = ''; let writer = ''; - if (!pptBio.classicalMusicMode || panelBio.alb.ix) { - aRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.album, '', cfg.supCache, a, aa, l, 'foAmRev', true).pth; - } else if (!panelBio.alb.ix) { - aRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.composition, '', cfg.supCache, a, aa, c, 'foAmRev', true).pth; + if (!bioSet.classicalMusicMode || bio.panel.alb.ix) { + aRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.album, '', bioCfg.supCache, a, aa, l, 'foAmRev', true).pth; + } else if (!bio.panel.alb.ix) { + aRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.composition, '', bioCfg.supCache, a, aa, c, 'foAmRev', true).pth; if ($Bio.file(aRev)) foundComp = true; } this.rev.amFallback = !foundComp; - if (!$Bio.file(aRev) && pptBio.classicalAlbFallback && !panelBio.alb.ix && panelBio.style.inclTrackRev != 2) { - aRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.album, '', cfg.supCache, a, aa, l, 'foAmRev', true).pth; + if (!$Bio.file(aRev) && bioSet.classicalAlbFallback && !bio.panel.alb.ix && bio.panel.style.inclTrackRev != 2) { + aRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.album, '', bioCfg.supCache, a, aa, l, 'foAmRev', true).pth; } this.avail.amalb = $Bio.file(aRev) ? 0 : -1; if (this.avail.amalb == -1) { this.rating.am = -1; - butBio.check(); - if (!panelBio.style.inclTrackRev || pptBio.classicalMusicMode && !pptBio.classicalAlbFallback) { + bio.but.check(); + if (!bio.panel.style.inclTrackRev || bioSet.classicalMusicMode && !bioSet.classicalAlbFallback) { this.rev.amAlb = ''; return; } } trk = this.track.toLowerCase(); - trackRev = $Bio.jsonParse(panelBio.getPth('track', panelBio.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foAmRev', true).pth, false, 'file'); + trackRev = $Bio.jsonParse(bio.panel.getPth('track', bio.panel.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foAmRev', true).pth, false, 'file'); this.avail.amtrk = this.isTrackRevAvail('am', trackRev[trk]); - if (panelBio.style.inclTrackRev && trackRev[trk] && trackRev[trk].update) am_tr_mod = trackRev[trk].update; + if (bio.panel.style.inclTrackRev && trackRev[trk] && trackRev[trk].update) am_tr_mod = trackRev[trk].update; this.mod.amRev = ($Bio.lastModified(aRev) || 0) + (am_tr_mod || 0); if (this.mod.amRev == this.mod.curAmRev) return; this.rev.amAlb = ''; - if (panelBio.style.inclTrackRev != 2 || foundComp) this.rev.amAlb = $Bio.open(aRev).trim(); + if (bio.panel.style.inclTrackRev != 2 || foundComp) this.rev.amAlb = $Bio.open(aRev).trim(); this.rev.amAlb = this.rev.amAlb.replace('Genre: ', 'Album Genres: '); this.newText = true; this.mod.curAmRev = this.mod.amRev; this.rating.am = -1; const b = this.rev.amAlb.indexOf('>> Album rating: ') + 17; const f = this.rev.amAlb.indexOf(' <<'); - if (panelBio.style.inclTrackRev != 2 || foundComp) { - if (pptBio.amRating) { + if (bio.panel.style.inclTrackRev != 2 || foundComp) { + if (bioSet.amRating) { if (b != -1 && f != -1 && f > b) this.rating.am = this.rev.amAlb.substring(b, f); if (!isNaN(this.rating.am) && this.rating.am != 0 && this.rating.am != -1) this.rating.am *= 2; else this.rating.am = -1; @@ -574,8 +574,8 @@ class Text { } this.rev.am = this.rev.amAlb; let needTrackSubHeading = false; - if (!pptBio.classicalMusicMode || !foundComp) { - if (panelBio.style.inclTrackRev) { + if (!bioSet.classicalMusicMode || !foundComp) { + if (bio.panel.style.inclTrackRev) { if (trackRev && trackRev[trk]) { const o = trackRev[trk]; let releaseYear = $Bio.getProp(o, 'date', ''); @@ -587,7 +587,7 @@ class Text { } let wiki = $Bio.getProp(o, 'wiki', ''); wiki = this.add([composer], wiki); - const showGenres = !pptBio.autoOptimiseText || !this.rev.amAlb; + const showGenres = !bioSet.autoOptimiseText || !this.rev.amAlb; if (showGenres) { const get = (item) => { const it2 = $Bio.titlecase(item); @@ -601,10 +601,10 @@ class Text { const author = $Bio.getProp(o, 'author', ''); wiki = this.add([releaseYear, author], wiki); if (wiki) { - if (pptBio.trackHeading == 1 && (this.rev.amAlb || !pptBio.heading) || pptBio.trackHeading == 2) { + if (bioSet.trackHeading == 1 && (this.rev.amAlb || !bioSet.heading) || bioSet.trackHeading == 2) { this.rev.amTrackHeading = false; if (this.rev.amAlb) { - trackRev = `!\u00a6${this.tf(pptBio.trackSubHeading, pptBio.artistView, true)}\r\n\r\n${wiki}`; + trackRev = `!\u00a6${this.tf(bioSet.trackSubHeading, bioSet.artistView, true)}\r\n\r\n${wiki}`; } else { trackRev = wiki; needTrackSubHeading = true; @@ -615,23 +615,23 @@ class Text { } this.rev.am = this.add([trackRev], this.rev.amAlb); } else { - this.rev.amTrackHeading = panelBio.style.inclTrackRev == 2; + this.rev.amTrackHeading = bio.panel.style.inclTrackRev == 2; } } else { - this.rev.amTrackHeading = panelBio.style.inclTrackRev == 2; + this.rev.amTrackHeading = bio.panel.style.inclTrackRev == 2; } } } else this.rev.amTrackHeading = false; - this.rev.am = this.formatText('amRev', this.rev.am, panelBio.summary.genre ? { limit: 6, list: true, key: this.rev.amAlb ? 'Album Genres: ' : 'Track Genres: ' } : {}, !panelBio.summary.other ? {} : { limit: 6, list: true, key: this.rev.amAlb ? 'Album Moods: ' : 'Track Moods: ', prefix: true }, panelBio.summary.date ? { key: this.rev.amAlb ? 'Release Date: ' : 'Release Year: ' } : {}, { str: this.rating.amStr }).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); - if (needTrackSubHeading) this.rev.am = `!\u00a6${this.tf(pptBio.trackSubHeading, pptBio.artistView, true)}\r\n\r\n${this.rev.am}`; - if (!this.rev.am) butBio.check(); + this.rev.am = this.formatText('amRev', this.rev.am, bio.panel.summary.genre ? { limit: 6, list: true, key: this.rev.amAlb ? 'Album Genres: ' : 'Track Genres: ' } : {}, !bio.panel.summary.other ? {} : { limit: 6, list: true, key: this.rev.amAlb ? 'Album Moods: ' : 'Track Moods: ', prefix: true }, bio.panel.summary.date ? { key: this.rev.amAlb ? 'Release Date: ' : 'Release Year: ' } : {}, { str: this.rating.amStr }).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); + if (needTrackSubHeading) this.rev.am = `!\u00a6${this.tf(bioSet.trackSubHeading, bioSet.artistView, true)}\r\n\r\n${this.rev.am}`; + if (!this.rev.am) bio.but.check(); } artCalc() { - if (!this.bio.text.length || pptBio.img_only || this.lyricsDisplayed()) return; - const font = !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying) || pptBio.sourceAll ? uiBio.font.main : uiBio.font.lyrics; + if (!this.bio.text.length || bioSet.img_only || this.lyricsDisplayed()) return; + const font = !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying) || bioSet.sourceAll ? bio.ui.font.main : bio.ui.font.lyrics; let j = 0; - this.line.h.bio = this.bio.loaded.txt && !pptBio.sourceAll && this.reader.bio.props ? uiBio.font.main_h + 1 * $Bio.scale : !this.reader.bio.txtLyrics || pptBio.sourceAll ? uiBio.font.main_h : uiBio.font.lyrics_h + 4 * $Bio.scale; + this.line.h.bio = this.bio.loaded.txt && !bioSet.sourceAll && this.reader.bio.props ? bio.ui.font.main_h + 1 * $Bio.scale : !this.reader.bio.txtLyrics || bioSet.sourceAll ? bio.ui.font.main_h : bio.ui.font.lyrics_h + 4 * $Bio.scale; this.bio.arr = []; this.bio.text.forEach(v => { @@ -639,10 +639,10 @@ class Text { this.bio.summaryEnd = 0; let arr = []; $Bio.gr(1, 1, false, g => { - if (!panelBio.summary.show || !v.includes('\u00a6End\u00a6')) { + if (!bio.panel.summary.show || !v.includes('\u00a6End\u00a6')) { if (!$Bio.isArray(v)) { if (v) { - l = g.EstimateLineWrap(v, font, panelBio.text.w); + l = g.EstimateLineWrap(v, font, bio.panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({ text: l[i].trim() }); } } else if (!this.isLyricsArr('bio', v)) { @@ -653,46 +653,46 @@ class Text { const bioSummary = bioText[0].trim(); const bioMain = bioText[1].trim(); if (bioSummary) { - l = g.EstimateLineWrap(bioSummary, uiBio.font.summary, panelBio.text.w); + l = g.EstimateLineWrap(bioSummary, bio.ui.font.summary, bio.panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({ text: l[i].trim() }); for (let i = 0; i < arr.length; i++) arr[i].text = arr[i].text.replace(/^\u2219\s|^\|\s+/, '').replace(/\s*\|$/, ''); this.bio.summaryEnd = arr.length; if (bioMain) arr.push({ text: '' }); } if (bioMain) { - l = g.EstimateLineWrap(bioMain, font, panelBio.text.w); + l = g.EstimateLineWrap(bioMain, font, bio.panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({ text: l[i].trim() }); } } }); - const y = this.bio.loaded.txt && this.reader.bio.nowplaying && !pptBio.sourceAll && pptBio.vCenter ? Math.max(panelBio.text.t + (panelBio.text.h - arr.length * this.line.h.bio) / 2, panelBio.text.t) : panelBio.text.t; + const y = this.bio.loaded.txt && this.reader.bio.nowplaying && !bioSet.sourceAll && bioSet.vCenter ? Math.max(bio.panel.text.t + (bio.panel.text.h - arr.length * this.line.h.bio) / 2, bio.panel.text.t) : bio.panel.text.t; arr.forEach((v, i) => { if (v.sectionHeading) j = 0; - v.align = v.property ? (pptBio.rowStripes ? this.lc : this.lp) : !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying && pptBio.hCenter) || pptBio.sourceAll ? this.l : this.cc; - v.col = v.property && v.sectionHeading ? uiBio.col.accent : this.bio.subHeading && !i ? uiBio.col.source : i < this.bio.summaryEnd ? uiBio.col.summary : uiBio.col.text; - v.dropShadowLevel = !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying) || pptBio.sourceAll ? false : pptBio.dropShadowLevel; + v.align = v.property ? (bioSet.rowStripes ? this.lc : this.lp) : !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying && bioSet.hCenter) || bioSet.sourceAll ? this.l : this.cc; + v.col = v.property && v.sectionHeading ? bio.ui.col.accent : this.bio.subHeading && !i ? bio.ui.col.source : i < this.bio.summaryEnd ? bio.ui.col.summary : bio.ui.col.text; + v.dropShadowLevel = !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying) || bioSet.sourceAll ? false : bioSet.dropShadowLevel; v.dropNegativeShadowLevel = v.dropShadowLevel && v.dropShadowLevel > 1 ? Math.floor(v.dropShadowLevel / 2) : 0; - v.font = !this.bio.subHeading || i ? (i < this.bio.summaryEnd ? uiBio.font.summary : font) : uiBio.font.subHeadSource; + v.font = !this.bio.subHeading || i ? (i < this.bio.summaryEnd ? bio.ui.font.summary : font) : bio.ui.font.subHeadSource; v.h1 = this.line.h.bio; v.h2 = this.line.h.bio + 1; $Bio.source.amLfmWikiTxt.forEach(w => { v[`${w}Line`] = (!v.property ? v.text : v.name) == this.bio.subhead[w][1]; if (v[`${w}Line`]) { - v[`${w}LineX1`] = panelBio.text.l + this.bio[`${w}_w`].nohd + this.bio.sp; - v[`${w}LineX2`] = Math.max(v[`${w}LineX1`], panelBio.text.l + panelBio.text.w); + v[`${w}LineX1`] = bio.panel.text.l + this.bio[`${w}_w`].nohd + this.bio.sp; + v[`${w}LineX2`] = Math.max(v[`${w}LineX1`], bio.panel.text.l + bio.panel.text.w); } }); v.offset = 0; v.membersTooltip = this.bio.membersTooltip; v.members_tt_needed = v.membersTooltip && i < this.bio.summaryEnd && v.text.startsWith('Members: '); this.formatItemProperties(v, j); - if ((this.bio.loaded.wiki || pptBio.sourceAll) && (v.text.startsWith('==') || v.text.endsWith('=='))) { - v.font = uiBio.font.subHeadWiki; - v.offset = uiBio.font.main_h * 0.125; + if ((this.bio.loaded.wiki || bioSet.sourceAll) && (v.text.startsWith('==') || v.text.endsWith('=='))) { + v.font = bio.ui.font.subHeadWiki; + v.offset = bio.ui.font.main_h * 0.125; v.sectionHeading = true; } - if (this.bio.subHeading && !i && pptBio.heading) v.offset = uiBio.font.main_h * 0.2; + if (this.bio.subHeading && !i && bioSet.heading) v.offset = bio.ui.font.main_h * 0.2; v.y = y; j++; }); @@ -701,11 +701,11 @@ class Text { }); this.tidyWiki('bio'); - butBio.refresh(true); - art_scrollbar.reset(); - this.line.drawn.bio = this.reader.bio.txtLyrics && !pptBio.sourceAll || this.bio.loaded.txt && !pptBio.sourceAll && this.reader.bio.props ? Math.floor(panelBio.lines_drawn * uiBio.font.main_h / this.line.h.bio) : panelBio.lines_drawn; - art_scrollbar.metrics(panelBio.sbar.x, panelBio.sbar.y, uiBio.sbar.w, panelBio.sbar.h, this.line.drawn.bio, this.line.h.bio, false); - art_scrollbar.setRows(this.bio.arr.length); + bio.but.refresh(true); + bio.art_scrollbar.reset(); + this.line.drawn.bio = this.reader.bio.txtLyrics && !bioSet.sourceAll || this.bio.loaded.txt && !bioSet.sourceAll && this.reader.bio.props ? Math.floor(bio.panel.lines_drawn * bio.ui.font.main_h / this.line.h.bio) : bio.panel.lines_drawn; + bio.art_scrollbar.metrics(bio.panel.sbar.x, bio.panel.sbar.y, bio.ui.sbar.w, bio.panel.sbar.h, this.line.drawn.bio, this.line.h.bio, false); + bio.art_scrollbar.setRows(this.bio.arr.length); this.getScrollPos(); } @@ -714,13 +714,13 @@ class Text { this.mod.amBio = this.bio.am = this.mod.lfmBio = this.bio.lfm = this.mod.wikiBio = this.bio.wiki = this.bio.txt = ''; this.mod.curAmBio = this.mod.curLfmBio = this.mod.curWikiBio = '1'; this.bio.text = []; - butBio.setScrollBtnsHide(); + bio.but.setScrollBtnsHide(); } artistReset(upd) { - if (panelBio.artistsSame() || panelBio.lock) return; + if (bio.panel.artistsSame() || bio.panel.lock) return; this.cur_artist = this.artist; - this.artist = name.artist(panelBio.id.focus); + this.artist = bio.name.artist(bio.panel.id.focus); const new_artist = this.artist != this.cur_artist; if (new_artist || upd) { this.bio.lookUp = false; @@ -729,8 +729,8 @@ class Text { } bioPth(n) { - if (pptBio.img_only) return ['', '', false, false]; - return panelBio.getPth('bio', panelBio.id.focus, this.artist, '', '', cfg.supCache, $Bio.clean(this.artist), '', '', `fo${n}Bio`, false); + if (bioSet.img_only) return ['', '', false, false]; + return bio.panel.getPth('bio', bio.panel.id.focus, this.artist, '', '', bioCfg.supCache, $Bio.clean(this.artist), '', '', `fo${n}Bio`, false); } checkAbb(str) { @@ -750,7 +750,7 @@ class Text { } checkLyrics(n, lyr) { - this.reader[n].txtLyrics = this.reader[n].lyrics && (pptBio.sourceAll || (this.isSynced(lyr) ? !pptBio.scrollSynced : !pptBio.scrollUnsynced)); + this.reader[n].txtLyrics = this.reader[n].lyrics && (bioSet.sourceAll || (this.isSynced(lyr) ? !bioSet.scrollSynced : !bioSet.scrollUnsynced)); } checkNewLine(sub, n) { @@ -760,8 +760,8 @@ class Text { for (let i = 1; i < n; i++) { cur_str = cur_str && sub[i] ? `${cur_str} | ${sub[i]}` : cur_str || sub[i]; } - $Bio.gr(1, 1, false, g => w = g.CalcTextWidth(cur_str, uiBio.font.summary)); - return sub[n] = cur_str && w < panelBio.text.w ? `\r\n${sub[n]}` : sub[n]; + $Bio.gr(1, 1, false, g => w = g.CalcTextWidth(cur_str, bio.ui.font.summary)); + return sub[n] = cur_str && w < bio.panel.text.w ? `\r\n${sub[n]}` : sub[n]; } checkStr(sub, n) { @@ -773,17 +773,17 @@ class Text { cur_str = cur_str.split('\r\n'); cur_str = cur_str[1] || cur_str[0]; $Bio.gr(1, 1, false, g => { - let w = g.CalcTextWidth(cur_str, uiBio.font.summary); - if (w < panelBio.text.w) return sub[n]; + let w = g.CalcTextWidth(cur_str, bio.ui.font.summary); + if (w < bio.panel.text.w) return sub[n]; cur_str = ''; for (let i = 1; i < n; i++) { cur_str = cur_str && sub[i] ? `${cur_str} | ${sub[i]}` : cur_str || sub[i]; } - w = g.CalcTextWidth(cur_str, uiBio.font.summary); - const precedingSingleLineStr = w < panelBio.text.w; - return precedingSingleLineStr ? (w > panelBio.text.w ? sub[n] : `\r\n${sub[n]}`) : sub[n]; + w = g.CalcTextWidth(cur_str, bio.ui.font.summary); + const precedingSingleLineStr = w < bio.panel.text.w; + return precedingSingleLineStr ? (w > bio.panel.text.w ? sub[n] : `\r\n${sub[n]}`) : sub[n]; }); } @@ -793,13 +793,13 @@ class Text { const traceName = onLine && item.tt.needed1 && x >= item.tt.x1 && x <= item.tt.x1 + item.tt.w1; const traceValue = onLine && item.tt.needed2 && x >= item.tt.x2 && x <= item.tt.x2 + item.tt.w2; const text = traceName ? item.name || item.membersTooltip || item.composersTooltip : traceValue ? item.value : ''; - if (text != tooltipBio.Text) this.deactivateTooltip(); + if (text != bioTooltip.Text) this.deactivateTooltip(); if (!traceName && !traceValue || !item.tt.needed1 && !item.tt.needed2) { this.deactivateTooltip(); return; } this.activateTooltip(text, artistView ? 1 : 0); - timerBio.tooltip(); + bio.timer.tooltip(); } checkTooltip(item, x1, needed1, w1, x2, needed2, w2, y) { @@ -815,9 +815,9 @@ class Text { } deactivateTooltip() { - if (!tooltipBio.Text || butBio.traceBtn) return; - tooltipBio.Text = ''; - tooltipBio.Deactivate(); + if (!bioTooltip.Text || bio.but.traceBtn) return; + bioTooltip.Text = ''; + bioTooltip.Deactivate(); } different(a, b) { @@ -825,128 +825,128 @@ class Text { } draw(gr) { - if (!pptBio.img_only) { + if (!bioSet.img_only) { this.getTxtFallback(); - if (pptBio.typeOverlay && pptBio.style > 3 && !pptBio.img_only && !pptBio.text_only) { + if (bioSet.typeOverlay && bioSet.style > 3 && !bioSet.img_only && !bioSet.text_only) { gr.SetSmoothingMode(2); let o = 0; - switch (pptBio.typeOverlay) { + switch (bioSet.typeOverlay) { case 1: - gr.FillSolidRect(panelBio.tbox.l - 1, panelBio.tbox.t - 1, panelBio.tbox.w + 1, panelBio.tbox.h + 1, uiBio.col.rectOv); + gr.FillSolidRect(bio.panel.tbox.l - 1, bio.panel.tbox.t - 1, bio.panel.tbox.w + 1, bio.panel.tbox.h + 1, bio.ui.col.rectOv); break; case 2: - o = Math.round(uiBio.overlay.borderWidth / 2); - gr.FillSolidRect(panelBio.tbox.l + o, panelBio.tbox.t + o, panelBio.tbox.w - o * 2, panelBio.tbox.h - o * 2, uiBio.col.rectOv); - gr.DrawRect(panelBio.tbox.l + o, panelBio.tbox.t + o, panelBio.tbox.w - uiBio.overlay.borderWidth - 1, panelBio.tbox.h - uiBio.overlay.borderWidth - 1, uiBio.overlay.borderWidth, uiBio.col.rectOvBor); + o = Math.round(bio.ui.overlay.borderWidth / 2); + gr.FillSolidRect(bio.panel.tbox.l + o, bio.panel.tbox.t + o, bio.panel.tbox.w - o * 2, bio.panel.tbox.h - o * 2, bio.ui.col.rectOv); + gr.DrawRect(bio.panel.tbox.l + o, bio.panel.tbox.t + o, bio.panel.tbox.w - bio.ui.overlay.borderWidth - 1, bio.panel.tbox.h - bio.ui.overlay.borderWidth - 1, bio.ui.overlay.borderWidth, bio.ui.col.rectOvBor); break; case 3: - gr.FillRoundRect(panelBio.tbox.l, panelBio.tbox.t, panelBio.tbox.w, panelBio.tbox.h, panelBio.arc, panelBio.arc, uiBio.col.rectOv); + gr.FillRoundRect(bio.panel.tbox.l, bio.panel.tbox.t, bio.panel.tbox.w, bio.panel.tbox.h, bio.panel.arc, bio.panel.arc, bio.ui.col.rectOv); break; case 4: - o = Math.round(uiBio.overlay.borderWidth / 2); - gr.FillRoundRect(panelBio.tbox.l + o, panelBio.tbox.t + o, panelBio.tbox.w - o * 2, panelBio.tbox.h - o * 2, panelBio.arc, panelBio.arc, uiBio.col.rectOv); - gr.DrawRoundRect(panelBio.tbox.l + o, panelBio.tbox.t + o, panelBio.tbox.w - uiBio.overlay.borderWidth - 1, panelBio.tbox.h - uiBio.overlay.borderWidth - 1, panelBio.arc, panelBio.arc, uiBio.overlay.borderWidth, uiBio.col.rectOvBor); + o = Math.round(bio.ui.overlay.borderWidth / 2); + gr.FillRoundRect(bio.panel.tbox.l + o, bio.panel.tbox.t + o, bio.panel.tbox.w - o * 2, bio.panel.tbox.h - o * 2, bio.panel.arc, bio.panel.arc, bio.ui.col.rectOv); + gr.DrawRoundRect(bio.panel.tbox.l + o, bio.panel.tbox.t + o, bio.panel.tbox.w - bio.ui.overlay.borderWidth - 1, bio.panel.tbox.h - bio.ui.overlay.borderWidth - 1, bio.panel.arc, bio.panel.arc, bio.ui.overlay.borderWidth, bio.ui.col.rectOvBor); break; } } - if (pptBio.artistView && this.bio.text.length && !this.lyricsDisplayed()) { - const b = Math.max(Math.round(art_scrollbar.delta / this.line.h.bio + 0.4), 0); + if (bioSet.artistView && this.bio.text.length && !this.lyricsDisplayed()) { + const b = Math.max(Math.round(bio.art_scrollbar.delta / this.line.h.bio + 0.4), 0); const f = Math.min(b + this.line.drawn.bio, this.bio.arr.length); this.bio.drawn = 0; if (this.logo.show && this.logo.img) gr.DrawImage(this.logo.img, this.logo.x, this.logo.y, this.logo.img.Width, this.logo.img.Height, 0, 0, this.logo.img.Width, this.logo.img.Height, 0, 16); for (let i = b; i < f; i++) { const item = this.bio.arr[i]; - const item_y = item.h1 * i + item.y - art_scrollbar.delta + SCALE(2); - if (item_y < panelBio.style.max_y) { + const item_y = item.h1 * i + item.y - bio.art_scrollbar.delta + SCALE(2); + if (item_y < bio.panel.style.max_y) { this.bio.drawn++; - const iy = Math.round(item_y + uiBio.font.main_h / 2); - if (!pptBio.heading) { + const iy = Math.round(item_y + bio.ui.font.main_h / 2); + if (!bioSet.heading) { $Bio.source.amLfmWikiTxt.forEach(v => { - if (item[`${v}Line`]) gr.DrawLine(item[`${v}LineX1`], iy, item[`${v}LineX2`], iy, uiBio.style.l_w, uiBio.col.centerLine); + if (item[`${v}Line`]) gr.DrawLine(item[`${v}LineX1`], iy, item[`${v}LineX2`], iy, bio.ui.style.l_w, bio.ui.col.centerLine); }); } if (!item.sectionHeading || (!item.property ? i < f - 2 : i < f - 1)) { if (!item.property) { if (item.dropNegativeShadowLevel) { // nowplaying & lyrics only - gr.GdiDrawText(item.text, item.font, uiBio.col.dropShadow, panelBio.text.l - item.dropNegativeShadowLevel, item_y + item.offset, panelBio.text.w, item.h2, item.align); - gr.GdiDrawText(item.text, item.font, uiBio.col.dropShadow, panelBio.text.l, item_y - item.dropNegativeShadowLevel + item.offset, panelBio.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, bio.ui.col.dropShadow, bio.panel.text.l - item.dropNegativeShadowLevel, item_y + item.offset, bio.panel.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, bio.ui.col.dropShadow, bio.panel.text.l, item_y - item.dropNegativeShadowLevel + item.offset, bio.panel.text.w, item.h2, item.align); } if (item.dropShadowLevel) { // nowplaying & lyrics only - gr.GdiDrawText(item.text, item.font, uiBio.col.dropShadow, panelBio.text.l + item.dropShadowLevel, item_y + item.dropShadowLevel + item.offset, panelBio.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, bio.ui.col.dropShadow, bio.panel.text.l + item.dropShadowLevel, item_y + item.dropShadowLevel + item.offset, bio.panel.text.w, item.h2, item.align); } - gr.GdiDrawText(item.text, item.font, item.col, panelBio.text.l, item_y + item.offset, panelBio.text.w, item.h2, item.align); - if (item.members_tt_needed) this.checkTooltip(item, panelBio.text.l, true, panelBio.text.w, 0, 0, 0, item_y + item.offset); + gr.GdiDrawText(item.text, item.font, item.col, bio.panel.text.l, item_y + item.offset, bio.panel.text.w, item.h2, item.align); + if (item.members_tt_needed) this.checkTooltip(item, bio.panel.text.l, true, bio.panel.text.w, 0, 0, 0, item_y + item.offset); } else { - if (item.rowStripe) gr.FillSolidRect(panelBio.text.l, item_y + item.stripeOffset, panelBio.text.w, item.h3, item.rowStripe); - if (item.sectionLine) gr.DrawLine(item.sectionLineX1, iy, item.sectionLineX2, iy, uiBio.style.l_w, uiBio.col.sectionLine); - gr.GdiDrawText(item.name, item.font, item.col, panelBio.text.l, item_y + item.offset, this.reader.w.nameCol, item.h2, item.align); - gr.GdiDrawText(item.value, item.font, item.col, panelBio.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item_y + item.offset, this.reader.w.valueCol, item.h2, item.align); - if (item.name_tt_needed || item.value_tt_needed) this.checkTooltip(item, panelBio.text.l, item.name_tt_needed, this.reader.w.nameCol, panelBio.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item.value_tt_needed, this.reader.w.valueCol, item_y + item.offset); + if (item.rowStripe) gr.FillSolidRect(bio.panel.text.l, item_y + item.stripeOffset, bio.panel.text.w, item.h3, item.rowStripe); + if (item.sectionLine) gr.DrawLine(item.sectionLineX1, iy, item.sectionLineX2, iy, bio.ui.style.l_w, bio.ui.col.sectionLine); + gr.GdiDrawText(item.name, item.font, item.col, bio.panel.text.l, item_y + item.offset, this.reader.w.nameCol, item.h2, item.align); + gr.GdiDrawText(item.value, item.font, item.col, bio.panel.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item_y + item.offset, this.reader.w.valueCol, item.h2, item.align); + if (item.name_tt_needed || item.value_tt_needed) this.checkTooltip(item, bio.panel.text.l, item.name_tt_needed, this.reader.w.nameCol, bio.panel.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item.value_tt_needed, this.reader.w.valueCol, item_y + item.offset); } } } } - if (pptBio.sbarShow) art_scrollbar.draw(gr); + if (bioSet.sbarShow) bio.art_scrollbar.draw(gr); } - if (!pptBio.artistView && this.rev.text.length && !this.lyricsDisplayed()) { - const b = Math.max(Math.round(alb_scrollbar.delta / this.line.h.rev + 0.4), 0); + if (!bioSet.artistView && this.rev.text.length && !this.lyricsDisplayed()) { + const b = Math.max(Math.round(bio.alb_scrollbar.delta / this.line.h.rev + 0.4), 0); const f = Math.min(b + this.line.drawn.rev, this.rev.arr.length); this.rev.drawn = 0; if (this.logo.show && this.logo.img) gr.DrawImage(this.logo.img, this.logo.x, this.logo.y, this.logo.img.Width, this.logo.img.Height, 0, 0, this.logo.img.Width, this.logo.img.Height, 0, 16); for (let i = b; i < f; i++) { const item = this.rev.arr[i]; - const item_y = item.h1 * i + item.y - alb_scrollbar.delta + SCALE(2); - if (item_y < panelBio.style.max_y) { + const item_y = item.h1 * i + item.y - bio.alb_scrollbar.delta + SCALE(2); + if (item_y < bio.panel.style.max_y) { this.rev.drawn++; - const iy = Math.round(item_y + uiBio.font.main_h / 2); + const iy = Math.round(item_y + bio.ui.font.main_h / 2); switch (true) { case this.ratingPos.subHeading: ['am', 'lfm'].forEach(v => { - if (item[`${v}SubHeadingRating`]) gr.DrawImage(butBio.rating.images[this.rating[v]], item[`${v}SubHeadingRatingX`], item_y + this.rating.y + item.offset, butBio.rating.w1 / 2, butBio.rating.h1 / 2, 0, 0, butBio.rating.w1, butBio.rating.h1, 0, 255); + if (item[`${v}SubHeadingRating`]) gr.DrawImage(bio.but.rating.images[this.rating[v]], item[`${v}SubHeadingRatingX`], item_y + this.rating.y + item.offset, bio.but.rating.w1 / 2, bio.but.rating.h1 / 2, 0, 0, bio.but.rating.w1, bio.but.rating.h1, 0, 255); }); break; case this.ratingPos.line: ['am', 'lfm'].forEach(v => { - if (item[`${v}LineRating`]) gr.DrawImage(butBio.rating.images[this.rating[v]], item[`${v}LineRatingX`], item_y + this.rating.y + item.offset, butBio.rating.w1 / 2, butBio.rating.h1 / 2, 0, 0, butBio.rating.w1, butBio.rating.h1, 0, 255); + if (item[`${v}LineRating`]) gr.DrawImage(bio.but.rating.images[this.rating[v]], item[`${v}LineRatingX`], item_y + this.rating.y + item.offset, bio.but.rating.w1 / 2, bio.but.rating.h1 / 2, 0, 0, bio.but.rating.w1, bio.but.rating.h1, 0, 255); }); break; } - if (!pptBio.heading) { + if (!bioSet.heading) { $Bio.source.amLfmWikiTxt.forEach(v => { - if (item[`${v}Line`]) gr.DrawLine(item[`${v}LineX1`], iy, item[`${v}LineX2`], iy, uiBio.style.l_w, uiBio.col.centerLine); + if (item[`${v}Line`]) gr.DrawLine(item[`${v}LineX1`], iy, item[`${v}LineX2`], iy, bio.ui.style.l_w, bio.ui.col.centerLine); }); } - if (item.songLine) gr.DrawLine(item.songX1, iy, item.songX2, iy, uiBio.style.l_w, uiBio.col.centerLine); + if (item.songLine) gr.DrawLine(item.songX1, iy, item.songX2, iy, bio.ui.style.l_w, bio.ui.col.centerLine); if (!item.sectionHeading || (!item.property ? i < f - 2 : i < f - 1)) { if (!item.property) { if (item.dropNegativeShadowLevel) { // nowplaying & lyrics only - gr.GdiDrawText(item.text, item.font, uiBio.col.dropShadow, panelBio.text.l - item.dropNegativeShadowLevel, item_y + item.offset, panelBio.text.w, item.h2, item.align); - gr.GdiDrawText(item.text, item.font, uiBio.col.dropShadow, panelBio.text.l, item_y - item.dropNegativeShadowLevel + item.offset, panelBio.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, bio.ui.col.dropShadow, bio.panel.text.l - item.dropNegativeShadowLevel, item_y + item.offset, bio.panel.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, bio.ui.col.dropShadow, bio.panel.text.l, item_y - item.dropNegativeShadowLevel + item.offset, bio.panel.text.w, item.h2, item.align); } if (item.dropShadowLevel) { // nowplaying & lyrics only - gr.GdiDrawText(item.text, item.font, uiBio.col.dropShadow, panelBio.text.l + item.dropShadowLevel, item_y + item.dropShadowLevel + item.offset, panelBio.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, bio.ui.col.dropShadow, bio.panel.text.l + item.dropShadowLevel, item_y + item.dropShadowLevel + item.offset, bio.panel.text.w, item.h2, item.align); } - gr.GdiDrawText(item.text, item.font, item.col, panelBio.text.l, item_y + item.offset, panelBio.text.w, item.h2, item.align); - if (item.composers_tt_needed) this.checkTooltip(item, panelBio.text.l, true, panelBio.text.w, 0, 0, 0, item_y + item.offset); + gr.GdiDrawText(item.text, item.font, item.col, bio.panel.text.l, item_y + item.offset, bio.panel.text.w, item.h2, item.align); + if (item.composers_tt_needed) this.checkTooltip(item, bio.panel.text.l, true, bio.panel.text.w, 0, 0, 0, item_y + item.offset); } else { - if (item.rowStripe) gr.FillSolidRect(panelBio.text.l, item_y + item.stripeOffset, panelBio.text.w, item.h3, item.rowStripe); - if (item.sectionLine) gr.DrawLine(item.sectionLineX1, iy, item.sectionLineX2, iy, uiBio.style.l_w, uiBio.col.sectionLine); - gr.GdiDrawText(item.name, item.font, item.col, panelBio.text.l, item_y + item.offset, this.reader.w.nameCol, item.h2, item.align); - gr.GdiDrawText(item.value, item.font, item.col, panelBio.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item_y + item.offset, this.reader.w.valueCol, item.h2, item.align); - if (item.name_tt_needed || item.value_tt_needed) this.checkTooltip(item, panelBio.text.l, item.name_tt_needed, this.reader.w.nameCol, panelBio.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item.value_tt_needed, this.reader.w.valueCol, item_y + item.offset); + if (item.rowStripe) gr.FillSolidRect(bio.panel.text.l, item_y + item.stripeOffset, bio.panel.text.w, item.h3, item.rowStripe); + if (item.sectionLine) gr.DrawLine(item.sectionLineX1, iy, item.sectionLineX2, iy, bio.ui.style.l_w, bio.ui.col.sectionLine); + gr.GdiDrawText(item.name, item.font, item.col, bio.panel.text.l, item_y + item.offset, this.reader.w.nameCol, item.h2, item.align); + gr.GdiDrawText(item.value, item.font, item.col, bio.panel.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item_y + item.offset, this.reader.w.valueCol, item.h2, item.align); + if (item.name_tt_needed || item.value_tt_needed) this.checkTooltip(item, bio.panel.text.l, item.name_tt_needed, this.reader.w.nameCol, bio.panel.text.l + this.reader.w.nameCol + this.reader.w.spaceCol, item.value_tt_needed, this.reader.w.valueCol, item_y + item.offset); } } } } - if (pptBio.sbarShow) alb_scrollbar.draw(gr); + if (bioSet.sbarShow) bio.alb_scrollbar.draw(gr); } } } expandLists(type, n) { - const en = cfg.language == 'EN' || cfg.languageFallback; + const en = bioCfg.language == 'EN' || bioCfg.languageFallback; let items = []; switch (type) { case 'amBio': @@ -956,37 +956,37 @@ class Text { const members = 'Members: |Mitglieder: |Miembros: |Membres: |Componenti: |\\u30e1\\u30f3\\u30d0\\u30fc: |Cz\\u0142onkowie: |Membros: |\\u0423\\u0447\\u0430\\u0441\\u0442\\u043d\\u0438\\u043a\\u0438: |Medlemmar: |\\u00dcyeler: |\\u6210\\u5458: '; const topTags = 'Top Tags: '; const topTracks = 'Top Tracks: |Top-Titel: |Temas m\\u00e1s escuchados: |Top titres: |Brani pi\\u00f9 ascoltati: |\\u4eba\\u6c17\\u30c8\\u30e9\\u30c3\\u30af: |Najpopularniejsze utwory: |Faixas principais: |\\u041b\\u0443\\u0447\\u0448\\u0438\\u0435 \\u043a\\u043e\\u043c\\u043f\\u043e\\u0437\\u0438\\u0446\\u0438\\u0438: |Toppl\\u00e5tar: |Pop\\u00fcler Par\\u00e7alar: |\\u6700\\u4f73\\u5355\\u66f2: '; - items = [members, panelBio.similarArtistsKey, panelBio.topAlbumsKey, topTracks]; - if (!panelBio.summary.genre) items.unshift(topTags); + items = [members, bio.panel.similarArtistsKey, bio.panel.topAlbumsKey, topTracks]; + if (!bio.panel.summary.genre) items.unshift(topTags); break; } case 'amRev': items = ['Album Genres: ', 'Album Moods: ', 'Album Themes: ', 'Composers: ', 'Track Moods: ', 'Track Themes: ']; - if (this.rev.amAlb || !panelBio.summary.genre) items.unshift('Track Genres: '); + if (this.rev.amAlb || !bio.panel.summary.genre) items.unshift('Track Genres: '); break; case 'lfmRev': items = ['Top Tags: ']; - if (this.rev.lfmAlb || !panelBio.summary.genre) items.unshift('Track Tags: '); + if (this.rev.lfmAlb || !bio.panel.summary.genre) items.unshift('Track Tags: '); break; case 'wikiBio': items = ['Genre: ']; break; case 'wikiRev': items = ['Album Genres: ', 'Composers: ']; - if (this.rev.wikiAlb || !panelBio.summary.genre) items.unshift('Track Genres: '); + if (this.rev.wikiAlb || !bio.panel.summary.genre) items.unshift('Track Genres: '); break; } $Bio.gr(1, 1, false, g => { items.forEach(v => { - const w = tagBio.getTag(n, v); + const w = bio.tag.getTag(n, v); const li = w.tag; if (li) { let list = `${w.label}\r\n`; li.forEach((v, i, arr) => { let nm = (en ? `${i + 1}. ` : '\u2022 ') + v; - if (g.CalcTextWidth(nm, uiBio.font.main) > panelBio.text.w) { - nm = `${g.EstimateLineWrap(nm, uiBio.font.main, panelBio.text.w - g.CalcTextWidth('... ', uiBio.font.main))[0]}...`; + if (g.CalcTextWidth(nm, bio.ui.font.main) > bio.panel.text.w) { + nm = `${g.EstimateLineWrap(nm, bio.ui.font.main, bio.panel.text.w - g.CalcTextWidth('... ', bio.ui.font.main))[0]}...`; } list += nm; if (i < arr.length - 1) list += '\r\n'; @@ -1005,10 +1005,10 @@ class Text { let pth = ''; let item = n == 'bio' ? v.pth.replace(/%BIO_ARTIST%|%BIO_ALBUMARTIST%/gi, '%BIO_ARTIST%') : v.pth.replace(/%BIO_ALBUMARTIST%|%BIO_ARTIST%/gi, '%BIO_ALBUMARTIST%'); - const a = $Bio.tfEscape(name.artist(!v.lyrics ? panelBio.id.focus : false, !!v.lyrics)); - const aa = $Bio.tfEscape(name.albumArtist(!v.lyrics ? panelBio.id.focus : false, !!v.lyrics)); - const l = $Bio.tfEscape(name.album(!v.lyrics ? panelBio.id.focus : false, !!v.lyrics)); - const tr = $Bio.tfEscape(name.title(!v.lyrics ? panelBio.id.focus : false, !!v.lyrics)); + const a = $Bio.tfEscape(bio.name.artist(!v.lyrics ? bio.panel.id.focus : false, !!v.lyrics)); + const aa = $Bio.tfEscape(bio.name.albumArtist(!v.lyrics ? bio.panel.id.focus : false, !!v.lyrics)); + const l = $Bio.tfEscape(bio.name.album(!v.lyrics ? bio.panel.id.focus : false, !!v.lyrics)); + const tr = $Bio.tfEscape(bio.name.title(!v.lyrics ? bio.panel.id.focus : false, !!v.lyrics)); item = item // substitue bio var + check advanced radio stream parser (tfBio & tfRev do lookUps not parser) .replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_artist%/gi, a ? '$&#@!%path%#@!' : '$&').replace(/%bio_artist%/gi, a) .replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_albumartist%/gi, aa ? '$&#@!%path%#@!' : '$&').replace(/%bio_albumartist%/gi, aa) @@ -1023,9 +1023,9 @@ class Text { return pths.some(w => { const wildCard = /[*?]/.test(w); if (!wildCard) { - this[n].readerItem = panelBio.cleanPth(w, !v.lyrics ? panelBio.id.focus : false, !v.lyrics ? '' : 'lyr').slice(0, -1).replace(/#@!.*?#@!/g, ''); + this[n].readerItem = bio.panel.cleanPth(w, !v.lyrics ? bio.panel.id.focus : false, !v.lyrics ? '' : 'lyr').slice(0, -1).replace(/#@!.*?#@!/g, ''); } else { - let p = panelBio.cleanPth(w.replace(/\*/g, '@!@').replace(/\?/g, '!@!'), !v.lyrics ? pptBio.focus : false, !v.lyrics ? '' : 'lyr').slice(0, -1); + let p = bio.panel.cleanPth(w.replace(/\*/g, '@!@').replace(/\?/g, '!@!'), !v.lyrics ? bioSet.focus : false, !v.lyrics ? '' : 'lyr').slice(0, -1); p = p.replace(/@!@/g, '*').replace(/!@!/g, '?').replace(/#@!.*?#@!/g, ''); const arr = utils.Glob(p); if (!arr.length) return false; @@ -1037,20 +1037,20 @@ class Text { formatItemProperties(v, j, g) { if (!v.property) return - if (pptBio.rowStripes && !v.subHeading && !v.sectionHeading && v.name) { - v.rowStripe = j % 2 == 0 ? uiBio.col.rowStripes /*uiBio.col.bg3*/ : uiBio.col.bg2; - const line_h = pptBio.artistView ? this.line.h.bio : this.line.h.rev; + if (bioSet.rowStripes && !v.subHeading && !v.sectionHeading && v.name) { + v.rowStripe = j % 2 == 0 ? bio.ui.col.rowStripes /*bio.ui.col.bg3*/ : bio.ui.col.bg2; + const line_h = bioSet.artistView ? this.line.h.bio : this.line.h.rev; v.h3 = j % 2 == 0 ? line_h - 2 : line_h; v.stripeOffset = j % 2 == 0 ? 1 : 0; } - if (pptBio.lineDividers && v.sectionHeading) { + if (bioSet.lineDividers && v.sectionHeading) { v.sectionLine = true; if (!g) { $Bio.gr(1, 1, false, g => { - v.sectionLineX1 = panelBio.text.l + g.CalcTextWidth(`${v.name} `, uiBio.font.main); + v.sectionLineX1 = bio.panel.text.l + g.CalcTextWidth(`${v.name} `, bio.ui.font.main); }); - } else v.sectionLineX1 = panelBio.text.l + g.CalcTextWidth(`${v.name} `, uiBio.font.main); - v.sectionLineX2 = Math.max(v.sectionLineX1, panelBio.text.l + panelBio.text.w); + } else v.sectionLineX1 = bio.panel.text.l + g.CalcTextWidth(`${v.name} `, bio.ui.font.main); + v.sectionLineX2 = Math.max(v.sectionLineX1, bio.panel.text.l + bio.panel.text.w); } } @@ -1063,15 +1063,15 @@ class Text { if (prefix) { $Bio.gr(1, 1, false, g => { sub[n] = s[n].key.replace(/(Album\s|Track\s)Moods: /g, 'Moods: ').replace(/Group Members: /g, 'Members: ') + sub[n]; - if (pptBio.summaryCompact) { + if (bioSet.summaryCompact) { if (type == 'amBio') { let tt_needed = false; let members = ''; if (/^Members:\s/.test(sub[n])) { membersFound = true; - tt_needed = g.CalcTextWidth(sub[n], uiBio.font.summary, true) > panelBio.text.w; + tt_needed = g.CalcTextWidth(sub[n], bio.ui.font.summary, true) > bio.panel.text.w; if (tt_needed) { - members = g.EstimateLineWrap(sub[n], uiBio.font.summary, panelBio.text.w - g.CalcTextWidth('... ', uiBio.font.summary))[0]; + members = g.EstimateLineWrap(sub[n], bio.ui.font.summary, bio.panel.text.w - g.CalcTextWidth('... ', bio.ui.font.summary))[0]; members = `${members.replace(/\u2219\s?$/, '')}...`; } } @@ -1081,11 +1081,11 @@ class Text { if (type == 'wikiRev') { let tt_needed = false; let composers = ''; - if (panelBio.style.inclTrackRev && /^Composers:\s/.test(sub[n])) { + if (bio.panel.style.inclTrackRev && /^Composers:\s/.test(sub[n])) { composersFound = true; - tt_needed = g.CalcTextWidth(sub[n], uiBio.font.summary, true) > panelBio.text.w; + tt_needed = g.CalcTextWidth(sub[n], bio.ui.font.summary, true) > bio.panel.text.w; if (tt_needed) { - composers = g.EstimateLineWrap(sub[n], uiBio.font.summary, panelBio.text.w - g.CalcTextWidth('... ', uiBio.font.summary))[0]; + composers = g.EstimateLineWrap(sub[n], bio.ui.font.summary, bio.panel.text.w - g.CalcTextWidth('... ', bio.ui.font.summary))[0]; composers = `${composers.replace(/\u2219\s?$/, '')}...`; } } @@ -1098,7 +1098,7 @@ class Text { } }); } - if (pptBio.summaryCompact) { + if (bioSet.summaryCompact) { if (!composersFound && !membersFound) { const line1 = this.getLine(sub[n], limit, suffix); let line2 = ''; @@ -1128,12 +1128,12 @@ class Text { const s = [s0, s1, s2, s3, s4, s5, s6]; const sub = ['', '', '', '', '', '', '', '']; let str = ''; - if (panelBio.summary.show) { + if (bio.panel.summary.show) { let m = ''; for (let i = 0; i < s.length; i++) { if (!s[i].key && !s[i].str) continue; if (s[i].key) { - m = tagBio.getTag(text, s[i].key, true); + m = bio.tag.getTag(text, s[i].key, true); if (m.tag) { sub[i] = m.label + m.tag; text = text.replace(RegExp($Bio.regexEscape(sub[i])), ''); @@ -1149,7 +1149,7 @@ class Text { } } if (type == 'amBio') { - if (panelBio.summary.genre && panelBio.summary.date && panelBio.summary.popNow && panelBio.summary.other && !sub[1]) sub[4] = this.checkNewLine(sub, 4); + if (bio.panel.summary.genre && bio.panel.summary.date && bio.panel.summary.popNow && bio.panel.summary.other && !sub[1]) sub[4] = this.checkNewLine(sub, 4); } if (type == 'lfmBio' && sub[5]) { @@ -1172,29 +1172,29 @@ class Text { text = text.replace(RegExp(`${sub6}; |${sub6}`), ''); } - sub[6] = panelBio.summary.other || panelBio.summary.date && (panelBio.summary.locale && this.type == 'lfmBio' || panelBio.summary.locale && this.type == 'wikiBio') ? this.checkNewLine(sub, 6) : this.checkStr(sub, 6); + sub[6] = bio.panel.summary.other || bio.panel.summary.date && (bio.panel.summary.locale && this.type == 'lfmBio' || bio.panel.summary.locale && this.type == 'wikiBio') ? this.checkNewLine(sub, 6) : this.checkStr(sub, 6); str = sub[1]; for (let i = 2; i < 7; i++) { str = str && sub[i] ? str + (!sub[i].startsWith('\r\n') ? ' | ' : '') + sub[i] : str || sub[i]; } } - if (pptBio.expandLists) text = this.expandLists(type, text); + if (bioSet.expandLists) text = this.expandLists(type, text); text = text.replace(/\u200b/g, ''); switch (type) { case 'amBio': text = text.replace('Genre: ', 'Genres: '); break; case 'amRev': text = text.replace(/(Album|Track)\s(Genre|Mood|Theme)(s|):\s/g, '$2$3: '); break; - case 'lfmBio': if (cfg.lang.ix > 3) text = text.replace('Top Tags: ', `${this.topTags[cfg.lang.ix]}: `); break; + case 'lfmBio': if (bioCfg.lang.ix > 3) text = text.replace('Top Tags: ', `${this.topTags[bioCfg.lang.ix]}: `); break; case 'lfmRev': - if (cfg.lang.ix > 3) text = text.replace('Top Tags: ', `${this.topTags[cfg.lang.ix]}: `); - text = text.replace('Track Tags: ', !cfg.lang.ix ? 'Top Tags: ' : `${this.topTags[cfg.lang.ix]}: `).replace(/Last-fm:/g, 'Last.fm:'); + if (bioCfg.lang.ix > 3) text = text.replace('Top Tags: ', `${this.topTags[bioCfg.lang.ix]}: `); + text = text.replace('Track Tags: ', !bioCfg.lang.ix ? 'Top Tags: ' : `${this.topTags[bioCfg.lang.ix]}: `).replace(/Last-fm:/g, 'Last.fm:'); break; case 'wikiBio': if (!singleGenre) text = text.replace('Genre: ', 'Genres: '); break; case 'wikiRev': text = text.replace(/Album\sGenres:\s/, singleGenre ? 'Genre: ' : 'Genres: ').replace(/Track\sGenre/, 'Genre').replace(/Track\sGenre/, 'Genre').replace(/Duration:\s/g, 'Length: '); break; } - if (panelBio.summary.show) { + if (bio.panel.summary.show) { if (str) { str = str.trim(); } @@ -1213,20 +1213,20 @@ class Text { this.rev.ix = -1; this.bio.ix = -1; switch (true) { - case !pptBio.artistView: - if (y > alb_scrollbar.y && y < alb_scrollbar.y + this.rev.drawn * this.line.h.rev && x >= panelBio.text.l && x < panelBio.text.l + this.reader.w.nameCol + panelBio.text.w - this.reader.w.nameCol) this.rev.ix = Math.round((y + alb_scrollbar.delta - panelBio.text.t - this.line.h.rev * 0.5) / this.line.h.rev) + case !bioSet.artistView: + if (y > bio.alb_scrollbar.y && y < bio.alb_scrollbar.y + this.rev.drawn * this.line.h.rev && x >= bio.panel.text.l && x < bio.panel.text.l + this.reader.w.nameCol + bio.panel.text.w - this.reader.w.nameCol) this.rev.ix = Math.round((y + bio.alb_scrollbar.delta - bio.panel.text.t - this.line.h.rev * 0.5) / this.line.h.rev) break; - case pptBio.artistView: - if (y > art_scrollbar.y && y < art_scrollbar.y + this.bio.drawn * this.line.h.bio && x >= panelBio.text.l && x < panelBio.text.l + this.reader.w.nameCol + panelBio.text.w - this.reader.w.nameCol) this.bio.ix = Math.round((y + art_scrollbar.delta - panelBio.text.t - this.line.h.bio * 0.5) / this.line.h.bio); + case bioSet.artistView: + if (y > bio.art_scrollbar.y && y < bio.art_scrollbar.y + this.bio.drawn * this.line.h.bio && x >= bio.panel.text.l && x < bio.panel.text.l + this.reader.w.nameCol + bio.panel.text.w - this.reader.w.nameCol) this.bio.ix = Math.round((y + bio.art_scrollbar.delta - bio.panel.text.t - this.line.h.bio * 0.5) / this.line.h.bio); break; } } getBornStr(source) { - const bi = tagBio.getTag(source, this.bio.bornIn, true); + const bi = bio.tag.getTag(source, this.bio.bornIn, true); const bin = bi.tag; - if (!panelBio.summary.date) { - if (panelBio.summary.locale) { + if (!bio.panel.summary.date) { + if (bio.panel.summary.locale) { let str = ''; if (bin) { const count = bin.split(',').length - 1; @@ -1238,7 +1238,7 @@ class Text { } return { bornStr: '', source }; } - const b = tagBio.getTag(source, this.bio.born, true); + const b = bio.tag.getTag(source, this.bio.born, true); const bn = b.tag; const count = bin.split(',').length - 1; const bornIn = count > 2 ? bin.replace(/,[^,]+,/, ',') : bin; @@ -1248,7 +1248,7 @@ class Text { let age = bn.match(/\s\(.*?\)/); age = age ? age[0] : ''; born = bn.replace(age, ''); - bornStr = b.label + born + (panelBio.summary.locale ? ` \u2219 ${bornIn}` : '') + (age ? (panelBio.summary.locale ? ` \u2219${$Bio.titlecase(age.replace(/[()]/g, ''))}` : $Bio.titlecase(age)) : ''); + bornStr = b.label + born + (bio.panel.summary.locale ? ` \u2219 ${bornIn}` : '') + (age ? (bio.panel.summary.locale ? ` \u2219${$Bio.titlecase(age.replace(/[()]/g, ''))}` : $Bio.titlecase(age)) : ''); source = source.replace(RegExp($Bio.regexEscape(b.label + bn)), ''); source = source.replace(RegExp($Bio.regexEscape(bi.label + bin)), ''); } @@ -1256,24 +1256,24 @@ class Text { } getFlag(artist, n) { - if (pptBio[`${n}FlagShow`] && !pptBio.hdPos) { + if (bioSet[`${n}FlagShow`] && !bioSet.hdPos) { const codes = $Bio.jsonParse(this.countryCodes, {}, 'file'); let handle = null; let code = (codes[artist.toLowerCase()] || '').slice(0, 2); - let country = code ? codeToCountry[code] || '' : ''; - if (!code) handle = $Bio.handle(panelBio.id.focus); + let country = code ? bioCodeToCountry[code] || '' : ''; + if (!code) handle = $Bio.handle(bio.panel.id.focus); if (!code && handle) { country = FbTitleFormat('[$meta(artistcountry,0)]').EvalWithMetadb(handle); - code = countryToCode[$Bio.strip(country)]; + code = bioCountryToCode[$Bio.strip(country)]; } if (!code && handle) { country = FbTitleFormat('[$meta(locale last.fm,$sub($meta_num(locale last.fm),1))]').EvalWithMetadb(handle); - code = countryToCode[$Bio.strip(country)]; + code = bioCountryToCode[$Bio.strip(country)]; } - const path = `${basePath}scripts\\biography\\assets\\images\\flags/${code}.png`; + const path = `${grPath.base}scripts\\biography\\assets\\images\\flags\\${code}.png`; if ($Bio.file(path)) { if (code && this[n].flagCode != code) { - this[n].flag = my_utilsBio.getFlagAsset(`${code}.png`); + this[n].flag = bio_my_utils.getFlagAsset(`${code}.png`); this[n].flagCode = code; this[n].flagCountry = country; } @@ -1286,8 +1286,8 @@ class Text { } getFoundedIn(source) { - if (!panelBio.summary.locale) return { foundedIn: '', source }; - const f = tagBio.getTag(source, this.bio.foundedIn, true); + if (!bio.panel.summary.locale) return { foundedIn: '', source }; + const f = bio.tag.getTag(source, this.bio.foundedIn, true); const loc = f.tag; const count = loc.split(',').length - 1; let foundedIn = count > 2 ? loc.replace(/,[^,]+,/, ',') : loc; @@ -1297,12 +1297,12 @@ class Text { } getItem(p_calc, art_ix, alb_ix, force) { - if (pptBio.img_only) return; + if (bioSet.img_only) return; switch (true) { - case pptBio.artistView: { + case bioSet.artistView: { this.cur_artist = this.artist; - const stndBio = !art_ix || art_ix + 1 > panelBio.art.list.length; - this.artist = stndBio ? name.artist(panelBio.id.focus) : panelBio.art.list[art_ix].name; + const stndBio = !art_ix || art_ix + 1 > bio.panel.art.list.length; + this.artist = stndBio ? bio.name.artist(bio.panel.id.focus) : bio.panel.art.list[art_ix].name; const new_artist = this.artist != this.cur_artist; if (new_artist) { this.artistFlush(); @@ -1312,11 +1312,11 @@ class Text { this.get = 0; break; } - case !pptBio.artistView: { + case !bioSet.artistView: { this.id.curAlb = this.id.alb; - const stndAlb = !alb_ix || alb_ix + 1 > panelBio.alb.list.length; - this.artist = stndAlb ? name.albumArtist(panelBio.id.focus) : panelBio.alb.list[alb_ix].artist; - const alb = stndAlb ? name.album(panelBio.id.focus) : panelBio.alb.list[alb_ix].album; + const stndAlb = !alb_ix || alb_ix + 1 > bio.panel.alb.list.length; + this.artist = stndAlb ? bio.name.albumArtist(bio.panel.id.focus) : bio.panel.alb.list[alb_ix].artist; + const alb = stndAlb ? bio.name.album(bio.panel.id.focus) : bio.panel.alb.list[alb_ix].album; this.id.alb = this.artist + alb; const new_album = this.id.alb != this.id.curAlb; if (new_album || force) { @@ -1345,7 +1345,7 @@ class Text { const showEmpty = data.showEmpty; item = []; let names = []; - const handle = $Bio.handle(panelBio.id.focus); + const handle = $Bio.handle(bio.panel.id.focus); let arr = []; keys.forEach(v => { const properties = data[v].properties; @@ -1355,8 +1355,8 @@ class Text { case !/Metadata\*|General\*|Other\*/i.test(v): properties.forEach(w => { if (!w.name || !w.titleformat) return; - w.titleformat = w.titleformat.replace(/%BIO_ALBUMARTIST%/gi, cfg.tfAlbumArtist).replace(/%BIO_ARTIST%/gi, cfg.tfArtist).replace(/%BIO_ALBUM%/gi, cfg.tfAlbum).replace(/%BIO_TITLE%/gi, cfg.tfTitle) - let value = this.formatValue(showEmpty ? $Bio.eval(`$trim(${w.titleformat})`, panelBio.id.focus) : $Bio.eval(`[$trim(${w.titleformat})]`, panelBio.id.focus)); + w.titleformat = w.titleformat.replace(/%BIO_ALBUMARTIST%/gi, bioCfg.tfAlbumArtist).replace(/%BIO_ARTIST%/gi, bioCfg.tfArtist).replace(/%BIO_ALBUM%/gi, bioCfg.tfAlbum).replace(/%BIO_TITLE%/gi, bioCfg.tfTitle) + let value = this.formatValue(showEmpty ? $Bio.eval(`$trim(${w.titleformat})`, bio.panel.id.focus) : $Bio.eval(`[$trim(${w.titleformat})]`, bio.panel.id.focus)); names = names.concat(fields(w.titleformat.toUpperCase()).filter(v => v.trim())); if (typeof w.titleformat == 'string' && w.titleformat.toLowerCase().includes('%album rating allmusic%')) value /= 2; if (value && /like count|dislike count|view count/i.test(w.name)) { @@ -1365,7 +1365,7 @@ class Text { } if (w.name == 'Rating' && this.local) { w.name = 'Dynamic rating'; - value = this.formatValue($Bio.eval('[%play_count%]|%added%|$if($strstr(%path%,\'://\'),1,0)', panelBio.id.focus)); + value = this.formatValue($Bio.eval('[%play_count%]|%added%|$if($strstr(%path%,\'://\'),1,0)', bio.panel.id.focus)); const i = value.split('|'); if (!i[0] || i[2] == 1) value = 0; else { @@ -1405,8 +1405,8 @@ class Text { arr = []; if (handle) { const f = handle.GetFileInfo(); - const length = $Bio.eval('[%length%]', panelBio.id.focus); - const samples = parseInt($Bio.eval('%length_samples%', panelBio.id.focus)); + const length = $Bio.eval('[%length%]', bio.panel.id.focus); + const samples = parseInt($Bio.eval('%length_samples%', bio.panel.id.focus)); if (length != 0 && length != -1) { arr.push({ text: '', name: 'Duration', value: length + (!isNaN(samples) ? ` (${samples.toLocaleString()} samples)` : ''), property: true }); } @@ -1432,7 +1432,7 @@ class Text { let name = f.MetaName(i); if (!names.includes(name.toUpperCase())) { name = this.upperCaseFirst(name); - const value = this.formatValue($Bio.eval(`[%${name}%]`, panelBio.id.focus)); + const value = this.formatValue($Bio.eval(`[%${name}%]`, bio.panel.id.focus)); if (showEmpty || value) arr.push({ text: '', name, value, property: true }); } } @@ -1443,26 +1443,26 @@ class Text { } }); item.shift(); - if (pptBio.sourceAll && pptBio.sourceHeading || pptBio.sourceHeading == 2) { - item.unshift({ text: '', name: this[n].subhead.txt[pptBio.heading ? 0 : 1], value: '', property: true }, { text: '', name: '', value: '', property: true }); + if (bioSet.sourceAll && bioSet.sourceHeading || bioSet.sourceHeading == 2) { + item.unshift({ text: '', name: this[n].subhead.txt[bioSet.heading ? 0 : 1], value: '', property: true }, { text: '', name: '', value: '', property: true }); } this.reader.w.nameCol = 10; this.reader.w.valueCol = 10; this.reader.w.spaceCol = 0; $Bio.gr(1, 1, false, g => { - this.reader.w.spaceCol = g.CalcTextWidth(' ', uiBio.font.main) * 5; - if (pptBio.sourceAll && pptBio.sourceHeading || pptBio.sourceHeading == 2) this.reader.w.nameCol = g.CalcTextWidth(this[n].subhead.txt[0], uiBio.font.subHeadSource) + this.reader.w.spaceCol; + this.reader.w.spaceCol = g.CalcTextWidth(' ', bio.ui.font.main) * 5; + if (bioSet.sourceAll && bioSet.sourceHeading || bioSet.sourceHeading == 2) this.reader.w.nameCol = g.CalcTextWidth(this[n].subhead.txt[0], bio.ui.font.subHeadSource) + this.reader.w.spaceCol; item.forEach(v => { v.name = v.name.replace(/\basin\b|\bbpm\b|\bid\b|\bisrc\b|\bcue_|\bmcn\b|\bmd5\b|\bmp3_|\burl\b/i, (m) => m.toUpperCase()); v.name = v.name.replace(RegExp(`\\b(${data.uppercase})\\b`, 'i'), (m) => m.toUpperCase()); - v.name_w = g.CalcTextWidth(v.name, uiBio.font.main, true); - this.reader.w.nameCol = !pptBio.fieldWidth ? Math.max(v.name_w, this.reader.w.nameCol) : panelBio.text.w * pptBio.fieldWidth / 100; + v.name_w = g.CalcTextWidth(v.name, bio.ui.font.main, true); + this.reader.w.nameCol = !bioSet.fieldWidth ? Math.max(v.name_w, this.reader.w.nameCol) : bio.panel.text.w * bioSet.fieldWidth / 100; }); - this.reader.w.nameCol = !pptBio.fieldWidth ? $Bio.clamp(this.reader.w.nameCol, Math.max(10, panelBio.text.w / 6), panelBio.text.w / 2) : $Bio.clamp(this.reader.w.nameCol, Math.max(10, panelBio.text.w / 10), panelBio.text.w * 0.9); - this.reader.w.valueCol = panelBio.text.w - this.reader.w.nameCol - this.reader.w.spaceCol; + this.reader.w.nameCol = !bioSet.fieldWidth ? $Bio.clamp(this.reader.w.nameCol, Math.max(10, bio.panel.text.w / 6), bio.panel.text.w / 2) : $Bio.clamp(this.reader.w.nameCol, Math.max(10, bio.panel.text.w / 10), bio.panel.text.w * 0.9); + this.reader.w.valueCol = bio.panel.text.w - this.reader.w.nameCol - this.reader.w.spaceCol; item.forEach(v => { v.name_tt_needed = v.name_w > this.reader.w.nameCol; - v.value_tt_needed = g.CalcTextWidth(v.value, uiBio.font.main, true) > this.reader.w.valueCol; + v.value_tt_needed = g.CalcTextWidth(v.value, bio.ui.font.main, true) > this.reader.w.valueCol; }); }); return item; @@ -1476,11 +1476,11 @@ class Text { let end = ''; let w = 0; $Bio.gr(1, 1, false, g => { - w = g.CalcTextWidth(sub, uiBio.font.summary); - while (w > panelBio.text.w && sub.includes('\u2219')) { + w = g.CalcTextWidth(sub, bio.ui.font.summary); + while (w > bio.panel.text.w && sub.includes('\u2219')) { const f = sub.lastIndexOf('\u2219'); // limit genres to 1 line if (f != -1) sub = sub.slice(0, f).trim(); - w = g.CalcTextWidth(suffix ? `${sub} ...` : sub, uiBio.font.summary); + w = g.CalcTextWidth(suffix ? `${sub} ...` : sub, bio.ui.font.summary); end = ' ...'; } }); @@ -1488,18 +1488,18 @@ class Text { } getLogo(artist, n) { // experimental feature: disabled in release version: best in large panel - if (this[n].loaded.txt && this.reader[n].props && this.local && window.Name == 'Details' && panelBio.text.w && panelBio.text.h) { + if (this[n].loaded.txt && this.reader[n].props && this.local && window.Name == 'Details' && bio.panel.text.w && bio.panel.text.h) { const a = $Bio.clean(artist); const path = `D:\\artistlogos\\${a}.png`; - const id = `${a}-${panelBio.text.t}-${panelBio.text.l}-${panelBio.text.h}-${panelBio.text.w}`; + const id = `${a}-${bio.panel.text.t}-${bio.panel.text.l}-${bio.panel.text.h}-${bio.panel.text.w}`; if ($Bio.file(path)) { if (a && this.logo.id != id) { this.logo.img = gdi.Image(path); - const sc = Math.min(panelBio.text.h / this.logo.img.Height, panelBio.text.w / this.logo.img.Width); + const sc = Math.min(bio.panel.text.h / this.logo.img.Height, bio.panel.text.w / this.logo.img.Width); const w = Math.round(this.logo.img.Width * sc); const h = Math.round(this.logo.img.Height * sc); - this.logo.x = panelBio.text.l + (panelBio.text.w - w) / 2; - this.logo.y = panelBio.text.t + (panelBio.text.h - h) / 2; + this.logo.x = bio.panel.text.l + (bio.panel.text.w - w) / 2; + this.logo.y = bio.panel.text.t + (bio.panel.text.h - h) / 2; this.logo.img = this.logo.img.Resize(w, h, 2); this.logo.id = id; } @@ -1508,23 +1508,23 @@ class Text { } else { if (artist && this.logo.id != id) { const ix = Math.floor(Math.random() * this.logo.fonts.length); - const size = Math.max(Math.round(Math.min(panelBio.text.w, panelBio.text.h) / 4), 10); + const size = Math.max(Math.round(Math.min(bio.panel.text.w, bio.panel.text.h) / 4), 10); const font = gdi.Font(this.logo.fonts[ix], size, 1); let htFull = 0; let htSingle = 0; $Bio.gr(1, 1, false, g => { - htFull = g.MeasureString(artist, font, 0, 0, panelBio.text.w, 5000, StringFormat(1, 1)).Height; + htFull = g.MeasureString(artist, font, 0, 0, bio.panel.text.w, 5000, StringFormat(1, 1)).Height; htSingle = g.CalcTextHeight('String', font); }); const offset = Math.max(htSingle * 0.01, 1); - const ht = Math.min(panelBio.text.h, htFull); - this.logo.img = $Bio.gr(panelBio.text.w, ht, true, g => { - g.GdiDrawText(artist, font, RGB(0, 0, 0), 0, 0, panelBio.text.w, ht, htFull - htSingle < panelBio.text.h ? this.ncc : this.tc) - g.GdiDrawText(artist, font, RGB(0, 0, 0), offset * 2, offset * 2, panelBio.text.w, ht, htFull - htSingle < panelBio.text.h ? this.ncc : this.tc) - g.GdiDrawText(artist, font, RGB(255, 255, 255), offset, offset, panelBio.text.w, ht, htFull - htSingle < panelBio.text.h ? this.ncc : this.tc) + const ht = Math.min(bio.panel.text.h, htFull); + this.logo.img = $Bio.gr(bio.panel.text.w, ht, true, g => { + g.GdiDrawText(artist, font, RGB(0, 0, 0), 0, 0, bio.panel.text.w, ht, htFull - htSingle < bio.panel.text.h ? this.ncc : this.tc) + g.GdiDrawText(artist, font, RGB(0, 0, 0), offset * 2, offset * 2, bio.panel.text.w, ht, htFull - htSingle < bio.panel.text.h ? this.ncc : this.tc) + g.GdiDrawText(artist, font, RGB(255, 255, 255), offset, offset, bio.panel.text.w, ht, htFull - htSingle < bio.panel.text.h ? this.ncc : this.tc) }); - this.logo.x = panelBio.text.l; - this.logo.y = panelBio.text.t + (panelBio.text.h - ht) / 2; + this.logo.x = bio.panel.text.l; + this.logo.y = bio.panel.text.t + (bio.panel.text.h - ht) / 2; this.logo.id = id; } this.logo.show = true; @@ -1536,30 +1536,30 @@ class Text { getNowplaying(item, n) { if (!item || typeof item !== 'string') return; - const focus = fb.IsPlaying ? false : panelBio.id.focus; + const focus = fb.IsPlaying ? false : bio.panel.id.focus; item = item.replace(/\r\n|\r|\n/g, ''); - const a = $Bio.tfEscape(name.artist(focus, true)); - const aa = $Bio.tfEscape(name.albumArtist(focus, true)); - const l = $Bio.tfEscape(name.album(focus, true)); - const tr = $Bio.tfEscape(name.title(focus, true)); + const a = $Bio.tfEscape(bio.name.artist(focus, true)); + const aa = $Bio.tfEscape(bio.name.albumArtist(focus, true)); + const l = $Bio.tfEscape(bio.name.album(focus, true)); + const tr = $Bio.tfEscape(bio.name.title(focus, true)); item = item .replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_artist%/gi, a ? '$&#@!%path%#@!' : '$&').replace(/%bio_artist%/gi, a) .replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_albumartist%/gi, aa ? '$&#@!%path%#@!' : '$&').replace(/%bio_albumartist%/gi, aa) .replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_album%/gi, l ? '$&#@!%path%#@!' : '$&').replace(/%bio_album%/gi, l) .replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_title%/gi, tr ? '$&#@!%path%#@!' : '$&').replace(/%bio_title%/gi, tr); this.reader[n].perSec = /%playback_time|%bitrate%|\$progress/i.test(item); - item = fb.IsPlaying ? (!this.reader[n].perSec ? $Bio.eval(`[$trim(${item})]`, false) : FbTitleFormat(item).Eval()) : $Bio.eval(`[$trim(${item})]`, panelBio.id.focus); + item = fb.IsPlaying ? (!this.reader[n].perSec ? $Bio.eval(`[$trim(${item})]`, false) : FbTitleFormat(item).Eval()) : $Bio.eval(`[$trim(${item})]`, bio.panel.id.focus); return item.replace(/#@!.*?#@!/g, ''); } getPlainTxtLyrics(item) { const isSynced = this.isSynced(item, true); - item = lyricsBio ? (isSynced ? lyricsBio.parseSyncLyrics(item, !item.length) : lyricsBio.parseUnsyncedLyrics(item, !item.length)) : []; + item = bio.lyrics ? (isSynced ? bio.lyrics.parseSyncLyrics(item, !item.length) : bio.lyrics.parseUnsyncedLyrics(item, !item.length)) : []; let lyr = item.map(v => v.content); const ln = Math.min(5, lyr.length); - if (pptBio.sourceAll) { - const lyricArtist = name.artist(); - const lyricTitle = name.title(); + if (bioSet.sourceAll) { + const lyricArtist = bio.name.artist(); + const lyricTitle = bio.name.title(); const cleanArtist = $Bio.strip(lyricArtist); const cleanTitle = $Bio.strip(lyricTitle); let artistFound = false; @@ -1595,22 +1595,22 @@ class Text { return; } - const subHeadOn = pptBio.sourceAll && pptBio.sourceHeading || pptBio.sourceHeading == 2; - if (!pptBio.star) { // pref heading - if (uiBio.stars == 1) this.ratingPos.heading = true; - else if (panelBio.summary.show) this.ratingPos.summary = true; + const subHeadOn = bioSet.sourceAll && bioSet.sourceHeading || bioSet.sourceHeading == 2; + if (!bioSet.star) { // pref heading + if (bio.ui.stars == 1) this.ratingPos.heading = true; + else if (bio.panel.summary.show) this.ratingPos.summary = true; else if (subHeadOn) this.ratingPos.subHeading = true; else this.ratingPos.text = true; } else { - switch (pptBio.ratingTextPos) { + switch (bioSet.ratingTextPos) { case 0: - if (panelBio.summary.show) this.ratingPos.summary = true; + if (bio.panel.summary.show) this.ratingPos.summary = true; else if (subHeadOn) this.ratingPos.subHeading = true; else this.ratingPos.text = true; break; case 1: - if (panelBio.summary.show) this.ratingPos.summary = true; + if (bio.panel.summary.show) this.ratingPos.summary = true; else this.ratingPos.text = true; break; case 2: @@ -1626,14 +1626,14 @@ class Text { this.rating[`${site}Str`] = ''; switch (true) { case this.ratingPos.summary: - this.rating[`${site}Str`] = this.rating[site] != -1 ? `${pptBio[`${site}RatingName`]}: ${this.rating[site] / 2}` : ''; - if (pptBio[`${site}RatingName`] != 'Album rating') this.rev[`${site}Alb`] = this.rev[`${site}Alb`].replace('Album rating', pptBio[`${site}RatingName`]); + this.rating[`${site}Str`] = this.rating[site] != -1 ? `${bioSet[`${site}RatingName`]}: ${this.rating[site] / 2}` : ''; + if (bioSet[`${site}RatingName`] != 'Album rating') this.rev[`${site}Alb`] = this.rev[`${site}Alb`].replace('Album rating', bioSet[`${site}RatingName`]); break; case this.ratingPos.subHeading: break; case this.ratingPos.line: - this.rev[`${site}Alb`] = `${this.rev.subhead[site][pptBio.heading ? 0 : 1]}\r\n\r\n${this.rev[`${site}Alb`]}`; - if (pptBio[`${site}RatingName`] != 'Album rating') this.rev[`${site}Alb`] = this.rev[`${site}Alb`].replace('Album rating', pptBio[`${site}RatingName`]); + this.rev[`${site}Alb`] = `${this.rev.subhead[site][bioSet.heading ? 0 : 1]}\r\n\r\n${this.rev[`${site}Alb`]}`; + if (bioSet[`${site}RatingName`] != 'Album rating') this.rev[`${site}Alb`] = this.rev[`${site}Alb`].replace('Album rating', bioSet[`${site}RatingName`]); break; } } @@ -1641,18 +1641,18 @@ class Text { getScrollPos() { let scrollPos; let v; - switch (pptBio.artistView) { + switch (bioSet.artistView) { case true: v = `${this.artist}-${this.bio.loaded.ix}-${!this.bio.loaded.txt ? '' : this.bio.readerItem}`; - scrollPos = $Bio.jsonParse(pptBio.bioScrollPos, {}); - if (!scrollPos[v]) return art_scrollbar.setScroll(0); - art_scrollbar.setScroll(scrollPos[v] || 0); + scrollPos = $Bio.jsonParse(bioSet.bioScrollPos, {}); + if (!scrollPos[v]) return bio.art_scrollbar.setScroll(0); + bio.art_scrollbar.setScroll(scrollPos[v] || 0); break; case false: { - v = `${panelBio.style.inclTrackRev != 2 ? `${this.albumartist + this.album + this.composition}-` : ''}-${this.rev.loaded.ix}-${pptBio.inclTrackRev}${!this.rev.loaded.txt ? '' : this.rev.readerItem}`; - scrollPos = $Bio.jsonParse(pptBio.revScrollPos, {}); - if (!scrollPos[v]) return alb_scrollbar.setScroll(0); - alb_scrollbar.setScroll(scrollPos[v] || 0); + v = `${bio.panel.style.inclTrackRev != 2 ? `${this.albumartist + this.album + this.composition}-` : ''}-${this.rev.loaded.ix}-${bioSet.inclTrackRev}${!this.rev.loaded.txt ? '' : this.rev.readerItem}`; + scrollPos = $Bio.jsonParse(bioSet.revScrollPos, {}); + if (!scrollPos[v]) return bio.alb_scrollbar.setScroll(0); + bio.alb_scrollbar.setScroll(scrollPos[v] || 0); break; } } @@ -1664,33 +1664,33 @@ class Text { const items = ['am_w', 'lfm_w', 'wiki_w']; let subHead = [`${this.rev.subhead.am[0]} `, `${this.rev.subhead.lfm[0]} `, `${this.rev.subhead.wiki[0]} `]; - items.forEach((v, i) => this.rev[v].hd = Math.max(g.CalcTextWidth(subHead[i], uiBio.font.subHeadSource), 1)); + items.forEach((v, i) => this.rev[v].hd = Math.max(g.CalcTextWidth(subHead[i], bio.ui.font.subHeadSource), 1)); subHead = [`${this.bio.subhead.am[1]} `, `${this.bio.subhead.lfm[1]} `, `${this.bio.subhead.wiki[1]} `]; - items.forEach((v, i) => this.bio[v].nohd = Math.max(g.CalcTextWidth(subHead[i], uiBio.font.subHeadSource), 1)); + items.forEach((v, i) => this.bio[v].nohd = Math.max(g.CalcTextWidth(subHead[i], bio.ui.font.subHeadSource), 1)); subHead = [`${this.rev.subhead.am[1]} `, `${this.rev.subhead.lfm[1]} `, `${this.rev.subhead.wiki[1]} `]; - items.forEach((v, i) => this.rev[v].nohd = Math.max(g.CalcTextWidth(subHead[i], uiBio.font.subHeadSource), 1)); + items.forEach((v, i) => this.rev[v].nohd = Math.max(g.CalcTextWidth(subHead[i], bio.ui.font.subHeadSource), 1)); - this.bio.sp = Math.max(g.CalcTextWidth(' ', uiBio.font.subHeadSource), 1); + this.bio.sp = Math.max(g.CalcTextWidth(' ', bio.ui.font.subHeadSource), 1); } - this.bio.txt_w.hd = this.bio.txt_w.nohd = Math.max(g.CalcTextWidth(`${this.bio.subhead.txt[1] || ''} `, uiBio.font.subHeadSource), 1); - this.rev.txt_w.hd = this.rev.txt_w.nohd = Math.max(g.CalcTextWidth(`${this.rev.subhead.txt[1] || ''} `, uiBio.font.subHeadSource), 1); + this.bio.txt_w.hd = this.bio.txt_w.nohd = Math.max(g.CalcTextWidth(`${this.bio.subhead.txt[1] || ''} `, bio.ui.font.subHeadSource), 1); + this.rev.txt_w.hd = this.rev.txt_w.nohd = Math.max(g.CalcTextWidth(`${this.rev.subhead.txt[1] || ''} `, bio.ui.font.subHeadSource), 1); }); } getText(p_calc, update, caller) { - if (pptBio.img_only) return; + if (bioSet.img_only) return; const a = $Bio.clean(this.artist); - const n = pptBio.artistView ? 'bio' : 'rev'; + const n = bioSet.artistView ? 'bio' : 'rev'; this.newText = false; - if (!panelBio.lock) { - this.trackartist = name.artist(panelBio.id.focus); - this.track = name.title(panelBio.id.focus); + if (!bio.panel.lock) { + this.trackartist = bio.name.artist(bio.panel.id.focus); + this.track = bio.name.title(bio.panel.id.focus); } if (this[n].reader) this.txtReader(); switch (true) { - case pptBio.artistView: + case bioSet.artistView: if (!a) break; if (!this.done.amBio || update) { this.done.amBio = true; @@ -1705,18 +1705,18 @@ class Text { this.wikiBio(a); } break; - case !pptBio.artistView: { + case !bioSet.artistView: { const aa = $Bio.clean(this.albumartist); - const c = pptBio.classicalMusicMode ? $Bio.clean(this.composition) : ''; + const c = bioSet.classicalMusicMode ? $Bio.clean(this.composition) : ''; const l = $Bio.clean(this.album); - if (!aa || !l && !panelBio.style.inclTrackRev && !c) { + if (!aa || !l && !bio.panel.style.inclTrackRev && !c) { this.resetRevAvailable(); this.rating.am = -1; this.rating.lfm = -1; - butBio.check(); + bio.but.check(); break; } - if (panelBio.isRadio(panelBio.id.focus) && !panelBio.style.inclTrackRev && !panelBio.alb.ix) { + if (bio.panel.isRadio(bio.panel.id.focus) && !bio.panel.style.inclTrackRev && !bio.panel.alb.ix) { this.resetRevAvailable(); break; } @@ -1738,18 +1738,18 @@ class Text { if (!update || this.newText) { this[n].text = []; - const fallbackText = !pptBio.heading ? this[n].fallbackText[1] : this[n].fallbackText[0]; + const fallbackText = !bioSet.heading ? this[n].fallbackText[1] : this[n].fallbackText[0]; const types = $Bio.source.amLfmWikiTxt; const types_1 = this.moveArrayItem(types, 0, 3); // first to last const types_2 = this.moveArrayItem(types_1, 0, 3); const types_3 = this.moveArrayItem(types_2, 0, 3); - const source = pptBio.artistView ? pptBio.sourcebio : pptBio.sourcerev; + const source = bioSet.artistView ? bioSet.sourcebio : bioSet.sourcerev; this.deactivateTooltip(); const type = types[source]; if (this[n].source[type]) { - if ((pptBio.sourceAll && pptBio.sourceHeading || pptBio.sourceHeading == 2) && this[n][type] && type == 'lfm') this[n][type] = this[n][type].replace(/Last\.fm: /g, ''); + if ((bioSet.sourceAll && bioSet.sourceHeading || bioSet.sourceHeading == 2) && this[n][type] && type == 'lfm') this[n][type] = this[n][type].replace(/Last\.fm: /g, ''); this[n].loaded = { am: false, @@ -1760,8 +1760,8 @@ class Text { }; switch (true) { - case !pptBio.sourceAll: - if (!pptBio.lockBio) { // get target else fallback source + case !bioSet.sourceAll: + if (!bioSet.lockBio) { // get target else fallback source const isMainAvail = this.isMainAvail(n); [type, types_1[source], types_2[source], types_3[source]].some(v => { if (this[n][v] && (v != 'txt' || !isMainAvail)) { // favour amLfmWiki fallback if !prefer textreader/lyrics/props @@ -1774,12 +1774,12 @@ class Text { if (this[n][type]) this[n].loaded[type] = true; } break; - case pptBio.sourceAll: { + case bioSet.sourceAll: { let setLoaded = false; - [types, types_1, types_2, types_3][pptBio[`source${n}`]].forEach((v, i) => { + [types, types_1, types_2, types_3][bioSet[`source${n}`]].forEach((v, i) => { if ($Bio.isArray(this[n][v]) ? this[n][v].length : this[n][v]) { - if (pptBio.sourceHeading) { - this[n].text[i] = !$Bio.isArray(this[n][v]) ? (`${this[n].subhead[v][pptBio.heading ? 0 : 1]}\r\n\r\n${this[n][v]}`) : this[n][v]; + if (bioSet.sourceHeading) { + this[n].text[i] = !$Bio.isArray(this[n][v]) ? (`${this[n].subhead[v][bioSet.heading ? 0 : 1]}\r\n\r\n${this[n][v]}`) : this[n][v]; } else this[n].text[i] = this[n][v]; if (!setLoaded) { @@ -1802,33 +1802,33 @@ class Text { this[n].loaded.ix = source; } - if (this[n].text.every(v => !v) && !pptBio.img_only) this[n].text[0] = fallbackText; - if (pptBio.sourceHeading == 2 && !pptBio.sourceAll) { - if (!$Bio.isArray(this[n].text[0])) this[n].text[0] = `${this[n].subhead[types[this[n].loaded.ix]][pptBio.heading ? 0 : 1]}\r\n\r\n${this[n].text[0]}`; + if (this[n].text.every(v => !v) && !bioSet.img_only) this[n].text[0] = fallbackText; + if (bioSet.sourceHeading == 2 && !bioSet.sourceAll) { + if (!$Bio.isArray(this[n].text[0])) this[n].text[0] = `${this[n].subhead[types[this[n].loaded.ix]][bioSet.heading ? 0 : 1]}\r\n\r\n${this[n].text[0]}`; } } - if (panelBio.id.lyricsSource) lyricsBio.clear(); - timerBio.clear(timerBio.lyrics); - if (panelBio.id.lyricsSource) { + if (bio.panel.id.lyricsSource) bio.lyrics.clear(); + bio.timer.clear(bio.timer.lyrics); + if (bio.panel.id.lyricsSource) { if (this[n].loaded.txt && this.reader[n].lyrics) { - if (!this.reader[n].txtLyrics) lyricsBio.load(this[n].txt); + if (!this.reader[n].txtLyrics) bio.lyrics.load(this[n].txt); else this.paint(); } else if (fb.IsPlaying && this.artist && (!this.reader.ESLyricSaved || !this.reader.lyrics3Saved)) { - if (pptBio.syncTxtReaderLyrics) this.lyricsSave(); + if (bioSet.syncTxtReaderLyrics) this.lyricsSave(); } } if (!this[n].loaded.txt || !this.reader[n].lyrics) this.reader[n].txtLyrics = false; - this[n].subHeading = (pptBio.sourceAll && pptBio.sourceHeading || pptBio.sourceHeading == 2) && this[n].text.length && this[n].text[0] != fallbackText ? 1 : 0; + this[n].subHeading = (bioSet.sourceAll && bioSet.sourceHeading || bioSet.sourceHeading == 2) && this[n].text.length && this[n].text[0] != fallbackText ? 1 : 0; - if (!pptBio.heading && this[n].subHeading && this[n].loaded.ix != -1) { + if (!bioSet.heading && this[n].subHeading && this[n].loaded.ix != -1) { const subHeadingWidth = this[n][['am_w', 'lfm_w', 'wiki_w', 'txt_w'][this[n].loaded.ix]].nohd + this.bio.sp; - this[n].ln.x1 = panelBio.text.l + subHeadingWidth; - this[n].ln.x2 = Math.max(this[n].ln.x1, panelBio.text.l + panelBio.text.w); + this[n].ln.x1 = bio.panel.text.l + subHeadingWidth; + this[n].ln.x2 = Math.max(this[n].ln.x1, bio.panel.text.l + bio.panel.text.w); } - if (caller != 'playbackTime') imgBio.setCrop(true); // stop nowplaying time trigger - if (pptBio.artistView) { + if (caller != 'playbackTime') bio.img.setCrop(true); // stop nowplaying time trigger + if (bioSet.artistView) { const bioText = JSON.stringify(this.bio.text); if (bioText != this.bio.cur || p_calc) this.artCalc(); this.bio.cur = bioText; @@ -1837,17 +1837,17 @@ class Text { if (revText != this.rev.cur || p_calc) this.albCalc(); this.rev.cur = revText; } - if (pptBio.text_only && !uiBio.style.isBlur || panelBio.alb.ix && panelBio.style.inclTrackRev) this.paint(); + if (bioSet.text_only && !bio.ui.style.isBlur || bio.panel.alb.ix && bio.panel.style.inclTrackRev) this.paint(); } const lyrPropsNowp = this[n].loaded.txt && (this.reader[n].lyrics || this.reader[n].props || this.reader[n].nowplaying); - const artist = pptBio.artistView ? (!lyrPropsNowp ? this.artist : name.artist(panelBio.id.focus)) : (!lyrPropsNowp ? this.albumartist : name.albumArtist(panelBio.id.focus)); + const artist = bioSet.artistView ? (!lyrPropsNowp ? this.artist : bio.name.artist(bio.panel.id.focus)) : (!lyrPropsNowp ? this.albumartist : bio.name.albumArtist(bio.panel.id.focus)); this.getFlag(artist, n); this.getLogo(artist, n); - if (!pptBio.heading) { + if (!bioSet.heading) { this.newText = false; return; } - if (panelBio.lock && !this.newText) { + if (bio.panel.lock && !this.newText) { if (this.curHeadingID == this.headingID()) { this.newText = false; return; @@ -1855,23 +1855,23 @@ class Text { } this.newText = false; - if (pptBio.artistView) this.heading = uiBio.show.headingText ? this.tf(!this.bio.reader || !this.bio.loaded.txt ? pptBio.bioHeading : this.bio.readerHeading, pptBio.artistView): ''; + if (bioSet.artistView) this.heading = bio.ui.show.headingText ? this.tf(!this.bio.reader || !this.bio.loaded.txt ? bioSet.bioHeading : this.bio.readerHeading, bioSet.artistView) : ''; else { - if (pptBio.classicalMusicMode) panelBio.getList(); - this.heading = uiBio.show.headingText ? + if (bioSet.classicalMusicMode) bio.panel.getList(); + this.heading = bio.ui.show.headingText ? ( - panelBio.style.inclTrackRev && (this.rev.loaded.lfm && this.rev.lfmTrackHeading || this.rev.loaded.am && this.rev.amTrackHeading || this.rev.loaded.wiki && this.rev.wikiTrackHeading) ? - this.tf(pptBio.trkHeading, pptBio.artistView, true) : - this.tf(!this.rev.reader || !this.rev.loaded.txt ? (panelBio.style.inclTrackRev == 2 && !this.isCompositionLoaded() ? pptBio.trkHeading : pptBio.revHeading) : - this.rev.readerHeading, pptBio.artistView) + bio.panel.style.inclTrackRev && (this.rev.loaded.lfm && this.rev.lfmTrackHeading || this.rev.loaded.am && this.rev.amTrackHeading || this.rev.loaded.wiki && this.rev.wikiTrackHeading) ? + this.tf(bioSet.trkHeading, bioSet.artistView, true) : + this.tf(!this.rev.reader || !this.rev.loaded.txt ? (bio.panel.style.inclTrackRev == 2 && !this.isCompositionLoaded() ? bioSet.trkHeading : bioSet.revHeading) : + this.rev.readerHeading, bioSet.artistView) ) : ''; } - if (panelBio.lock) this.curHeadingID = this.headingID(); + if (bio.panel.lock) this.curHeadingID = this.headingID(); } getTxtFallback() { if (this.scrollbar_type().draw_timer) return; - if (!panelBio.updateNeeded()) return; + if (!bio.panel.updateNeeded()) return; if (!this.get && !this.textUpdate) return; this.na = ''; if (this.textUpdate) this.updText(); @@ -1879,7 +1879,7 @@ class Text { this.albumReset(); this.artistReset(); this.getText(this.calc); - if (this.get == 2) panelBio.focusServer(); + if (this.get == 2) bio.panel.focusServer(); this.calc = false; this.get = 0; } @@ -1888,12 +1888,12 @@ class Text { grab() { this.textUpdate = 1; this.notifyTags(); - if (panelBio.block()) return; + if (bio.panel.block()) return; this.updText(); } headingID() { - return `${pptBio.artistView}-${panelBio.art.ix}-${panelBio.alb.ix}-${pptBio.sourcebio}-${pptBio.sourcerev}-${panelBio.style.inclTrackRev}`; + return `${bioSet.artistView}-${bio.panel.art.ix}-${bio.panel.alb.ix}-${bioSet.sourcebio}-${bioSet.sourcerev}-${bio.panel.style.inclTrackRev}`; } increment(n) { @@ -1904,7 +1904,7 @@ class Text { } isCompositionLoaded() { - return !pptBio.artistView && pptBio.classicalMusicMode && (this.rev.loaded.am && !this.rev.amFallback || this.rev.loaded.wiki && !this.rev.wikiFallback) && !panelBio.alb.ix; + return !bioSet.artistView && bioSet.classicalMusicMode && (this.rev.loaded.am && !this.rev.amFallback || this.rev.loaded.wiki && !this.rev.wikiFallback) && !bio.panel.alb.ix; } isLyricsArr(n, v) { @@ -1912,12 +1912,12 @@ class Text { } isMainAvail(n) { - return $Bio.source.amLfmWiki.some(v => this[n][v] && pptBio[`source${n}`] != 3); + return $Bio.source.amLfmWiki.some(v => this[n][v] && bioSet[`source${n}`] != 3); } isSynced(n, lines) { - if (!n || !lyricsBio) return false; - return lines ? n.some(line => lyricsBio.leadingTimestamps.test(line)) : n.match(RegExp(lyricsBio.leadingTimestamps, 'm')); + if (!n || !bio.lyrics) return false; + return lines ? n.some(line => bio.lyrics.leadingTimestamps.test(line)) : n.match(RegExp(bio.lyrics.leadingTimestamps, 'm')); } isTag(n) { @@ -1938,14 +1938,14 @@ class Text { } lfmBio(a) { - const lBio = panelBio.getPth('bio', panelBio.id.focus, this.artist, '', '', cfg.supCache, a, '', '', 'foLfmBio', true).pth; + const lBio = bio.panel.getPth('bio', bio.panel.id.focus, this.artist, '', '', bioCfg.supCache, a, '', '', 'foLfmBio', true).pth; if (!$Bio.file(lBio)) return; this.mod.lfmBio = $Bio.lastModified(lBio) || 0; if (this.mod.lfmBio == this.mod.curLfmBio) return; let bornStr = ''; let foundedIn = ''; this.bio.lfm = $Bio.open(lBio).trim(); - if (!pptBio.stats) { + if (!bioSet.stats) { const f = this.bio.lfm.indexOf('Last.fm: '); if (f != -1) this.bio.lfm = this.bio.lfm.slice(0, f).trim(); } @@ -1956,7 +1956,7 @@ class Text { const o = this.getFoundedIn(this.bio.lfm); foundedIn = o.foundedIn; this.bio.lfm = o.source; - this.bio.lfm = this.formatText('lfmBio', this.bio.lfm, { limit: 6, list: true, key: panelBio.summary.genre ? 'Top Tags: ' : '' }, { str: foundedIn }, { str: bornStr }, panelBio.summary.date ? { key: this.bio.died } : {}, panelBio.summary.date ? { key: this.bio.yrsActive } : {}, !panelBio.summary.other ? {} : { key: 'Last.fm: ' }, panelBio.summary.popNow ? { key: this.bio.popNow } : '').replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); + this.bio.lfm = this.formatText('lfmBio', this.bio.lfm, { limit: 6, list: true, key: bio.panel.summary.genre ? 'Top Tags: ' : '' }, { str: foundedIn }, { str: bornStr }, bio.panel.summary.date ? { key: this.bio.died } : {}, bio.panel.summary.date ? { key: this.bio.yrsActive } : {}, !bio.panel.summary.other ? {} : { key: 'Last.fm: ' }, bio.panel.summary.popNow ? { key: this.bio.popNow } : '').replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); this.newText = true; this.mod.curLfmBio = this.mod.lfmBio; } @@ -1966,32 +1966,32 @@ class Text { let trackLength = ''; let trackRev = ''; let trk = ''; - const lRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.album, '', cfg.supCache, a, aa, l, 'foLfmRev', true).pth; + const lRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.album, '', bioCfg.supCache, a, aa, l, 'foLfmRev', true).pth; this.avail.lfmalb = $Bio.file(lRev) ? 1 : -1; if (this.avail.lfmalb == -1) { this.rating.lfm = -1; - butBio.check(); - if (!panelBio.style.inclTrackRev) { + bio.but.check(); + if (!bio.panel.style.inclTrackRev) { this.rev.lfmAlb = ''; return; } } trk = this.track.toLowerCase(); - trackRev = $Bio.jsonParse(panelBio.getPth('track', panelBio.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foLfmRev', true).pth, false, 'file'); + trackRev = $Bio.jsonParse(bio.panel.getPth('track', bio.panel.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foLfmRev', true).pth, false, 'file'); this.avail.lfmtrk = this.isTrackRevAvail('lfm', trackRev[trk]); - if (panelBio.style.inclTrackRev && trackRev[trk] && trackRev[trk].update) lfm_tr_mod = trackRev[trk].update; + if (bio.panel.style.inclTrackRev && trackRev[trk] && trackRev[trk].update) lfm_tr_mod = trackRev[trk].update; this.mod.lfmRev = ($Bio.lastModified(lRev) || 0) + (lfm_tr_mod || 0); if (this.mod.lfmRev == this.mod.curLfmRev) return; this.rev.lfmAlb = ''; - if (panelBio.style.inclTrackRev != 2) this.rev.lfmAlb = $Bio.open(lRev).trim(); + if (bio.panel.style.inclTrackRev != 2) this.rev.lfmAlb = $Bio.open(lRev).trim(); this.rev.lfmAlb = this.rev.lfmAlb.replace(/\s\u200b\|[\d.,\s]*?;/g, ';').replace(/\u200b\|[\d.,\s]*?$/gm, ''); this.newText = true; this.mod.curLfmRev = this.mod.lfmRev; this.rating.lfm = -1; - if (panelBio.style.inclTrackRev != 2) { - if (pptBio.lfmRating) { + if (bio.panel.style.inclTrackRev != 2) { + if (bioSet.lfmRating) { const b = this.rev.lfmAlb.indexOf('Rating: '); if (b != -1) { this.rating.lfm = this.rev.lfmAlb.substring(b).replace(/\D/g, ''); @@ -2003,18 +2003,18 @@ class Text { } else { this.rating.lfmStr = ''; } - this.rev.lfmAlb = pptBio.score ? this.rev.lfmAlb.replace('Rating: ', '') : this.rev.lfmAlb.replace(/^Rating: .*$/m, '').trim(); + this.rev.lfmAlb = bioSet.score ? this.rev.lfmAlb.replace('Rating: ', '') : this.rev.lfmAlb.replace(/^Rating: .*$/m, '').trim(); } this.rev.lfm = this.rev.lfmAlb; let needTrackSubHeading = false; let releases = ''; - if (panelBio.style.inclTrackRev) { + if (bio.panel.style.inclTrackRev) { if (trackRev && trackRev[trk]) { const o = trackRev[trk]; let wiki = ''; releases = $Bio.getProp(o, 'releases', ''); - if (!panelBio.summary.date) wiki = this.add([releases], wiki); + if (!bio.panel.summary.date) wiki = this.add([releases], wiki); releases = releases.replace(/\.$/, ''); if (releases.includes('\u200b')) { const chk = releases.split(/\u200b:\s|\u200b,\s|\s\u200band\s/); @@ -2028,7 +2028,7 @@ class Text { } } wiki = this.add([$Bio.getProp(o, 'wiki', '')], wiki); - const showGenres = !pptBio.autoOptimiseText || !this.rev.lfmAlb; + const showGenres = !bioSet.autoOptimiseText || !this.rev.lfmAlb; let tags = ''; if (showGenres) { tags = $Bio.getProp(o, 'tags', []).join('\u200b, '); @@ -2036,17 +2036,17 @@ class Text { } const length = $Bio.getProp(o, 'length', ''); if (length) { - const ix = cfg.lang.arr.indexOf(o.lang); + const ix = bioCfg.lang.arr.indexOf(o.lang); const label = this.rev.length[ix]; trackLength = label + length; } const stats = $Bio.getProp(o, 'stats', ''); wiki = this.add([tags, trackLength, stats], wiki); if (wiki) { - if (pptBio.trackHeading == 1 && (this.rev.lfmAlb || !pptBio.heading) || pptBio.trackHeading == 2) { + if (bioSet.trackHeading == 1 && (this.rev.lfmAlb || !bioSet.heading) || bioSet.trackHeading == 2) { this.rev.lfmTrackHeading = false; if (this.rev.lfmAlb) { - trackRev = `!\u00a6${this.tf(pptBio.trackSubHeading, pptBio.artistView, true)}\r\n\r\n${wiki}`; + trackRev = `!\u00a6${this.tf(bioSet.trackSubHeading, bioSet.artistView, true)}\r\n\r\n${wiki}`; } else { trackRev = wiki; needTrackSubHeading = true; @@ -2055,68 +2055,68 @@ class Text { this.rev.lfmTrackHeading = true; trackRev = wiki; } - if (panelBio.summary.other) trackRev = trackRev.replace(/^Last\.fm:\s/gm, 'Last-fm: '); + if (bio.panel.summary.other) trackRev = trackRev.replace(/^Last\.fm:\s/gm, 'Last-fm: '); this.rev.lfm = this.add([trackRev], this.rev.lfmAlb); } else { - this.rev.lfmTrackHeading = panelBio.style.inclTrackRev == 2; + this.rev.lfmTrackHeading = bio.panel.style.inclTrackRev == 2; } } else { - this.rev.lfmTrackHeading = panelBio.style.inclTrackRev == 2; + this.rev.lfmTrackHeading = bio.panel.style.inclTrackRev == 2; } } - if (!pptBio.stats) { + if (!bioSet.stats) { this.rev.lfm = this.rev.lfm.replace(/^Last\.fm: .*$(\n)?/gm, '').trim(); } - this.rev.lfm = this.formatText('lfmRev', this.rev.lfm, panelBio.summary.genre ? { limit: 6, list: true, key: this.rev.lfmAlb ? 'Top Tags: ' : 'Track Tags: ' } : {}, panelBio.summary.date ? { key: this.rev.releaseDate } : {}, !panelBio.summary.date || this.rev.lfmAlb ? {} : { str: releases }, !panelBio.summary.other ? {} : { key: this.rev.lfmAlb ? 'Last.fm: ' : 'Last-fm: ' }, { str: this.rating.lfmStr }); - if (panelBio.summary.show || !pptBio.stats) this.rev.lfm = this.rev.lfm.replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); - if (needTrackSubHeading) this.rev.lfm = `!\u00a6${this.tf(pptBio.trackSubHeading, pptBio.artistView, true)}\r\n\r\n${this.rev.lfm}`; - if (!this.rev.lfm) butBio.check(); + this.rev.lfm = this.formatText('lfmRev', this.rev.lfm, bio.panel.summary.genre ? { limit: 6, list: true, key: this.rev.lfmAlb ? 'Top Tags: ' : 'Track Tags: ' } : {}, bio.panel.summary.date ? { key: this.rev.releaseDate } : {}, !bio.panel.summary.date || this.rev.lfmAlb ? {} : { str: releases }, !bio.panel.summary.other ? {} : { key: this.rev.lfmAlb ? 'Last.fm: ' : 'Last-fm: ' }, { str: this.rating.lfmStr }); + if (bio.panel.summary.show || !bioSet.stats) this.rev.lfm = this.rev.lfm.replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); + if (needTrackSubHeading) this.rev.lfm = `!\u00a6${this.tf(bioSet.trackSubHeading, bioSet.artistView, true)}\r\n\r\n${this.rev.lfm}`; + if (!this.rev.lfm) bio.but.check(); } loadLyric() { setTimeout(() => { this.getText(); }, 1000); - timerBio.clear(timerBio.lyrics); + bio.timer.clear(bio.timer.lyrics); } loadReader() { this.bio.reader = false; for (let i = 0; i < 4; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && pptBio[`pthTxtReader${i}`]) { + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && bioSet[`pthTxtReader${i}`]) { this.bio.reader = true; break; } } this.rev.reader = false; for (let i = 4; i < 8; i++) { - if (pptBio.txtReaderEnable && pptBio[`useTxtReader${i}`] && pptBio[`pthTxtReader${i}`]) { + if (bioSet.txtReaderEnable && bioSet[`useTxtReader${i}`] && bioSet[`pthTxtReader${i}`]) { this.rev.reader = true; break; } } - pptBio.sourcebio = $Bio.clamp(pptBio.sourcebio, 0, this.bio.reader ? 3 : 2); - pptBio.sourcerev = $Bio.clamp(pptBio.sourcerev, 0, this.rev.reader ? 3 : 2); + bioSet.sourcebio = $Bio.clamp(bioSet.sourcebio, 0, this.bio.reader ? 3 : 2); + bioSet.sourcerev = $Bio.clamp(bioSet.sourcerev, 0, this.rev.reader ? 3 : 2); this.bio.source = { - am: pptBio.sourcebio == 0, - lfm: pptBio.sourcebio == 1, - wiki: pptBio.sourcebio == 2, - txt: pptBio.sourcebio == 3 + am: bioSet.sourcebio == 0, + lfm: bioSet.sourcebio == 1, + wiki: bioSet.sourcebio == 2, + txt: bioSet.sourcebio == 3 }; this.rev.source = { - am: pptBio.sourcerev == 0, - lfm: pptBio.sourcerev == 1, - wiki: pptBio.sourcerev == 2, - txt: pptBio.sourcerev == 3 + am: bioSet.sourcerev == 0, + lfm: bioSet.sourcerev == 1, + wiki: bioSet.sourcerev == 2, + txt: bioSet.sourcerev == 3 }; this.bio.readerItem = ''; this.rev.readerItem = ''; this.reader.items = []; for (let i = 0; i < 8; i++) { - const item = pptBio[`useTxtReader${i}`] ? pptBio[`pthTxtReader${i}`] : ''; + const item = bioSet[`useTxtReader${i}`] ? bioSet[`pthTxtReader${i}`] : ''; this.reader.items.push({ view: i < 4 ? 'bio' : 'rev', - lyrics: pptBio[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(item)[1]), - name: pptBio[`nmTxtReader${i}`], + lyrics: bioSet[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(item)[1]), + name: bioSet[`nmTxtReader${i}`], nowplaying: /nowplaying/i.test(utils.SplitFilePath(item)[1]), props: /item_properties/i.test(utils.SplitFilePath(item)[1]), pth: item, @@ -2124,7 +2124,7 @@ class Text { }); } for (let i = 0; i < 8; i++) { - this.reader.items[i].heading = this.reader.items[i].lyrics ? pptBio.lyricHeading : this.reader.items[i].props ? pptBio.trkHeading : this.reader.items[i].view == 'bio' ? pptBio.bioHeading : pptBio.revHeading; + this.reader.items[i].heading = this.reader.items[i].lyrics ? bioSet.lyricHeading : this.reader.items[i].props ? bioSet.trkHeading : this.reader.items[i].view == 'bio' ? bioSet.bioHeading : bioSet.revHeading; } } @@ -2132,23 +2132,23 @@ class Text { let keys = []; let scrollPos; let v; - n = n == 'rev' ? false : n == 'bio' ? true : pptBio.artistView; + n = n == 'rev' ? false : n == 'bio' ? true : bioSet.artistView; switch (n) { case true: - scrollPos = $Bio.jsonParse(pptBio.bioScrollPos, {}); + scrollPos = $Bio.jsonParse(bioSet.bioScrollPos, {}); keys = Object.keys(scrollPos); if (keys.length > 70) delete scrollPos[keys[0]]; v = `${this.artist}-${this.bio.loaded.ix}-${!this.bio.loaded.txt ? '' : this.bio.readerItem}`; - scrollPos[v] = art_scrollbar.scroll; - pptBio.bioScrollPos = JSON.stringify(scrollPos); + scrollPos[v] = bio.art_scrollbar.scroll; + bioSet.bioScrollPos = JSON.stringify(scrollPos); break; case false: - scrollPos = $Bio.jsonParse(pptBio.revScrollPos, {}); + scrollPos = $Bio.jsonParse(bioSet.revScrollPos, {}); keys = Object.keys(scrollPos); if (keys.length > 70) delete scrollPos[keys[0]]; - v = `${panelBio.style.inclTrackRev != 2 ? `${this.albumartist + this.album + this.composition}-` : ''}-${this.rev.loaded.ix}-${pptBio.inclTrackRev}${!this.rev.loaded.txt ? '' : this.rev.readerItem}`; - scrollPos[v] = alb_scrollbar.scroll; - pptBio.revScrollPos = JSON.stringify(scrollPos); + v = `${bio.panel.style.inclTrackRev != 2 ? `${this.albumartist + this.album + this.composition}-` : ''}-${this.rev.loaded.ix}-${bioSet.inclTrackRev}${!this.rev.loaded.txt ? '' : this.rev.readerItem}`; + scrollPos[v] = bio.alb_scrollbar.scroll; + bioSet.revScrollPos = JSON.stringify(scrollPos); break; } } @@ -2156,21 +2156,21 @@ class Text { lyricExists() { return this.reader.items.some(v => { if (v.lyrics) { - return v.tag ? $Bio.eval(`[$trim(${v.pth})]`, false) : this.findFile(v, pptBio.artistView ? 'bio' : 'rev'); + return v.tag ? $Bio.eval(`[$trim(${v.pth})]`, false) : this.findFile(v, bioSet.artistView ? 'bio' : 'rev'); } }); } lyricsDisplayed() { - const n = pptBio.artistView ? 'bio' : 'rev'; - return this[n].loaded.txt && this.reader[n].lyrics && !this.reader[n].txtLyrics && !pptBio.img_only; + const n = bioSet.artistView ? 'bio' : 'rev'; + return this[n].loaded.txt && this.reader[n].lyrics && !this.reader[n].txtLyrics && !bioSet.img_only; } lyricsSave() { if (!this.lyrics.ESLyricInstalled && !this.lyrics.lyrics3Installed) return; let counter = 0; - timerBio.clear(timerBio.lyrics); - timerBio.lyrics.id = setInterval(() => { + bio.timer.clear(bio.timer.lyrics); + bio.timer.lyrics.id = setInterval(() => { if (this.lyrics.lyrics3Installed && !this.reader.lyrics3Saved) { if ($Bio.eval('%lyric_exists%', false)) { fb.RunMainMenuCommand('View/Lyrics Show 3/Save'); @@ -2187,17 +2187,17 @@ class Text { } } counter++; - if (counter == 30) timerBio.clear(timerBio.lyrics); + if (counter == 30) bio.timer.clear(bio.timer.lyrics); }, 1000); } move(x, y) { this.get_ix(x, y); if (this.rev.ix != -1) { - if (!pptBio.img_only && !this.lyricsDisplayed()) this.check_tooltip(this.rev.arr[this.rev.ix], x, y, false, this.line.h.rev); + if (!bioSet.img_only && !this.lyricsDisplayed()) this.check_tooltip(this.rev.arr[this.rev.ix], x, y, false, this.line.h.rev); else this.deactivateTooltip(); } else if (this.bio.ix != -1) { - if (!pptBio.img_only && !this.lyricsDisplayed()) this.check_tooltip(this.bio.arr[this.bio.ix], x, y, true, this.line.h.bio); + if (!bioSet.img_only && !this.lyricsDisplayed()) this.check_tooltip(this.bio.arr[this.bio.ix], x, y, true, this.line.h.bio); else this.deactivateTooltip(); } else this.deactivateTooltip(); if (this.rev.ix == this.rev.cur_ix && this.bio.ix == this.bio.cur_ix) return; @@ -2212,17 +2212,17 @@ class Text { } notifyTags() { - if (!cfg.notifyTags && !tagBio.force) return; + if (!bioCfg.notifyTags && !bio.tag.force) return; this.currentTrackTags(); } on_playback_new_track(force) { - if (panelBio.lock) panelBio.getList(); + if (bio.panel.lock) bio.panel.getList(); this.notifyTags(); - if (!panelBio.updateNeeded() && !force) return; - if (panelBio.block()) { + if (!bio.panel.updateNeeded() && !force) return; + if (bio.panel.block()) { this.get = 1; - if (!panelBio.lock) panelBio.getList(true); + if (!bio.panel.lock) bio.panel.getList(true); this.logScrollPos(); this.albumReset(); this.artistReset(); @@ -2232,7 +2232,7 @@ class Text { this.artistReset(); this.na = ''; this.getText(false); - if (!panelBio.lock) panelBio.getList(true); + if (!bio.panel.lock) bio.panel.getList(true); this.get = 0; } } @@ -2244,55 +2244,55 @@ class Text { this.bio.cur = ''; this.rev.cur = ''; this.getText(false); - panelBio.getList(true); - butBio.refresh(true); + bio.panel.getList(true); + bio.but.refresh(true); this.notifyTags(); } paint() { if (!this.repaint) return; - if (!panelBio.style.showFilmStrip || pptBio.filmStripOverlay) window.Repaint(); - else window.RepaintRect(0, geo.topMenuHeight, uiBio.w, uiBio.h); + if (!bio.panel.style.showFilmStrip || bioSet.filmStripOverlay) window.Repaint(); + else window.RepaintRect(0, grm.ui.topMenuHeight, bio.ui.w, bio.ui.h); } refresh(n) { switch (n) { case 0: // general style changes etc - filmStrip.logScrollPos(); - filmStrip.setFilmStripSize(); - panelBio.setStyle(); - imgBio.clearCache(); + bio.filmStrip.logScrollPos(); + bio.filmStrip.setFilmStripSize(); + bio.panel.setStyle(); + bio.img.clearCache(); this.albumFlush(); this.artistFlush(); this.rev.cur = ''; this.bio.cur = ''; this.getText(true); - butBio.refresh(); - imgBio.getImages(); - if (pptBio.showFilmStrip && pptBio.autoFilm) this.getScrollPos(); - butBio.setLookUpPos(); + bio.but.refresh(); + bio.img.getImages(); + if (bioSet.showFilmStrip && bioSet.autoFilm) this.getScrollPos(); + bio.but.setLookUpPos(); break; case 1: // onOff heading summary; setReviewType; toggle('sourceAll'); toggle('classicalMusicMode') all except setReviewType set scrollPos = {} to forcescrollReset - uiBio.getFont(); - uiBio.calcText(); - panelBio.setStyle(); - uiBio.getColours(); - butBio.createStars(); + bio.ui.getFont(); + bio.ui.calcText(); + bio.panel.setStyle(); + bio.ui.getColours(); + bio.but.createStars(); this.albumFlush(); this.artistFlush(); - if (!pptBio.img_only) imgBio.clearCache(); + if (!bioSet.img_only) bio.img.clearCache(); this.rev.cur = ''; this.bio.cur = ''; this.getText(true); - butBio.refresh(true); - imgBio.getImages(); + bio.but.refresh(true); + bio.img.getImages(); break; case 2: // reset zoom - if (panelBio.style.inclTrackRev == 1) this.logScrollPos(); - uiBio.getColours(); - uiBio.getFont(); - panelBio.setStyle(); - if (!pptBio.img_only) imgBio.clearCache(); + if (bio.panel.style.inclTrackRev == 1) this.logScrollPos(); + bio.ui.getColours(); + bio.ui.getFont(); + bio.panel.setStyle(); + if (!bioSet.img_only) bio.img.clearCache(); this.albumFlush(); this.artistFlush(); this.rev.cur = ''; @@ -2300,25 +2300,25 @@ class Text { this.reader.w.nameCol = 10; this.reader.w.valueCol = 10; this.getText(true); - imgBio.getImages(); - if (pptBio.text_only && !uiBio.style.isBlur) this.paint(); + bio.img.getImages(); + if (bioSet.text_only && !bio.ui.style.isBlur) this.paint(); break; case 3: // wheel setZoom & resetStyle - filmStrip.logScrollPos(); + bio.filmStrip.logScrollPos(); this.logScrollPos(); - panelBio.setStyle(); + bio.panel.setStyle(); this.albumFlush(); this.artistFlush(); - imgBio.clearCache(); - butBio.refresh(); - if (panelBio.stndItem()) { + bio.img.clearCache(); + bio.but.refresh(); + if (bio.panel.stndItem()) { this.getText(false); - imgBio.getImages(); + bio.img.getImages(); } else { - this.getItem(false, panelBio.art.ix, panelBio.alb.ix); - imgBio.getItem(panelBio.art.ix, panelBio.alb.ix); + this.getItem(false, bio.panel.art.ix, bio.panel.alb.ix); + bio.img.getItem(bio.panel.art.ix, bio.panel.alb.ix); } - if (pptBio.artistView) { + if (bioSet.artistView) { this.rev.cur = ''; this.artCalc(); } else { @@ -2327,7 +2327,7 @@ class Text { } break; } - initBiographyColors(); + grm.theme.initBiographyColors(); this.artCalc(); this.albCalc(); // Refresh text color } @@ -2339,13 +2339,13 @@ class Text { } revPth(n) { - if (pptBio.img_only) return ['', '', false, false]; - const field = n != 'Am' && n != 'Wiki' ? this.album : !pptBio.classicalMusicMode || n == 'Am' && this.rev.amFallback || n == 'Wiki' && this.rev.wikiFallback || panelBio.alb.ix ? this.album : this.composition; - return panelBio.getPth('rev', panelBio.id.focus, this.artist, field, '', cfg.supCache, $Bio.clean(this.artist), $Bio.clean(this.albumartist), $Bio.clean(field), `fo${n}Rev`, false); + if (bioSet.img_only) return ['', '', false, false]; + const field = n != 'Am' && n != 'Wiki' ? this.album : !bioSet.classicalMusicMode || n == 'Am' && this.rev.amFallback || n == 'Wiki' && this.rev.wikiFallback || bio.panel.alb.ix ? this.album : this.composition; + return bio.panel.getPth('rev', bio.panel.id.focus, this.artist, field, '', bioCfg.supCache, $Bio.clean(this.artist), $Bio.clean(this.albumartist), $Bio.clean(field), `fo${n}Rev`, false); } scrollbar_type() { - return pptBio.artistView ? art_scrollbar : alb_scrollbar; + return bioSet.artistView ? bio.art_scrollbar : bio.alb_scrollbar; } setSectionHeading(item, name, arr) { @@ -2357,18 +2357,18 @@ class Text { tf(n, artistView, trackreview) { if (!n) return ''; - if (panelBio.lock) n = n.replace(/%artist%|\$meta\(artist,0\)/g, '#\u00a6#\u00a6#%artist%#\u00a6#\u00a6#').replace(/%title%|\$meta\(title,0\)/g, '#!#!#%title%#!#!#'); + if (bio.panel.lock) n = n.replace(/%artist%|\$meta\(artist,0\)/g, '#\u00a6#\u00a6#%artist%#\u00a6#\u00a6#').replace(/%title%|\$meta\(title,0\)/g, '#!#!#%title%#!#!#'); const b = artistView ? 'bio' : 'rev'; - const a = this[b].loaded.txt && (this.reader[b].lyrics || this.reader[b].props || this.reader[b].nowplaying) ? $Bio.tfEscape(name.artist(panelBio.id.focus)) : $Bio.tfEscape(artistView ? this.artist : (!trackreview ? (panelBio.alb.ix ? this.albumartist : this.artist) : this.trackartist)); - const aa = this[b].loaded.txt && (this.reader[b].lyrics || this.reader[b].props || this.reader[b].nowplaying) || panelBio.isRadio(panelBio.id.focus) && !panelBio.alb.ix ? $Bio.tfEscape(name.albumArtist(panelBio.id.focus)) : $Bio.tfEscape(artistView ? (panelBio.art.ix ? this.artist : this.albumartist) : (!trackreview ? this.albumartist : this.trackartist)); + const a = this[b].loaded.txt && (this.reader[b].lyrics || this.reader[b].props || this.reader[b].nowplaying) ? $Bio.tfEscape(bio.name.artist(bio.panel.id.focus)) : $Bio.tfEscape(artistView ? this.artist : (!trackreview ? (bio.panel.alb.ix ? this.albumartist : this.artist) : this.trackartist)); + const aa = this[b].loaded.txt && (this.reader[b].lyrics || this.reader[b].props || this.reader[b].nowplaying) || bio.panel.isRadio(bio.panel.id.focus) && !bio.panel.alb.ix ? $Bio.tfEscape(bio.name.albumArtist(bio.panel.id.focus)) : $Bio.tfEscape(artistView ? (bio.panel.art.ix ? this.artist : this.albumartist) : (!trackreview ? this.albumartist : this.trackartist)); const composition = this.isCompositionLoaded(); const l = composition ? $Bio.tfEscape(this.composition.replace('Album Unknown', '')) : $Bio.tfEscape(this.album.replace('Album Unknown', '')); const tr = $Bio.tfEscape(this.track); - if (composition) n = n.replace(/%bio_album%/gi, cfg.tf.composition); - n = n.replace(/%lookup_item%/gi, panelBio.simTagTopLookUp() ? '$&#@!%path%#@!' : '$&'); + if (composition) n = n.replace(/%bio_album%/gi, bioCfg.tf.composition); + n = n.replace(/%lookup_item%/gi, bio.panel.simTagTopLookUp() ? '$&#@!%path%#@!' : '$&'); n = n.replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_artist%/gi, a ? '$&#@!%path%#@!' : '$&').replace(/%bio_artist%/gi, a).replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_albumartist%/gi, aa ? '$&#@!%path%#@!' : '$&').replace(/%bio_albumartist%/gi, aa).replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_album%/gi, l ? '$&#@!%path%#@!' : '$&').replace(/%bio_album%/gi, l).replace(/((\$if|\$and|\$or|\$not|\$xor)(|\d)\(|\[)[^$%]*%bio_title%/gi, tr ? '$&#@!%path%#@!' : '$&').replace(/%bio_title%/gi, tr); - n = $Bio.eval(n, panelBio.id.focus); - if (panelBio.lock) n = n.replace(/#\u00a6#\u00a6#.*?#\u00a6#\u00a6#/g, this.trackartist).replace(/#!#!#.*?#!#!#/g, this.track); + n = $Bio.eval(n, bio.panel.id.focus); + if (bio.panel.lock) n = n.replace(/#\u00a6#\u00a6#.*?#\u00a6#\u00a6#/g, this.trackartist).replace(/#!#!#.*?#!#!#/g, this.track); return n.replace(/#@!.*?#@!/g, '') || 'No Selection'; } @@ -2388,7 +2388,7 @@ class Text { } tidyWiki(n) { - if (!this[n].loaded.wiki && !pptBio.sourceAll || !pptBio.wikiStyle) return; + if (!this[n].loaded.wiki && !bioSet.sourceAll || !bioSet.wikiStyle) return; const arr = this[n].arr; let i = arr.length; while (i--) { @@ -2399,23 +2399,23 @@ class Text { } trackPth(n) { - if (pptBio.img_only || pptBio.artistView) return ['', '', false, false]; - return panelBio.getPth('track', panelBio.id.focus, this.artist, 'Track Reviews', '', '', $Bio.clean(this.artist), '', 'Track Reviews', `fo${n}Rev`, false); + if (bioSet.img_only || bioSet.artistView) return ['', '', false, false]; + return bio.panel.getPth('track', bio.panel.id.focus, this.artist, 'Track Reviews', '', '', $Bio.clean(this.artist), '', 'Track Reviews', `fo${n}Rev`, false); } txtBioPth() { - if (!pptBio.artistView || !pptBio.txtReaderEnable || pptBio.img_only) return ['', '', false, false]; + if (!bioSet.artistView || !bioSet.txtReaderEnable || bioSet.img_only) return ['', '', false, false]; let pth = ''; if (!this.bio.readerTag) pth = this.bio.readerItem; else { - const handle = $Bio.handle(panelBio.id.focus); + const handle = $Bio.handle(bio.panel.id.focus); if (handle) pth = handle.Path; } return ['', pth, true, $Bio.file(pth)]; } txtReader() { - const n = pptBio.artistView ? 'bio' : 'rev'; + const n = bioSet.artistView ? 'bio' : 'rev'; this[n].readerTag = false; let found = -1; let nm = $Bio.titlecase('textreader'); @@ -2444,7 +2444,7 @@ class Text { this.reader[n].props = false; this[n].readerItem = ''; this[n].txt = ''; - butBio.createStars(); + bio.but.createStars(); return; } this.reader[n].lyrics = this.reader.items[found].lyrics; @@ -2453,7 +2453,7 @@ class Text { nm = this.upperCaseFirst(this.reader.items[found].name); this[n].subhead.txt = [nm, nm]; this[n].readerHeading = this.reader.items[found].heading; - butBio.createStars(); + bio.but.createStars(); this.getSubHeadWidths(true); if (this.reader.items[found].tag) { this[n].readerTag = true; @@ -2507,11 +2507,11 @@ class Text { } txtRevPth() { - if (pptBio.artistView || !pptBio.txtReaderEnable || pptBio.img_only) return ['', '', false, false]; + if (bioSet.artistView || !bioSet.txtReaderEnable || bioSet.img_only) return ['', '', false, false]; let pth = ''; if (!this.rev.readerTag) pth = this.rev.readerItem; else { - const handle = $Bio.handle(panelBio.id.focus); + const handle = $Bio.handle(bio.panel.id.focus); if (handle) pth = handle.Path; } return ['', pth, true, $Bio.file(pth)]; @@ -2519,8 +2519,8 @@ class Text { updText() { this.getText(false, true); - imgBio.getArtImg(); - imgBio.getFbImg(); + bio.img.getArtImg(); + bio.img.getFbImg(); this.textUpdate = 0; this.done.amBio = this.done.lfmBio = this.done.amRev = this.done.lfmRev = this.done.wikiRev = false; } @@ -2530,10 +2530,10 @@ class Text { } wikiBio(a) { - const wBio = panelBio.getPth('bio', panelBio.id.focus, this.artist, '', '', cfg.supCache, a, '', '', 'foWikiBio', true).pth; - const lBio = panelBio.getPth('bio', panelBio.id.focus, this.artist, '', '', cfg.supCache, a, '', '', 'foLfmBio', true).pth; + const wBio = bio.panel.getPth('bio', bio.panel.id.focus, this.artist, '', '', bioCfg.supCache, a, '', '', 'foWikiBio', true).pth; + const lBio = bio.panel.getPth('bio', bio.panel.id.focus, this.artist, '', '', bioCfg.supCache, a, '', '', 'foLfmBio', true).pth; if (!$Bio.file(wBio) && !$Bio.file(lBio)) return; - this.mod.wikiBio = ($Bio.lastModified(wBio) || 0) + (panelBio.summary.show ? ($Bio.lastModified(lBio) || 0) : 0); + this.mod.wikiBio = ($Bio.lastModified(wBio) || 0) + (bio.panel.summary.show ? ($Bio.lastModified(lBio) || 0) : 0); if (this.mod.wikiBio == this.mod.curWikiBio) return; this.bio.wiki = $Bio.open(wBio).replace(/\u200b/g, '').trim(); const checkGenre = this.checkGenre(this.bio.wiki); @@ -2544,16 +2544,16 @@ class Text { let foundedIn = ''; let latest = ''; - if (this.bio.wiki && panelBio.summary.show) { - if (panelBio.summary.latest) { - const latestRelease = tagBio.getTag(bioLfm, this.bio.latestRelease, true); + if (this.bio.wiki && bio.panel.summary.show) { + if (bio.panel.summary.latest) { + const latestRelease = bio.tag.getTag(bioLfm, this.bio.latestRelease, true); if (latestRelease.tag) latest = latestRelease.label + latestRelease.tag; } const b = this.getBornStr(en ? this.bio.wiki : bioLfm); bornStr = b.bornStr; if (en) this.bio.wiki = b.source; else if (!bornStr) { - const y = tagBio.getTag(bioLfm, this.bio.yrsActive, true); + const y = bio.tag.getTag(bioLfm, this.bio.yrsActive, true); if (y.tag) { active = y.label + y.tag; } @@ -2563,7 +2563,7 @@ class Text { if (en) this.bio.wiki = o.source; } - if (pptBio.sourceAll && pptBio.autoOptimiseText) { + if (bioSet.sourceAll && bioSet.autoOptimiseText) { const f = this.bio.wiki.indexOf('=='); if (f != -1) { const ix = this.bio.wiki.lastIndexOf('Genre: '); @@ -2576,7 +2576,7 @@ class Text { } } this.bio.wiki = this.bio.wiki.replace(/Wikipedia language:\s[A-Z]{2}/, ''); - this.bio.wiki = this.formatText('wikiBio', this.bio.wiki, panelBio.summary.genre ? { limit: 6, list: true, key: 'Genre: ' } : {}, { str: foundedIn }, { str: bornStr }, panelBio.summary.date ? { key: this.bio.died } : {}, panelBio.summary.date ? (en ? { key: this.bio.yrsActive } : { str: active }) : {}, '', panelBio.summary.latest ? { str: latest } : '', checkGenre.singleGenre).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); + this.bio.wiki = this.formatText('wikiBio', this.bio.wiki, bio.panel.summary.genre ? { limit: 6, list: true, key: 'Genre: ' } : {}, { str: foundedIn }, { str: bornStr }, bio.panel.summary.date ? { key: this.bio.died } : {}, bio.panel.summary.date ? (en ? { key: this.bio.yrsActive } : { str: active }) : {}, '', bio.panel.summary.latest ? { str: latest } : '', checkGenre.singleGenre).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); this.newText = true; this.mod.curWikiBio = this.mod.wikiBio; } @@ -2592,36 +2592,36 @@ class Text { let wiki_tr_mod = 0; let writer = ''; - if (!pptBio.classicalMusicMode || panelBio.alb.ix) { - wRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.album, '', cfg.supCache, a, aa, l, 'foWikiRev', true).pth; - } else if (!panelBio.alb.ix) { - wRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.composition, '', cfg.supCache, a, aa, c, 'foWikiRev', true).pth; + if (!bioSet.classicalMusicMode || bio.panel.alb.ix) { + wRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.album, '', bioCfg.supCache, a, aa, l, 'foWikiRev', true).pth; + } else if (!bio.panel.alb.ix) { + wRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.composition, '', bioCfg.supCache, a, aa, c, 'foWikiRev', true).pth; if ($Bio.file(wRev)) foundComp = true; } this.rev.wikiFallback = !foundComp; - if (!$Bio.file(wRev) && pptBio.classicalAlbFallback && !panelBio.alb.ix && panelBio.style.inclTrackRev != 2) { - wRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.album, '', cfg.supCache, a, aa, l, 'foWikiRev', true).pth; + if (!$Bio.file(wRev) && bioSet.classicalAlbFallback && !bio.panel.alb.ix && bio.panel.style.inclTrackRev != 2) { + wRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.album, '', bioCfg.supCache, a, aa, l, 'foWikiRev', true).pth; } this.avail.wikialb = $Bio.file(wRev) ? 2 : -1; - const lRev = panelBio.getPth('rev', panelBio.id.focus, this.artist, this.album, '', cfg.supCache, a, aa, l, 'foLfmRev', true).pth; + const lRev = bio.panel.getPth('rev', bio.panel.id.focus, this.artist, this.album, '', bioCfg.supCache, a, aa, l, 'foLfmRev', true).pth; if (this.avail.wikialb == -1 && !$Bio.file(lRev)) { - butBio.check(); - if (!panelBio.style.inclTrackRev || pptBio.classicalMusicMode && !pptBio.classicalAlbFallback) { + bio.but.check(); + if (!bio.panel.style.inclTrackRev || bioSet.classicalMusicMode && !bioSet.classicalAlbFallback) { this.rev.wikiAlb = ''; return; } } trk = this.track.toLowerCase(); - trackRev = $Bio.jsonParse(panelBio.getPth('track', panelBio.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foWikiRev', true).pth, false, 'file'); + trackRev = $Bio.jsonParse(bio.panel.getPth('track', bio.panel.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foWikiRev', true).pth, false, 'file'); this.avail.wikitrk = this.isTrackRevAvail('wiki', trackRev[trk]); - if (panelBio.style.inclTrackRev && trackRev[trk] && trackRev[trk].update) wiki_tr_mod = trackRev[trk].update; + if (bio.panel.style.inclTrackRev && trackRev[trk] && trackRev[trk].update) wiki_tr_mod = trackRev[trk].update; - this.mod.wikiRev = ($Bio.lastModified(wRev) || 0) + (panelBio.summary.show ? ($Bio.lastModified(lRev) || 0) : 0) + (wiki_tr_mod || 0); + this.mod.wikiRev = ($Bio.lastModified(wRev) || 0) + (bio.panel.summary.show ? ($Bio.lastModified(lRev) || 0) : 0) + (wiki_tr_mod || 0); if (this.mod.wikiRev == this.mod.curWikiRev) return; this.rev.wikiAlb = ''; let revLfm = ''; - if (panelBio.style.inclTrackRev != 2 || foundComp) { + if (bio.panel.style.inclTrackRev != 2 || foundComp) { this.rev.wikiAlb = $Bio.open(wRev).replace(/\u200b/g, '').trim(); if (!foundComp) revLfm = $Bio.open(lRev).replace(/\u200b/g, '').trim(); } @@ -2631,12 +2631,12 @@ class Text { this.mod.curWikiRev = this.mod.wikiRev; this.rev.wikiAlb = this.rev.wikiAlb.replace('Genre: ', 'Album Genres: '); const eng = this.rev.wikiAlb.includes('Wikipedia language: EN'); - if (panelBio.style.inclTrackRev != 2 || foundComp) { - if (this.rev.wikiAlb && panelBio.summary.date) { - const lfmDate = tagBio.getTag(revLfm, this.rev.releaseDate, true); + if (bio.panel.style.inclTrackRev != 2 || foundComp) { + if (this.rev.wikiAlb && bio.panel.summary.date) { + const lfmDate = bio.tag.getTag(revLfm, this.rev.releaseDate, true); if (lfmDate.tag) albReleaseDate = lfmDate.label + lfmDate.tag; if (eng) { - const wRevDate = tagBio.getTag(this.rev.wikiAlb, this.rev.releaseDate, true); + const wRevDate = bio.tag.getTag(this.rev.wikiAlb, this.rev.releaseDate, true); if (wRevDate.tag) { albReleaseDate = wRevDate.label + wRevDate.tag; if (lfmDate.tag) { @@ -2650,8 +2650,8 @@ class Text { this.rev.wikiAlb = this.rev.wikiAlb.replace(RegExp($Bio.regexEscape(sub)), ''); } } - if (panelBio.summary.other) { - const length = tagBio.getTag(eng ? this.rev.wikiAlb : revLfm, eng ? 'Length: ' : this.rev.len, true); + if (bio.panel.summary.other) { + const length = bio.tag.getTag(eng ? this.rev.wikiAlb : revLfm, eng ? 'Length: ' : this.rev.len, true); if (length.tag) { albLength = length.label + length.tag; if (eng) { @@ -2664,7 +2664,7 @@ class Text { } } - if ((panelBio.style.inclTrackRev == 1 || pptBio.sourceAll) && pptBio.autoOptimiseText) { + if ((bio.panel.style.inclTrackRev == 1 || bioSet.sourceAll) && bioSet.autoOptimiseText) { const f = this.rev.wikiAlb.indexOf('=='); if (f != -1) { const ix = this.rev.wikiAlb.lastIndexOf('Album Genres: '); @@ -2679,8 +2679,8 @@ class Text { this.rev.wiki = this.rev.wikiAlb; let genrePrefix = 'Track Genre: '; let needTrackSubHeading = false; - if (!pptBio.classicalMusicMode || !foundComp) { - if (panelBio.style.inclTrackRev) { + if (!bioSet.classicalMusicMode || !foundComp) { + if (bio.panel.style.inclTrackRev) { if (trackRev && trackRev[trk]) { const o = trackRev[trk]; let releaseDate = $Bio.getProp(o, 'date', ''); @@ -2696,19 +2696,19 @@ class Text { let label = 'Duration: '; if (en) { length = $Bio.getProp(o, 'length', ''); - } else if (panelBio.summary.other) { // static read - const trackLfmRev = $Bio.jsonParse(panelBio.getPth('track', panelBio.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foLfmRev', true).pth, false, 'file'); + } else if (bio.panel.summary.other) { // static read + const trackLfmRev = $Bio.jsonParse(bio.panel.getPth('track', bio.panel.id.focus, this.trackartist, 'Track Reviews', '', '', $Bio.clean(this.trackartist), '', 'Track Reviews', 'foLfmRev', true).pth, false, 'file'); if (trackLfmRev && trackLfmRev[trk]) { length = $Bio.getProp(trackLfmRev[trk], 'length', ''); if (length) { - const ix = cfg.lang.arr.indexOf(lang); + const ix = bioCfg.lang.arr.indexOf(lang); if (ix != -1) label = this.rev.length[ix]; } } } - if (length) length = (panelBio.summary.other ? label : 'Length: ') + length; + if (length) length = (bio.panel.summary.other ? label : 'Length: ') + length; let wiki = $Bio.getProp(o, 'wiki', ''); - const showGenres = !pptBio.autoOptimiseText || !this.rev.wikiAlb; + const showGenres = !bioSet.autoOptimiseText || !this.rev.wikiAlb; if (showGenres) { let genres = $Bio.getProp(o, 'genre', []); if (genres.length) { @@ -2717,19 +2717,19 @@ class Text { wiki = this.add([genres], wiki); } } - if (pptBio.expandLists) { + if (bioSet.expandLists) { wiki = this.add([wiki], composer); wiki = this.add([wiki], releaseDate); } else { wiki = this.add([wiki], releaseDate); wiki = composer && wiki ? composer + (releaseDate ? '\r\n' : '\r\n\r\n') + wiki : composer || wiki; } - if (this.rev.wikiAlb || (!panelBio.summary.show || !panelBio.summary.other) && length) wiki = this.add([length], wiki); + if (this.rev.wikiAlb || (!bio.panel.summary.show || !bio.panel.summary.other) && length) wiki = this.add([length], wiki); if (wiki) { - if (pptBio.trackHeading == 1 && (this.rev.wikiAlb || !pptBio.heading) || pptBio.trackHeading == 2) { + if (bioSet.trackHeading == 1 && (this.rev.wikiAlb || !bioSet.heading) || bioSet.trackHeading == 2) { this.rev.wikiTrackHeading = false; if (this.rev.wikiAlb) { - trackRev = `!\u00a6${this.tf(pptBio.trackSubHeading, pptBio.artistView, true)}\r\n\r\n${wiki}`; + trackRev = `!\u00a6${this.tf(bioSet.trackSubHeading, bioSet.artistView, true)}\r\n\r\n${wiki}`; } else { trackRev = wiki; needTrackSubHeading = true; @@ -2738,7 +2738,7 @@ class Text { this.rev.wikiTrackHeading = true; trackRev = wiki; } - if (pptBio.sourceAll && pptBio.autoOptimiseText) { + if (bioSet.sourceAll && bioSet.autoOptimiseText) { const f = trackRev.indexOf('=='); if (f != -1) { const ix = trackRev.lastIndexOf('Track Genres: '); @@ -2752,10 +2752,10 @@ class Text { } this.rev.wiki = this.add([trackRev], this.rev.wikiAlb); } else { - this.rev.wikiTrackHeading = panelBio.style.inclTrackRev == 2; + this.rev.wikiTrackHeading = bio.panel.style.inclTrackRev == 2; } } else { - this.rev.wikiTrackHeading = panelBio.style.inclTrackRev == 2; + this.rev.wikiTrackHeading = bio.panel.style.inclTrackRev == 2; } } } else { @@ -2763,8 +2763,8 @@ class Text { } this.rev.wiki = this.rev.wiki.replace(/Wikipedia language:\s[A-Z]{2}/, ''); - this.rev.wiki = this.formatText('wikiRev', this.rev.wiki, panelBio.summary.genre ? { limit: 6, list: true, key: this.rev.wikiAlb ? 'Album Genres: ' : genrePrefix } : {}, panelBio.summary.other && !this.rev.wikiAlb ? { list: true, key: writer, prefix: true, suffix: true } : {}, panelBio.summary.date ? (this.rev.wikiAlb ? (albReleaseDate ? { str: albReleaseDate } : (eng ? { key: this.rev.releaseDate } : '')) : { key: this.rev.releaseDate }) : {}, panelBio.summary.other && !this.rev.wikiAlb ? (length ? { str: length } : {}) : { str: albLength }, '', '', '', checkGenre.singleGenre).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); - if (needTrackSubHeading) this.rev.wiki = `!\u00a6${this.tf(pptBio.trackSubHeading, pptBio.artistView, true)}\r\n\r\n${this.rev.wiki}`; - if (!this.rev.wiki) butBio.check(); + this.rev.wiki = this.formatText('wikiRev', this.rev.wiki, bio.panel.summary.genre ? { limit: 6, list: true, key: this.rev.wikiAlb ? 'Album Genres: ' : genrePrefix } : {}, bio.panel.summary.other && !this.rev.wikiAlb ? { list: true, key: writer, prefix: true, suffix: true } : {}, bio.panel.summary.date ? (this.rev.wikiAlb ? (albReleaseDate ? { str: albReleaseDate } : (eng ? { key: this.rev.releaseDate } : '')) : { key: this.rev.releaseDate }) : {}, bio.panel.summary.other && !this.rev.wikiAlb ? (length ? { str: length } : {}) : { str: albLength }, '', '', '', checkGenre.singleGenre).replace(/(?:\s*\r\n){3,}/g, '\r\n\r\n'); + if (needTrackSubHeading) this.rev.wiki = `!\u00a6${this.tf(bioSet.trackSubHeading, bioSet.artistView, true)}\r\n\r\n${this.rev.wiki}`; + if (!this.rev.wiki) bio.but.check(); } } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-timers.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-timers.js index c7708103..1e3a6fdf 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-timers.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-timers.js @@ -1,6 +1,6 @@ 'use strict'; -class TimersBio { +class BioTimers { constructor() { ['dl', 'img', 'lyrics', 'sim1', 'sim2', 'source', 'transition', 'tt', 'zSearch'].forEach(v => this[v] = { id: null @@ -35,21 +35,21 @@ class TimersBio { if (!$Bio.server) return; this.clear(this.img); this.img.id = setInterval(() => { - imgBio.fresh(); - menBio.fresh(); + bio.img.fresh(); + bio.men.fresh(); window.NotifyOthers('bio_imgChange', 0); }, 1000); } res(force) { window.NotifyOthers('bio_getImg', force); - if ($Bio.server) imgBio.grab(force); + if ($Bio.server) bio.img.grab(force); } tooltip() { this.clear(this.tt); this.tt.id = setTimeout(() => { - txt.deactivateTooltip(); + bio.txt.deactivateTooltip(); this.tt.id = null; }, 5000); } diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-utils.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-utils.js index 0d2c4748..e6cfc10c 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-utils.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-utils.js @@ -1,12 +1,13 @@ 'use strict'; -const my_utilsBio = {}; +/** @global @type {object} */ +const bio_my_utils = {}; -my_utilsBio.scriptInfo = window.ScriptInfo; -// my_utilsBio.packageInfo = utils.GetPackageInfo(my_utilsBio.scriptInfo.PackageId); -my_utilsBio.packagePath = `${basePath}scripts\\biography/`; +bio_my_utils.scriptInfo = window.ScriptInfo; +// bio_my_utils.packageInfo = utils.GetPackageInfo(bio_my_utils.scriptInfo.PackageId); +bio_my_utils.packagePath = `${grPath.base}scripts\\biography\\`; -my_utilsBio.getAsset = assetFile => utils.ReadTextFile(`${basePath}scripts\\biography\\assets/${assetFile}`); -my_utilsBio.getImageAsset = assetFile => gdi.Image(`${basePath}scripts\\biography\\assets\\images/${assetFile}`); -my_utilsBio.getFlagAsset = assetFile => gdi.Image(`${basePath}scripts\\biography\\assets\\images\\flags/${assetFile}`); -my_utilsBio.getScriptPath = `${basePath}scripts\\biography\\scripts/`; +bio_my_utils.getAsset = assetFile => utils.ReadTextFile(`${grPath.base}scripts\\biography\\assets\\${assetFile}`); +bio_my_utils.getImageAsset = assetFile => gdi.Image(`${grPath.base}scripts\\biography\\assets\\images\\${assetFile}`); +bio_my_utils.getFlagAsset = assetFile => gdi.Image(`${grPath.base}scripts\\biography\\assets\\images\\flags\\${assetFile}`); +bio_my_utils.getScriptPath = `${grPath.base}scripts\\biography\\scripts\\`; diff --git a/profile/georgia-reborn/scripts/Biography/scripts/bio-wikipedia.js b/profile/georgia-reborn/scripts/Biography/scripts/bio-wikipedia.js index faa7cad7..068fb838 100644 --- a/profile/georgia-reborn/scripts/Biography/scripts/bio-wikipedia.js +++ b/profile/georgia-reborn/scripts/Biography/scripts/bio-wikipedia.js @@ -1,6 +1,6 @@ 'use strict'; -class DldWikipedia { +class BioDldWikipedia { constructor(state_callback) { this.alias = ''; this.alwaysCheckArtistInWiki = true; @@ -29,7 +29,7 @@ class DldWikipedia { this.rg_mbid = ''; this.artistWorksChecked = false; this.searchItem = 0; - this.site = cfg.language.toLowerCase(); + this.site = bioCfg.language.toLowerCase(); this.tagChecked = false; this.timer = null; this.title; @@ -95,10 +95,10 @@ class DldWikipedia { } else { this.ar_mbid = $Bio.eval('$trim($if3(%musicbrainz_artistid%,%musicbrainz artist id%,))', this.focus); if (!this.ar_mbid || this.ar_mbid.length != 36) { - const related_artists = $Bio.jsonParse(`${cfg.storageFolder.replace('{BA9557CE-7B4B-4E0E-9373-99F511E81252}', '{F5E9D9EB-42AD-4A47-B8EE-C9877A8E7851}')}related_artists.json`, {}, 'file'); + const related_artists = $Bio.jsonParse(`${bioCfg.storageFolder.replace('{BA9557CE-7B4B-4E0E-9373-99F511E81252}', '{F5E9D9EB-42AD-4A47-B8EE-C9877A8E7851}')}related_artists.json`, {}, 'file'); this.ar_mbid = related_artists[this.artist.toUpperCase()]; // f&p says if it's a tag read } - if ((!this.ar_mbid || this.ar_mbid.length != 36) && !this.force) this.ar_mbid = serverBio.artistMbid[this.artist]; + if ((!this.ar_mbid || this.ar_mbid.length != 36) && !this.force) this.ar_mbid = bio.server.artistMbid[this.artist]; if (!this.ar_mbid || this.ar_mbid.length != 36) this.ar_mbid = ''; this.tagChecked = true; } @@ -112,23 +112,23 @@ class DldWikipedia { if (this.ar_mbid) return this.search(1); switch (this.aridType) { case 0: - URL = `${serverBio.url.mb}recording/?query=${encodeURIComponent($Bio.regexEscape(this.recording.toLowerCase()))} AND artist:${encodeURIComponent($Bio.regexEscape(this.artist.toLowerCase()))}&fmt=json`; + URL = `${bio.server.url.mb}recording/?query=${encodeURIComponent($Bio.regexEscape(this.recording.toLowerCase()))} AND artist:${encodeURIComponent($Bio.regexEscape(this.artist.toLowerCase()))}&fmt=json`; break; case 1: - URL = `${serverBio.url.mb}release-group/?query=${encodeURIComponent($Bio.regexEscape(this.album.toLowerCase()))} AND artist:${encodeURIComponent($Bio.regexEscape(this.artist.toLowerCase()))} AND (primarytype:Album OR primarytype:EP)&fmt=json`; + URL = `${bio.server.url.mb}release-group/?query=${encodeURIComponent($Bio.regexEscape(this.album.toLowerCase()))} AND artist:${encodeURIComponent($Bio.regexEscape(this.artist.toLowerCase()))} AND (primarytype:Album OR primarytype:EP)&fmt=json`; break; case 2: - URL = `${serverBio.url.mb}artist/?query=${encodeURIComponent($Bio.regexEscape(this.artist.toLowerCase()))}&fmt=json`; + URL = `${bio.server.url.mb}artist/?query=${encodeURIComponent($Bio.regexEscape(this.artist.toLowerCase()))}&fmt=json`; break; } break; case 1: if (this.type && !this.force) { - this.wikidataArtist = $Bio.getProp(serverBio.artistQid, `${this.artist}.code`, ''); - this.alias = $Bio.getProp(serverBio.artistQid, `${this.artist}.alias`, ''); + this.wikidataArtist = $Bio.getProp(bio.server.artistQid, `${this.artist}.code`, ''); + this.alias = $Bio.getProp(bio.server.artistQid, `${this.artist}.alias`, ''); } if (!this.type || !this.wikidataArtist) { - URL = `${serverBio.url.mb}artist/${this.ar_mbid}?inc=genres+url-rels&fmt=json`; + URL = `${bio.server.url.mb}artist/${this.ar_mbid}?inc=genres+url-rels&fmt=json`; } if (this.type && this.wikidataArtist) { return this.search(2); @@ -140,38 +140,38 @@ class DldWikipedia { } const params = this.type == 1 || this.type == 3 ? ` AND arid:${this.ar_mbid} AND ${this.type == 1 ? '(primarytype:Album OR primarytype:EP)' : 'primarytype:Single'}` : ''; const type = this.type == 2 || this.type == 4 ? 'work' : 'release-group'; - URL = `${serverBio.url.mb + type}?query=${encodeURIComponent($Bio.regexEscape(this.title.toLowerCase()) + params)}&fmt=json`; + URL = `${bio.server.url.mb + type}?query=${encodeURIComponent($Bio.regexEscape(this.title.toLowerCase()) + params)}&fmt=json`; break; } case 3: - URL = `${serverBio.url.mb + (this.type == 2 || this.type == 4 ? 'work/' : 'release-group/') + this.rg_mbid}?inc=genres+url-rels&fmt=json`; + URL = `${bio.server.url.mb + (this.type == 2 || this.type == 4 ? 'work/' : 'release-group/') + this.rg_mbid}?inc=genres+url-rels&fmt=json`; break; case 4: { - let lang = cfg.language == 'EN' ? 'enwiki' : `${cfg.language.toLowerCase()}wiki`; - if (serverBio.langFallback) lang += '|enwiki'; - URL = `${serverBio.url.wikidata + this.wikidata}&sitefilter=${lang}`; + let lang = bioCfg.language == 'EN' ? 'enwiki' : `${bioCfg.language.toLowerCase()}wiki`; + if (bio.server.langFallback) lang += '|enwiki'; + URL = `${bio.server.url.wikidata + this.wikidata}&sitefilter=${lang}`; break; } case 5: - URL = serverBio.url.wikipedia.replace('//lang', `//${this.site}`) + this.wikititle; // encodeURIComponent broke Y Viva España + URL = bio.server.url.wikipedia.replace('//lang', `//${this.site}`) + this.wikititle; // encodeURIComponent broke Y Viva España break; case 6: { this.artistValidated = true; setTimeout(() => { - URL = `${serverBio.url.mb}work?artist=${this.ar_mbid}&limit=100&offset=${this.offset}&fmt=json`; + URL = `${bio.server.url.mb}work?artist=${this.ar_mbid}&limit=100&offset=${this.offset}&fmt=json`; this.get(URL, this.force); }, 1200); break; } case 7: setTimeout(() => { - URL = serverBio.url.wikisearch + encodeURIComponent(this.title + (this.type == 1 ? ` ${this.artist}` : '')); + URL = bio.server.url.wikisearch + encodeURIComponent(this.title + (this.type == 1 ? ` ${this.artist}` : '')); this.get(URL, this.force); }, 1200); break; case 8: { - const site = this.site == 'en' || cfg.wikipediaEnGenres ? 'en' : this.site; - URL = serverBio.url.wikiinfo.replace('//lang', `//${site}`) + this.wikititle; + const site = this.site == 'en' || bioCfg.wikipediaEnGenres ? 'en' : this.site; + URL = bio.server.url.wikiinfo.replace('//lang', `//${site}`) + this.wikititle; break; } } @@ -190,7 +190,7 @@ class DldWikipedia { } const list = []; data.forEach(v => { - const i = serverBio.match(this.artist, !this.aridType ? this.recording : this.album, [{ artist: this.artist, title: v.title }], this.type != 2 ? (!this.aridType ? 'song' : 'album') : 'composition', true, 80); + const i = bio.server.match(this.artist, !this.aridType ? this.recording : this.album, [{ artist: this.artist, title: v.title }], this.type != 2 ? (!this.aridType ? 'song' : 'album') : 'composition', true, 80); if (i != -1) list.push(v); }); const artist = $Bio.strip(this.artist); @@ -198,7 +198,7 @@ class DldWikipedia { if (this.ar_mbid) return true; v['artist-credit'].some(w => { if (artist == $Bio.strip(w.artist.name)) { - serverBio.artistMbid[this.artist] = this.ar_mbid = w.artist.id; + bio.server.artistMbid[this.artist] = this.ar_mbid = w.artist.id; return true; } }); @@ -211,22 +211,22 @@ class DldWikipedia { w.artist.aliases.some(u => { if (artist == $Bio.strip(u.name)) { this.alias = u.name; - serverBio.artistMbid[this.artist] = this.ar_mbid = w.artist.id; + bio.server.artistMbid[this.artist] = this.ar_mbid = w.artist.id; return true; } }); } }); }); - if (!this.ar_mbid && cfg.partialMatchEnabled) { - const alias = serverBio.tidy(this.artist, true); + if (!this.ar_mbid && bioCfg.partialMatchEnabled) { + const alias = bio.server.tidy(this.artist, true); if (alias) { list.some(v => { if (this.ar_mbid) return true; v['artist-credit'].some(w => { - if (alias == serverBio.tidy(w.artist.name, true)) { + if (alias == bio.server.tidy(w.artist.name, true)) { this.alias = w.artist.name; - serverBio.artistMbid[this.artist] = this.ar_mbid = w.artist.id; + bio.server.artistMbid[this.artist] = this.ar_mbid = w.artist.id; return true; } }); @@ -246,7 +246,7 @@ class DldWikipedia { const aliases = []; let items = []; items = data.filter(v => artist == $Bio.strip(v.name)); - if (items.length == 1 || this.type == 2) serverBio.artistMbid[this.artist] = this.ar_mbid = items[0].id; + if (items.length == 1 || this.type == 2) bio.server.artistMbid[this.artist] = this.ar_mbid = items[0].id; if (!items.length) { data.forEach(v => { if (v.aliases) { @@ -258,7 +258,7 @@ class DldWikipedia { } }); if (aliases.length == 1 || this.type == 2) { - serverBio.artistMbid[this.artist] = this.ar_mbid = aliases[0].id; + bio.server.artistMbid[this.artist] = this.ar_mbid = aliases[0].id; this.alias = aliases[0].name; } } @@ -305,7 +305,7 @@ class DldWikipedia { }); } this.wikidataArtist = this.wikidata = this.wikidata.split('/').pop(); - serverBio.artistQid[this.artist] = { + bio.server.artistQid[this.artist] = { alias: this.alias, code: this.wikidata }; @@ -313,7 +313,7 @@ class DldWikipedia { return this.search(!this.type ? 4 : 2); } let wikidata = ''; - if (!this.type && cfg.language == 'EN') { + if (!this.type && bioCfg.language == 'EN') { data.some(v => { if (v.type == 'wikipedia' && v.url.resource && v.url.resource.includes('//en.') && !v.url.resource.includes('disambiguation')) return wikidata = v.url.resource; }); @@ -348,11 +348,11 @@ class DldWikipedia { $Bio.sort(list, 'type'); let i = -1; if (this.type == 2 || this.type == 4) { - i = serverBio.match(this.artist, this.title, filteredList, ['', 'review', 'composition', 'song', 'song'][this.type], true); + i = bio.server.match(this.artist, this.title, filteredList, ['', 'review', 'composition', 'song', 'song'][this.type], true); if (i != -1) this.rg_mbid = filteredList[i].rg_mbid; } if (i == -1) { - i = serverBio.match(this.artist, this.title, list, ['', 'review', 'composition', 'song', 'song'][this.type], true); + i = bio.server.match(this.artist, this.title, list, ['', 'review', 'composition', 'song', 'song'][this.type], true); if (i != -1) this.rg_mbid = list[i].rg_mbid; } if (i == -1) { @@ -395,7 +395,7 @@ class DldWikipedia { } this.wikidata = ''; this.wikititle = ''; - if (cfg.language == 'EN') { + if (bioCfg.language == 'EN') { data.some(v => { if (v.type == 'wikipedia' && v.url.resource && v.url.resource.includes('//en.') && !v.url.resource.includes('disambiguation')) return this.wikidata = v.url.resource; }); @@ -420,17 +420,17 @@ class DldWikipedia { case 4: { // parseWikidata const data = $Bio.jsonParse(this.xmlhttp.responseText, {}); if (this.alwaysCheckArtistInWiki && this.type || !this.artistValidated && (this.type == 2 || this.type == 4 || this.fallBackDone)) this.artistValidated = this.wikidataArtist ? this.xmlhttp.responseText.includes(this.wikidataArtist) : false; - const wikititles = serverBio.getObjKeyValue(data, 'title'); - const wikiurls = serverBio.getObjKeyValue(data, 'url'); + const wikititles = bio.server.getObjKeyValue(data, 'title'); + const wikiurls = bio.server.getObjKeyValue(data, 'url'); this.wikititle = ''; wikiurls.some((v, i) => { - if (v.includes(`${cfg.language.toLowerCase()}.wikipedia`) && !v.includes('disambiguation')) { - this.site = cfg.language.toLowerCase(); + if (v.includes(`${bioCfg.language.toLowerCase()}.wikipedia`) && !v.includes('disambiguation')) { + this.site = bioCfg.language.toLowerCase(); this.wikititle = encodeURIComponent(wikititles[i]); return true; } }); - if (!this.wikititle && serverBio.langFallback) { + if (!this.wikititle && bio.server.langFallback) { wikiurls.some((v, i) => { if (v.includes('en.wikipedia') && !v.includes('disambiguation')) { this.site = 'en'; @@ -456,7 +456,7 @@ class DldWikipedia { } case 5: { // parseWikipediaResponse const data = $Bio.jsonParse(this.xmlhttp.responseText, {}); - this.wiki = serverBio.getObjKeyValue(data, 'extract')[0] || ''; + this.wiki = bio.server.getObjKeyValue(data, 'extract')[0] || ''; const needArtistCheck = this.alwaysCheckArtistInWiki && this.type || this.type == 2 || this.type == 4 || this.fallBackDone; if (!this.artistValidated && needArtistCheck) { @@ -482,7 +482,7 @@ class DldWikipedia { response.forEach(v => { list.push({ artist: this.artist, rg_mbid: v.id, title: v.title }); }); - const i = serverBio.match(this.artist, this.title, list, ['', 'review', 'composition', 'song', 'song'][this.type], true); + const i = bio.server.match(this.artist, this.title, list, ['', 'review', 'composition', 'song', 'song'][this.type], true); if (i == -1) { this.offset += 100; if (this.releases > this.offset) { @@ -511,7 +511,7 @@ class DldWikipedia { this.wikidataFirst = v.title; } }); - const i = serverBio.match(this.artist, this.title, list, ['', 'review', 'composition', 'song', 'song'][this.type], true); + const i = bio.server.match(this.artist, this.title, list, ['', 'review', 'composition', 'song', 'song'][this.type], true); if (i != -1) { this.wikidata = list[i].id; this.artistValidated = true; @@ -527,9 +527,9 @@ class DldWikipedia { const json = $Bio.jsonParse(this.xmlhttp.responseText, {}, 'get', 'query.pages'); const key = Object.keys(json); if (!key.length) { this.save(); break; } - const source = infoboxBio.cleanText($Bio.getProp(json, `${key[0]}.revisions.0.slots.main.*`, ''), this.site); + const source = bio.infobox.cleanText($Bio.getProp(json, `${key[0]}.revisions.0.slots.main.*`, ''), this.site); if (!source || /#REDIRECT/i.test(source)) { this.save(); break; } - this.info = infoboxBio.getValues(this.type, source, this.site); + this.info = bio.infobox.getValues(this.type, source, this.site); this.save(); break; } @@ -588,7 +588,7 @@ class DldWikipedia { get(URL, force) { this.func = this.analyse; - if (pptBio.multiServer && !force && serverBio.urlDone(md5Bio.hashStr(this.artist + this.title + this.type + this.pth + cfg.partialMatch + URL))) return; + if (bioSet.multiServer && !force && bio.server.urlDone(bioMD5.hashStr(this.artist + this.title + this.type + this.pth + bioCfg.partialMatch + URL))) return; this.xmlhttp.open('GET', URL); this.xmlhttp.onreadystatechange = this.ready_callback; this.xmlhttp.setRequestHeader('User-Agent', 'foobar2000_yttm (https://hydrogenaud.io/index.php/topic,111059.0.html)'); @@ -646,14 +646,14 @@ class DldWikipedia { } if (this.type == 0) { - this.wiki = txt.add([this.info.active, this.info.start, this.info.bornIn, this.info.end, this.info.foundedIn], this.wiki); - const value = $Bio.jsonParse(txt.countryCodes, {}, 'file')[this.artist.toLowerCase()]; + this.wiki = bio.txt.add([this.info.active, this.info.start, this.info.bornIn, this.info.end, this.info.foundedIn], this.wiki); + const value = $Bio.jsonParse(bio.txt.countryCodes, {}, 'file')[this.artist.toLowerCase()]; if (!value) { let countryCode = ''; let locale = this.info.bornIn || this.info.foundedIn; if (locale) { locale = locale.split(','); - countryCode = countryToCode[$Bio.strip(locale[locale.length - 1])]; + countryCode = bioCountryToCode[$Bio.strip(locale[locale.length - 1])]; this.saveCountryCode(countryCode); } } @@ -661,16 +661,16 @@ class DldWikipedia { this.checkTypeOf(); - if (this.site != 'en' && !cfg.wikipediaEnGenres) this.genres = this.type < 3 ? '' : []; // no fallback to mb genres if !en + if (this.site != 'en' && !bioCfg.wikipediaEnGenres) this.genres = this.type < 3 ? '' : []; // no fallback to mb genres if !en const genres = this.info.genre.length ? this.info.genre : this.genres.length ? this.genres : this.type < 3 ? '' : []; - if (genres && this.type < 3) this.wiki = txt.add([`Genre: ${genres}`], this.wiki); + if (genres && this.type < 3) this.wiki = bio.txt.add([`Genre: ${genres}`], this.wiki); if (this.type == 1) { - this.wiki = txt.add([this.info.released], this.wiki); + this.wiki = bio.txt.add([this.info.released], this.wiki); } if (this.type > 0 && this.type < 3) { - this.wiki = txt.add([this.info.length], this.wiki); + this.wiki = bio.txt.add([this.info.length], this.wiki); } this.wiki = this.wiki.trim(); @@ -682,9 +682,9 @@ class DldWikipedia { if (this.fo) { $Bio.buildPth(this.fo); - this.wiki = txt.add([`Wikipedia language: ${this.site.toUpperCase()}`], this.wiki); + this.wiki = bio.txt.add([`Wikipedia language: ${this.site.toUpperCase()}`], this.wiki); $Bio.save(this.pth, this.wiki, true); - serverBio.res(); + bio.server.res(); } } else { const text = $Bio.jsonParse(this.pth, {}, 'file'); @@ -706,7 +706,7 @@ class DldWikipedia { }; $Bio.save(this.pth, JSON.stringify($Bio.sortKeys(text), null, 3), true); } - if (genres.length || this.info.composer.length || this.info.released || this.info.length || this.wiki) serverBio.res(); + if (genres.length || this.info.composer.length || this.info.released || this.info.length || this.wiki) bio.server.res(); else $Bio.trace(`wikipedia: ${this.name}: not found`, true); } } @@ -714,12 +714,12 @@ class DldWikipedia { saveCountryCode(code, force) { if (!code) return; const a = this.artist.toLowerCase(); - const m = $Bio.jsonParse(txt.countryCodes, {}, 'file'); + const m = $Bio.jsonParse(bio.txt.countryCodes, {}, 'file'); const value = m[a]; if (code == value && !force) return; m[a] = code; - $Bio.save(txt.countryCodes, JSON.stringify($Bio.sortKeys(m), null, 3), true); - serverBio.res(); + $Bio.save(bio.txt.countryCodes, JSON.stringify($Bio.sortKeys(m), null, 3), true); + bio.server.res(); } tidyWiki(n, en) { @@ -731,7 +731,7 @@ class DldWikipedia { } } -class InfoboxBio { +class BioInfobox { constructor() { this.date = new Intl.DateTimeFormat('en-GB', { year: 'numeric', month: 'long', day: 'numeric' }); this.mm_yyyy = new Intl.DateTimeFormat('en-GB', { year: 'numeric', month: 'long' }); diff --git a/profile/georgia-reborn/scripts/Library/assets/html/config.html b/profile/georgia-reborn/scripts/Library/assets/html/config.html index 34f4b56e..48b2c84c 100644 --- a/profile/georgia-reborn/scripts/Library/assets/html/config.html +++ b/profile/georgia-reborn/scripts/Library/assets/html/config.html @@ -650,7 +650,7 @@ var needCustomCol = /colour_container/.test(id); if (needCustomCol) setCustomColours('value'); - document.getElementById("btn_apply").disabled = JSON.stringify(cfg) != parsed_args[0] || JSON.stringify(ppt) != parsed_args[1] ? false : true; + document.getElementById("btn_apply").disabled = JSON.stringify(cfg) != parsed_args[0] || JSON.stringify(libSet) != parsed_args[1] ? false : true; } function set_btn_del_state() { @@ -738,7 +738,7 @@ var new_id = 100; var parsed_args; var props_callback - var ppt = {}; + var libSet = {}; var sizes = []; try { @@ -750,7 +750,7 @@ cur = JSON.parse(parsed_args[2]); document.title = 'Library Tree ' + cur.version + ': Panel Settings'; cfg = JSON.parse(parsed_args[0]); - ppt = JSON.parse(parsed_args[1]); + libSet = JSON.parse(parsed_args[1]); cbox_h = Math.round(Math.max(em, 13)); cbox_w = cbox_h + 2; var cbox = "width:" + cbox_w + "px; height:" + cbox_h + "px;" @@ -774,12 +774,12 @@ var albumArtOverlayLabels = ['None', 'Track Count', 'Year']; var altClickLabels = ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (alt dblclk removes)']; var barLabels = ['Search', 'Filter Button', 'Settings button*']; - var clickLabels = ppt['actionMode_internal'].value == 1 ? ['Send to playlist'] : ppt['actionMode_internal'].value == 2 ? ['Select'] : ['Select', 'Send to playlist', 'Send to playlist & play', 'Send to playlist & play [add if playing]']; + var clickLabels = libSet['actionMode_internal'].value == 1 ? ['Send to playlist'] : libSet['actionMode_internal'].value == 2 ? ['Select'] : ['Select', 'Send to playlist', 'Send to playlist & play', 'Send to playlist & play [add if playing]']; var colourLabels = ['Text', 'Text highlight', 'Text selection', 'Nowplaying text / sidemarker highlight', 'Text search', 'Buttons', 'Background', 'Background accent', 'Background selection', 'Frame hover', 'Frame selection', 'Counts & statistics', 'Node collapse', 'Node expand', 'Node hover', 'Node lines', 'Separators', 'Side marker', 'Transparent fill']; var coverFormatLabels = ['Cover auto-fill', 'Cover opacity (%)']; var cusNodeLabels = ['Custom icon expand|collapse', 'Custom icon font name', 'Custom icon padding (+/-)', 'Square nodes: use windows']; var cusScrollLabels = ['Step (0 = page)', 'Smooth duration max (ms)', 'Touch duration max (ms)', 'Touch sensitivity 0-10']; - var dblClickLabels = ppt['actionMode_internal'].value == 1 ? ['Send to playlist & play'] : ppt['actionMode_internal'].value == 2 ? ['Send to playback queue and play'] : ['Send to playlist', 'Send to playlist & play', 'Expand / collapse (tree)', 'Send to playback queue and play']; + var dblClickLabels = libSet['actionMode_internal'].value == 1 ? ['Send to playlist & play'] : libSet['actionMode_internal'].value == 2 ? ['Send to playback queue and play'] : ['Send to playlist', 'Send to playlist & play', 'Expand / collapse (tree)', 'Send to playback queue and play']; var diskCacheLabels = ['Enable', 'Preload cached images'] var followCurrentLabels = ['Standard mode', 'Album art flow mode']; var fontLabels = ['Main', 'Album art group line 1', 'Album art group line 2', 'Album art statistics line']; @@ -788,7 +788,7 @@ var keystrokeLabels = ['Select', 'Send to playlist'] var itemCountsLabels = ['Hide', '# Tracks', '# Items', 'Align right (tree / text)']; var metricsLabels = ['Margin', 'Tree indent', 'Line padding tree', 'Line padding album art', 'Margin overide top/bottom (no top bar)', 'Side marker width (0 = auto)']; - var mbtnClickLabels = ppt['actionMode_internal'].value == 2 ? ['Add to playback queue (middle dblclk removes)'] : ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (middle dblclk removes)']; + var mbtnClickLabels = libSet['actionMode_internal'].value == 2 ? ['Add to playback queue (middle dblclk removes)'] : ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (middle dblclk removes)']; var nodeLabels = ['Squares', 'Arrows', 'Arrows Light', 'Triangles', 'Triangles Light', 'Signs', 'Custom']; var playlistLabels = ['Default', 'Activate when changed']; var playlistSortLabels = ['Playlist sort order (empty = default)']; @@ -806,7 +806,7 @@ var statisticsLabels = ['Rating', 'Popularity', 'Date', '', 'Auto show labels', 'Auto show tooltips']; var targetPlaylistLabels = ['Default', 'Current']; var tfInput = ['Library View']; - var themeLabels = !ppt['themed_internal'].value ? ['User interface', 'Dark', 'Blend', 'Light', 'Random', 'Cover', 'Swap colours', 'Panel source: use received item image'] : ['N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set']; + var themeLabels = !libSet['themed_internal'].value ? ['User interface', 'Dark', 'Blend', 'Light', 'Random', 'Cover', 'Swap colours', 'Panel source: use received item image'] : ['N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set']; var themeBlurLabels = ['Auto-fill', 'Level (%)', 'Opacity (%)', '%']; var thumbNailGapLabels = ['Thumbnail gap']; var thumbnailSizeLabels = ['Mini', 'Small', 'Regular', 'Medium', 'Large', 'XL', 'XXL', 'Max']; @@ -815,8 +815,8 @@ var modeLabels = ['Max height', 'Min height', 'Limit menu expand: 10-6000']; function setNodeLabels() { - nodeLabels[0] = !ppt['winNode_internal'].value ? 'Squares' : 'Squares (windows)'; - if (ppt['nodeStyle_internal'].value == 0 || ppt['nodeStyle_internal'].value == 7) window.node_dropdown.innerHTML = nodeLabels[0]; + nodeLabels[0] = !libSet['winNode_internal'].value ? 'Squares' : 'Squares (windows)'; + if (libSet['nodeStyle_internal'].value == 0 || libSet['nodeStyle_internal'].value == 7) window.node_dropdown.innerHTML = nodeLabels[0]; window.nodeNo0.innerHTML = nodeLabels[0]; } @@ -870,13 +870,13 @@ function setSbarShow(n) { window.sbarShow_dropdown.innerHTML = sbarShowLabels[n]; - ppt['sbarShow_internal'].value = n; + libSet['sbarShow_internal'].value = n; window.sbarShow.style.display = 'none'; } function setsbarType(n) { window.sbarType_dropdown.innerHTML = sbarTypeLabels[n]; - ppt['sbarType_internal'].value = n; + libSet['sbarType_internal'].value = n; sbar_col_dropdown.style.display = n != 2 ? "inline-block" : "none"; sbar_icon_dropdown.style.display = n != 2 ? "inline-block" : "none"; window.sbarType.style.display = 'none'; @@ -884,113 +884,113 @@ function setSbarCol(n) { window.sbarCol_dropdown.innerHTML = sbarColLabels[n]; - ppt['sbarCol_internal'].value = n; + libSet['sbarCol_internal'].value = n; window.sbarCol.style.display = 'none'; } function setSbarIcon(n) { window.sbarIcon_dropdown.innerHTML = sbarIconLabels[n]; - ppt['sbarButType_internal'].value = n; + libSet['sbarButType_internal'].value = n; window.sbarIcon.style.display = 'none'; } function setRootImage(n) { window.rootStub_dropdown.innerHTML = 'Root: '+rootStubLabels[n].name+'<'; - ppt.curRootImg_internal.value = n; + libSet.curRootImg_internal.value = n; btn_apply.onclick(); window.rootStub.style.display = 'none'; } function setNoArtistImage(n) { window.noArtistStub_dropdown.innerHTML = 'No artist: '+noArtistStubLabels[n].name+'<'; - ppt.curNoArtistImg_internal.value = n; + libSet.curNoArtistImg_internal.value = n; btn_apply.onclick(); window.noArtistStub.style.display = 'none'; } function setNoCoverImage(n) { window.noCoverStub_dropdown.innerHTML = 'No cover: '+noCoverStubLabels[n].name+'<'; - ppt.curNoCoverImg_internal.value = n; + libSet.curNoCoverImg_internal.value = n; btn_apply.onclick(); window.noCoverStub.style.display = 'none'; } function setRoot(n) { window.root_dropdown.innerHTML = rootLabels[n]; - ppt['rootNode_internal'].value = n; + libSet['rootNode_internal'].value = n; window.root.style.display = 'none'; } function setNode(n) { setNodeLabels(); window.node_dropdown.innerHTML = nodeLabels[n]; - ppt['nodeStyle_internal'].value = n; + libSet['nodeStyle_internal'].value = n; window.node.style.display = 'none'; } function setItemCounts(n) { window.itemCounts_dropdown.innerHTML = itemCountsLabels[n]; - ppt['nodeCounts_internal'].value = n; + libSet['nodeCounts_internal'].value = n; window.itemCounts.style.display = 'none'; } function setHoverBg(n) { window.showHl_dropdown.innerHTML = highLightLabels[n]; - ppt['highLightRow_internal'].value = n; + libSet['highLightRow_internal'].value = n; window.showHl.style.display = 'none'; } function setOverlay(n) { window.overlay_dropdown.innerHTML = albumArtOverlayLabels[n]; - ppt['itemOverlayType_internal'].value = n; + libSet['itemOverlayType_internal'].value = n; window.overlay.style.display = 'none'; } function setSearch(n) { window.search_dropdown.innerHTML = searchLabels[n]; - ppt['searchSend_internal'].value = n; + libSet['searchSend_internal'].value = n; window.search.style.display = 'none'; } function setTheme(n) { window.theme_dropdown.innerHTML = themeLabels[n]; - ppt['theme_internal'].value = n; + libSet['theme_internal'].value = n; window.themeBg.style.display = 'none'; } function setthumbSize(n) { window.thumbSize_dropdown.innerHTML = thumbnailSizeLabels[n]; - ppt['thumbNailSize_internal'].value = n; + libSet['thumbNailSize_internal'].value = n; window.thumbSize.style.display = 'none'; } function setClickAction(n) { window.click_dropdown.innerHTML = clickLabels[n]; - ppt['clickAction_internal'].value = n; + libSet['clickAction_internal'].value = n; window.click.style.display = 'none'; } function setDblClickAction(n) { window.dblClick_dropdown.innerHTML = dblClickLabels[n]; - ppt['dblClickAction_internal'].value = n; + libSet['dblClickAction_internal'].value = n; window.dblClick.style.display = 'none'; } function setMbtnClickAction(n) { window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[n]; - ppt['mbtnClickAction_internal'].value = n; + libSet['mbtnClickAction_internal'].value = n; window.mbtnClick.style.display = 'none'; } function setAltClickAction(n) { window.altClick_dropdown.innerHTML = altClickLabels[n]; - ppt['altClickAction_internal'].value = n; + libSet['altClickAction_internal'].value = n; window.altClick.style.display = 'none'; } function setKeyAction(n) { window.keystroke_dropdown.innerHTML = keystrokeLabels[n]; - ppt['keyAction_internal'].value = n; + libSet['keyAction_internal'].value = n; window.keystroke.style.display = 'none'; } @@ -1117,7 +1117,7 @@ function setBehaviour() { for (i = 0; i < 3; i++) {window.actionType.insertAdjacentHTML('beforeend', '
');} - if (ppt['actionMode_internal'].value) { + if (libSet['actionMode_internal'].value) { window.click_dropdown.innerHTML = clickLabels[0]; window.click.insertAdjacentHTML('beforeend', 'PRESET'); window.click_dropdown.style.background = 'rgb(244, 67, 54)'; @@ -1125,40 +1125,40 @@ window.dblClick.insertAdjacentHTML('beforeend', 'PRESET'); window.dblClick_dropdown.style.background = 'rgb(244, 67, 54)'; } else { - window.click_dropdown.innerHTML = clickLabels[ppt['clickAction_internal'].value]; + window.click_dropdown.innerHTML = clickLabels[libSet['clickAction_internal'].value]; for (i = 0; i < 4; i++) {window.click.insertAdjacentHTML('beforeend', ''+clickLabels[i]+'');} - window.dblClick_dropdown.innerHTML = dblClickLabels[ppt['dblClickAction_internal'].value]; + window.dblClick_dropdown.innerHTML = dblClickLabels[libSet['dblClickAction_internal'].value]; for (i = 0; i < 4; i++) {window.dblClick.insertAdjacentHTML('beforeend', ''+dblClickLabels[i]+'');} } - if (ppt['actionMode_internal'].value == 2) { + if (libSet['actionMode_internal'].value == 2) { window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[0]; window.mbtnClick.insertAdjacentHTML('beforeend', 'PRESET'); window.mbtnClick_dropdown.style.background = 'rgb(244, 67, 54)'; } else { - window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[ppt['mbtnClickAction_internal'].value]; + window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[libSet['mbtnClickAction_internal'].value]; for (i = 0; i < 3; i++) {window.mbtnClick.insertAdjacentHTML('beforeend', ''+mbtnClickLabels[i]+'');} } - window.altClick_dropdown.innerHTML = altClickLabels[ppt['altClickAction_internal'].value]; + window.altClick_dropdown.innerHTML = altClickLabels[libSet['altClickAction_internal'].value]; for (i = 0; i < 3; i++) {window.altClick.insertAdjacentHTML('beforeend', ''+altClickLabels[i]+'');} - window.keystroke_dropdown.innerHTML = keystrokeLabels[ppt['keyAction_internal'].value]; + window.keystroke_dropdown.innerHTML = keystrokeLabels[libSet['keyAction_internal'].value]; for (i = 0; i < 2; i++) {window.keystroke.insertAdjacentHTML('beforeend', ''+keystrokeLabels[i]+'');} - window.sbarShow_dropdown.innerHTML = sbarShowLabels[ppt['sbarShow_internal'].value]; + window.sbarShow_dropdown.innerHTML = sbarShowLabels[libSet['sbarShow_internal'].value]; for (i = 0; i < 3; i++) {window.sbarShow.insertAdjacentHTML('beforeend', ''+sbarShowLabels[i]+'');} - window.sbarType_dropdown.innerHTML = sbarTypeLabels[ppt['sbarType_internal'].value]; + window.sbarType_dropdown.innerHTML = sbarTypeLabels[libSet['sbarType_internal'].value]; for (i = 0; i < 3; i++) {window.sbarType.insertAdjacentHTML('beforeend', ''+sbarTypeLabels[i]+'');} - window.sbarCol_dropdown.innerHTML = sbarColLabels[ppt['sbarCol_internal'].value]; + window.sbarCol_dropdown.innerHTML = sbarColLabels[libSet['sbarCol_internal'].value]; for (i = 0; i < 2; i++) {window.sbarCol.insertAdjacentHTML('beforeend', ''+sbarColLabels[i]+'');} - sbar_col_dropdown.style.display = ppt['sbarType_internal'].value != 2 ? "inline-block" : "none"; - window.sbarIcon_dropdown.innerHTML = sbarIconLabels[ppt['sbarButType_internal'].value]; + sbar_col_dropdown.style.display = libSet['sbarType_internal'].value != 2 ? "inline-block" : "none"; + window.sbarIcon_dropdown.innerHTML = sbarIconLabels[libSet['sbarButType_internal'].value]; for (i = 0; i < 3; i++) {window.sbarIcon.insertAdjacentHTML('beforeend', ''+sbarIconLabels[i]+'');} - sbar_icon_dropdown.style.display = ppt['sbarType_internal'].value != 2 ? "inline-block" : "none"; + sbar_icon_dropdown.style.display = libSet['sbarType_internal'].value != 2 ? "inline-block" : "none"; for (i = 0; i < 3; i++) {window.scrolling.insertAdjacentHTML('beforeend', '
');} - window.search_dropdown.innerHTML = searchLabels[ppt['searchSend_internal'].value]; + window.search_dropdown.innerHTML = searchLabels[libSet['searchSend_internal'].value]; for (i = 0; i < 3; i++) {window.search.insertAdjacentHTML('beforeend', ''+searchLabels[i]+'');} i = 3; window.searchEnter.insertAdjacentHTML('beforeend', '
'); for (i = 0; i < 3; i++) {window.expand.insertAdjacentHTML('beforeend', '
');} @@ -1176,7 +1176,7 @@ function setDisplay() { for (i = 0; i < 3; i++) {window.bar.insertAdjacentHTML('beforeend', '
');} - window.theme_dropdown.innerHTML = themeLabels[ppt['theme_internal'].value]; + window.theme_dropdown.innerHTML = themeLabels[libSet['theme_internal'].value]; for (i = 0; i < 6; i++) {window.themeBg.insertAdjacentHTML('beforeend', ''+themeLabels[i]+'');} i = 6; window.themeStyle.insertAdjacentHTML('beforeend', '
'); i = 0; window.coverFormat.insertAdjacentHTML('beforeend', '
'); @@ -1188,41 +1188,41 @@ for (i = 2; i < 4; i++) {window.metrics2.insertAdjacentHTML('beforeend', '
');} for (i = 4; i < 6; i++) {window.metrics3.insertAdjacentHTML('beforeend', '
')}; - window.root_dropdown.innerHTML = rootLabels[ppt['rootNode_internal'].value]; + window.root_dropdown.innerHTML = rootLabels[libSet['rootNode_internal'].value]; for (i = 0; i < 4; i++) {window.root.insertAdjacentHTML('beforeend', ''+rootLabels[i]+'');} i = 4; window.rootStyle.insertAdjacentHTML('beforeend', '
'); i = 5; window.rootStyle.insertAdjacentHTML('beforeend', '
'); - window.node_dropdown.innerHTML = ppt['nodeStyle_internal'].value != 7 ? nodeLabels[ppt['nodeStyle_internal'].value] : 'Squares (windows)'; + window.node_dropdown.innerHTML = libSet['nodeStyle_internal'].value != 7 ? nodeLabels[libSet['nodeStyle_internal'].value] : 'Squares (windows)'; for (i = 0; i < 7; i++) {window.node.insertAdjacentHTML('beforeend', ''+nodeLabels[i]+'');} i = 7; window.nodeLines.insertAdjacentHTML('beforeend', '
'); - window.itemCounts_dropdown.innerHTML = itemCountsLabels[ppt['nodeCounts_internal'].value]; + window.itemCounts_dropdown.innerHTML = itemCountsLabels[libSet['nodeCounts_internal'].value]; for (i = 0; i < 3; i++) {window.itemCounts.insertAdjacentHTML('beforeend', ''+itemCountsLabels[i]+'');} i = 3; window.countsRight.insertAdjacentHTML('beforeend', '
'); for (i = 0; i < 3; i++) {window.statistics2.insertAdjacentHTML('beforeend', '
');} for (i = 4; i < 6; i++) {window.labelStatistics.insertAdjacentHTML('beforeend', '
')}; - window.showHl_dropdown.innerHTML = highLightLabels[ppt['highLightRow_internal'].value]; + window.showHl_dropdown.innerHTML = highLightLabels[libSet['highLightRow_internal'].value]; for (i = 0; i < 4; i++) {window.showHl.insertAdjacentHTML('beforeend', ''+highLightLabels[i]+'');} i = 0; window.frameStyle.insertAdjacentHTML('beforeend', '
'); for (i = 4; i < 7; i++) {window.showH2.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 7; i++) {window.show.insertAdjacentHTML('beforeend', '
');} } - var rootStubLabels = JSON.parse(ppt['rootImages_internal'].value) || []; - var root_ix = ppt['curRootImg_internal'].value; + var rootStubLabels = JSON.parse(libSet['rootImages_internal'].value) || []; + var root_ix = libSet['curRootImg_internal'].value; - var noArtistStubLabels = JSON.parse(ppt['noArtistImages_internal'].value) || []; - var noart_ix = ppt['curNoArtistImg_internal'].value; + var noArtistStubLabels = JSON.parse(libSet['noArtistImages_internal'].value) || []; + var noart_ix = libSet['curNoArtistImg_internal'].value; - var noCoverStubLabels = JSON.parse(ppt['noCoverImages_internal'].value) || []; - var nocov_ix = ppt['curNoCoverImg_internal'].value; + var noCoverStubLabels = JSON.parse(libSet['noCoverImages_internal'].value) || []; + var nocov_ix = libSet['curNoCoverImg_internal'].value; function setAlbumArt() { for (i = 0; i < 5; i++) {window.labels.insertAdjacentHTML('beforeend', '
');} i = 5; window.flip.insertAdjacentHTML('beforeend', '
'); - window.overlay_dropdown.innerHTML = albumArtOverlayLabels[ppt['itemOverlayType_internal'].value]; + window.overlay_dropdown.innerHTML = albumArtOverlayLabels[libSet['itemOverlayType_internal'].value]; for (i = 0; i < 3; i++) {window.overlay.insertAdjacentHTML('beforeend', ''+albumArtOverlayLabels[i]+'');} for (i = 0; i < 2; i++) {window.followCurrent.insertAdjacentHTML('beforeend', '
');} @@ -1244,7 +1244,7 @@ window.dropShadow.insertAdjacentHTML('beforeend', '
'); window.sameAsTree.insertAdjacentHTML('beforeend', '
'); - window.thumbSize_dropdown.innerHTML = thumbnailSizeLabels[ppt['thumbNailSize_internal'].value]; + window.thumbSize_dropdown.innerHTML = thumbnailSizeLabels[libSet['thumbNailSize_internal'].value]; for (i = 0; i < 8; i++) {window.thumbSize.insertAdjacentHTML('beforeend', ''+thumbnailSizeLabels[i]+'');} i = 0; window.indexLetter.insertAdjacentHTML('beforeend', '
'); @@ -1257,7 +1257,7 @@ function setCustomColours(value) { var items = ['text', 'text_h', 'textSel', 'nowp', 'search', 'txt_box', 'bg', 'bg_h', 'bgSel', 'frame', 'bgSelframe', 'counts', 'icon_c', 'icon_e', 'icon_h', 'line', 's_line', 'sideMarker', 'bgTrans']; for (var i= 0; i < items.length; i++) { - bg[i] = assign(ppt[items[i] + '_internal'][value], i < 6 ? 0 : 1); + bg[i] = assign(libSet[items[i] + '_internal'][value], i < 6 ? 0 : 1); fg[i] = getSelCol(bg[i]); bg[i] = "rgb("+bg[i][0]+","+bg[i][1]+","+bg[i][2]+")"; var el = document.getElementById('colour_container'+i); @@ -1276,11 +1276,11 @@ function setAdvanced() { for (i = 0; i < 4; i++) {window.cusScroll.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 5; i++) {window.scrollbar.insertAdjacentHTML('beforeend', '
');} - var fFamily = ppt['butCustIconFont_internal'].value; + var fFamily = libSet['butCustIconFont_internal'].value; i = 5; window.scrollbar.insertAdjacentHTML('beforeend', '
'); for (i = 6; i < 8; i++) {window.scrollbar.insertAdjacentHTML('beforeend', '
');} i = 8; window.scrollbar.insertAdjacentHTML('beforeend', '
'); - fFamily = ppt['custIconFont_internal'].value; + fFamily = libSet['custIconFont_internal'].value; i = 0; window.cusNode.insertAdjacentHTML('beforeend', '
'); for (i = 1; i < 3; i++) {window.cusNode.insertAdjacentHTML('beforeend', '
');} i = 3; window.cusNode.insertAdjacentHTML('beforeend', '
'); @@ -1327,10 +1327,10 @@ } var nm = page.toString()+'_checkbox', boxes = document.querySelectorAll(page == 'all' ? "[name$='_checkbox']" : "[name="+nm+"]"); for (i = 0; i < boxes.length; i++) { - boxes[i].checked = ppt[box[page][i] + '_internal'][value]; + boxes[i].checked = libSet[box[page][i] + '_internal'][value]; } - if (ppt['albumArtShow_internal'][value]) { + if (libSet['albumArtShow_internal'][value]) { window.show_checkbox4.parentNode.disabled = true; } if (window.remember_checkbox1.checked || window.remember_checkbox2.checked) window.remember_checkbox0.checked = true; @@ -1340,31 +1340,31 @@ window.checkbox_folderView.disabled = true; window.checkbox_menuName.disabled = true; document.getElementById("albumArtCfg").style.display = window.show_checkbox4.checked ? "block" : "none"; - window.indexLetter_checkbox2.parentNode.disabled = ppt['albumArtLetterNo_internal'].value == 0; - window.metrics_container4.parentNode.style.display = !ppt['filterShow_internal'].value && !ppt['searchShow_internal'].value && !ppt['settingsShow_internal'].value ? "block" : "none"; + window.indexLetter_checkbox2.parentNode.disabled = libSet['albumArtLetterNo_internal'].value == 0; + window.metrics_container4.parentNode.style.display = !libSet['filterShow_internal'].value && !libSet['searchShow_internal'].value && !libSet['settingsShow_internal'].value ? "block" : "none"; } function textChange(e) { if (window.remember_checkbox1.checked || window.remember_checkbox2.checked) window.remember_checkbox0.checked = true; window.remember_checkbox0.parentNode.disabled = window.remember_checkbox1.checked || window.remember_checkbox2.checked; - window.theme_checkbox7.parentNode.disabled = ppt['theme_internal'].value == 1 || ppt['theme_internal'].value == 4; - window.coverFormat_checkbox0.parentNode.disabled = ppt['theme_internal'].value != 5; - window.coverFormat_container1.parentNode.disabled = ppt['theme_internal'].value != 5; - window.frame_checkbox0.parentNode.disabled = ppt['highLightRow_internal'].value != 3; - window.node_checkbox7.parentNode.disabled = ppt['nodeStyle_internal'].value != 0 && ppt['nodeStyle_internal'].value != 7; - window.itemCounts_checkbox3.parentNode.disabled = ppt['itemShowStatistics_internal'].value; + window.theme_checkbox7.parentNode.disabled = libSet['theme_internal'].value == 1 || libSet['theme_internal'].value == 4; + window.coverFormat_checkbox0.parentNode.disabled = libSet['theme_internal'].value != 5; + window.coverFormat_container1.parentNode.disabled = libSet['theme_internal'].value != 5; + window.frame_checkbox0.parentNode.disabled = libSet['highLightRow_internal'].value != 3; + window.node_checkbox7.parentNode.disabled = libSet['nodeStyle_internal'].value != 0 && libSet['nodeStyle_internal'].value != 7; + window.itemCounts_checkbox3.parentNode.disabled = libSet['itemShowStatistics_internal'].value; setNodeLabels(); - window.showH2_checkbox5.parentNode.disabled = window.cusNode_checkbox3.checked && (ppt['nodeStyle_internal'].value == 0 || ppt['nodeStyle_internal'].value == 7); + window.showH2_checkbox5.parentNode.disabled = window.cusNode_checkbox3.checked && (libSet['nodeStyle_internal'].value == 0 || libSet['nodeStyle_internal'].value == 7); document.getElementById("albumArtCfg").style.display = window.show_checkbox4.checked ? "block" : "none"; window.albumArtLabelType2.parentNode.disabled = window.flowMode_checkbox.checked; window.thumbSize_dropdown.style.visibility = window.flowMode_checkbox.checked ? 'hidden' : 'visible'; window.thumbNailSize.style.visibility = window.flowMode_checkbox.checked ? 'hidden' : 'visible'; - window.metrics_container4.parentNode.style.display = !ppt['filterShow_internal'].value && !ppt['searchShow_internal'].value && !ppt['settingsShow_internal'].value ? "block" : "none"; - clickLabels = ppt['actionMode_internal'].value == 1 ? ['Send to playlist'] : ppt['actionMode_internal'].value == 2 ? ['Select'] : ['Select', 'Send to playlist', 'Send to playlist & play', 'Send to playlist & play [add if playing]']; - dblClickLabels = ppt['actionMode_internal'].value == 1 ? ['Send to playlist & play'] : ppt['actionMode_internal'].value == 2 ? ['Send to playback queue and play'] : ['Send to playlist', 'Send to playlist & play', 'Expand / collapse (tree)', 'Send to playback queue and play']; - mbtnClickLabels = ppt['actionMode_internal'].value == 2 ? ['Add to playback queue (middle dblclk removes)'] : ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (middle dblclk removes)']; + window.metrics_container4.parentNode.style.display = !libSet['filterShow_internal'].value && !libSet['searchShow_internal'].value && !libSet['settingsShow_internal'].value ? "block" : "none"; + clickLabels = libSet['actionMode_internal'].value == 1 ? ['Send to playlist'] : libSet['actionMode_internal'].value == 2 ? ['Select'] : ['Select', 'Send to playlist', 'Send to playlist & play', 'Send to playlist & play [add if playing]']; + dblClickLabels = libSet['actionMode_internal'].value == 1 ? ['Send to playlist & play'] : libSet['actionMode_internal'].value == 2 ? ['Send to playback queue and play'] : ['Send to playlist', 'Send to playlist & play', 'Expand / collapse (tree)', 'Send to playback queue and play']; + mbtnClickLabels = libSet['actionMode_internal'].value == 2 ? ['Add to playback queue (middle dblclk removes)'] : ['Add to default playlist', 'Add to current playlist', 'Add to playback queue (middle dblclk removes)']; - if (ppt['actionMode_internal'].value) { + if (libSet['actionMode_internal'].value) { window.click_dropdown.innerHTML = clickLabels[0]; window.click.innerHTML = ''; window.click.insertAdjacentHTML('beforeend', 'PRESET'); @@ -1374,22 +1374,22 @@ window.dblClick.insertAdjacentHTML('beforeend', 'PRESET'); window.dblClick_dropdown.style.background = 'rgb(244, 67, 54)'; } else { - window.click_dropdown.innerHTML = clickLabels[ppt['clickAction_internal'].value]; + window.click_dropdown.innerHTML = clickLabels[libSet['clickAction_internal'].value]; window.click.innerHTML = ''; for (i = 0; i < 4; i++) {window.click.insertAdjacentHTML('beforeend', ''+clickLabels[i]+'');} window.click_dropdown.style.background = '#3e8e41'; - window.dblClick_dropdown.innerHTML = dblClickLabels[ppt['dblClickAction_internal'].value]; + window.dblClick_dropdown.innerHTML = dblClickLabels[libSet['dblClickAction_internal'].value]; window.dblClick.innerHTML = ''; for (i = 0; i < 4; i++) {window.dblClick.insertAdjacentHTML('beforeend', ''+dblClickLabels[i]+'');} window.dblClick_dropdown.style.background = '#3e8e41'; } - if (ppt['actionMode_internal'].value == 2) { + if (libSet['actionMode_internal'].value == 2) { window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[0]; window.mbtnClick.innerHTML = ''; window.mbtnClick.insertAdjacentHTML('beforeend', 'PRESET'); window.mbtnClick_dropdown.style.background = 'rgb(244, 67, 54)'; } else { - window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[ppt['mbtnClickAction_internal'].value]; + window.mbtnClick_dropdown.innerHTML = mbtnClickLabels[libSet['mbtnClickAction_internal'].value]; window.mbtnClick.innerHTML = ''; for (i = 0; i < 3; i++) {window.mbtnClick.insertAdjacentHTML('beforeend', ''+mbtnClickLabels[i]+'');} window.mbtnClick_dropdown.style.background = '#3e8e41'; @@ -1398,7 +1398,7 @@ setInterval(inputChanged, 500); function inputChanged() { - window.indexLetter_checkbox2.parentNode.disabled = ppt['albumArtLetterNo_internal'].value == 0; + window.indexLetter_checkbox2.parentNode.disabled = libSet['albumArtLetterNo_internal'].value == 0; set_btn_apply_state(); } @@ -1406,7 +1406,7 @@ var radios = radio[page]; for (i = 0; i < radios.length; i++) { var radioBtns = document.getElementsByName(radios[i]); - var new_value = radios[i] == 'nodeStyle' && ppt[radios[i] + '_internal'][value] == 7 ? 0 : ppt[radios[i] + '_internal'][value]; + var new_value = radios[i] == 'nodeStyle' && libSet[radios[i] + '_internal'][value] == 7 ? 0 : libSet[radios[i] + '_internal'][value]; for (j = 0; j < radioBtns.length; j++) { if (j == new_value) { radioBtns[j].checked = true; @@ -1414,14 +1414,14 @@ } } } - window.theme_checkbox7.parentNode.disabled = ppt['theme_internal'].value == 1 || ppt['theme_internal'].value == 4; - window.coverFormat_checkbox0.parentNode.disabled = ppt['theme_internal'].value != 5; - window.coverFormat_container1.parentNode.disabled = ppt['theme_internal'].value != 5; - if (ppt['themed_internal'].value) window.themeStyle.style.display = "none"; - if (ppt['themed_internal'].value) window.coverFormat.style.display = "none"; - if (ppt['themed_internal'].value) window.panelStyle.style.display = "none"; - window.node_checkbox7.parentNode.disabled = ppt['nodeStyle_internal'].value != 0 && ppt['nodeStyle_internal'].value != 7; - window.showH2_checkbox5.parentNode.disabled = window.cusNode_checkbox3.checked && (ppt['nodeStyle_internal'].value == 0 || ppt['nodeStyle_internal'].value == 7); + window.theme_checkbox7.parentNode.disabled = libSet['theme_internal'].value == 1 || libSet['theme_internal'].value == 4; + window.coverFormat_checkbox0.parentNode.disabled = libSet['theme_internal'].value != 5; + window.coverFormat_container1.parentNode.disabled = libSet['theme_internal'].value != 5; + if (libSet['themed_internal'].value) window.themeStyle.style.display = "none"; + if (libSet['themed_internal'].value) window.coverFormat.style.display = "none"; + if (libSet['themed_internal'].value) window.panelStyle.style.display = "none"; + window.node_checkbox7.parentNode.disabled = libSet['nodeStyle_internal'].value != 0 && libSet['nodeStyle_internal'].value != 7; + window.showH2_checkbox5.parentNode.disabled = window.cusNode_checkbox3.checked && (libSet['nodeStyle_internal'].value == 0 || libSet['nodeStyle_internal'].value == 7); window.albumArtLabelType2.parentNode.disabled = window.flowMode_checkbox.checked; } @@ -1480,19 +1480,19 @@ inputs = document.querySelectorAll("[name$='_input']"); for (i = 0; i < inputs.length; i++) { - (ppt[input.all.name[i] + '_internal'] || cfg[input.all.name[i] + '_internal']).value = input.all.type[i] == 'text' ? inputs[i].value : input.all.type[i] == 'int' ? parseInt(inputs[i].value) : parseFloat(inputs[i].value); + (libSet[input.all.name[i] + '_internal'] || cfg[input.all.name[i] + '_internal']).value = input.all.type[i] == 'text' ? inputs[i].value : input.all.type[i] == 'int' ? parseInt(inputs[i].value) : parseFloat(inputs[i].value); } var boxes = document.querySelectorAll("[name$='_checkbox']"); for (i = 0; i < boxes.length; i++) { - (ppt[box.all[i] + '_internal'] || cfg[box.all[i] + '_internal']).value = boxes[i].checked; + (libSet[box.all[i] + '_internal'] || cfg[box.all[i] + '_internal']).value = boxes[i].checked; } for (i = 0; i < radio.all.length; i++) { var radioBtns = document.getElementsByName(radio.all[i]); for (j = 0; j < radioBtns.length; j++) { if (radioBtns[j].checked) { - ppt[radio.all[i] + '_internal'].value = j; + libSet[radio.all[i] + '_internal'].value = j; break; } } @@ -1502,7 +1502,7 @@ window.btn_ok.onclick = function () { getValues(); var new_cfg = JSON.stringify(cfg); - var new_ppt = JSON.stringify(ppt); + var new_ppt = JSON.stringify(libSet); var new_cur = JSON.stringify(cur); if (new_cfg == parsed_args[0]) new_cfg = ''; if (new_ppt == parsed_args[1]) new_ppt = ''; @@ -1518,7 +1518,7 @@ window.btn_apply.onclick = function () { getValues(); var new_cfg = JSON.stringify(cfg); - var new_ppt = JSON.stringify(ppt); + var new_ppt = JSON.stringify(libSet); if (new_cfg == parsed_args[0]) new_cfg = ''; else parsed_args[0] = new_cfg; if (new_ppt == parsed_args[1]) new_ppt = ''; @@ -1630,10 +1630,10 @@ var nm = page.toString()+'_input', inputs = document.querySelectorAll(page == 'all' ? "[name$='_input']" : "[name="+nm+"]"); for (i = 0; i < inputs.length; i++) { - inputs[i].value = ppt[input[page].name[i] + '_internal'][value]; + inputs[i].value = libSet[input[page].name[i] + '_internal'][value]; } - window.coverFormat_container1.parentNode.disabled = ppt['theme_internal'].value != 4; + window.coverFormat_container1.parentNode.disabled = libSet['theme_internal'].value != 4; } window.btn_resetPage.onclick = function () { @@ -1654,34 +1654,34 @@ setInputs(cur.page, 'default_value'); setRadioBtns(cur.page, 'default_value'); if (cur.page == 'behaviour') { - setClickAction(ppt['clickAction_internal'].default_value); - setDblClickAction(ppt['dblClickAction_internal'].default_value); - setKeyAction(ppt['keyAction_internal'].default_value); + setClickAction(libSet['clickAction_internal'].default_value); + setDblClickAction(libSet['dblClickAction_internal'].default_value); + setKeyAction(libSet['keyAction_internal'].default_value); - setRoot(ppt['rootNode_internal'].default_value); - setNode(ppt['nodeStyle_internal'].default_value); - setItemCounts(ppt['nodeCounts_internal'].default_value); - setHoverBg(ppt['highLightRow_internal'].default_value); + setRoot(libSet['rootNode_internal'].default_value); + setNode(libSet['nodeStyle_internal'].default_value); + setItemCounts(libSet['nodeCounts_internal'].default_value); + setHoverBg(libSet['highLightRow_internal'].default_value); - setSbarShow(ppt['sbarShow_internal'].default_value); - setsbarType(ppt['sbarType_internal'].default_value); - setSbarCol(ppt['sbarCol_internal'].default_value); - setSbarIcon(ppt['highLightRow_internal'].default_value); + setSbarShow(libSet['sbarShow_internal'].default_value); + setsbarType(libSet['sbarType_internal'].default_value); + setSbarCol(libSet['sbarCol_internal'].default_value); + setSbarIcon(libSet['highLightRow_internal'].default_value); - setSearch(ppt['searchSend_internal'].default_value); + setSearch(libSet['searchSend_internal'].default_value); } if (cur.page == 'display') { - setTheme(ppt['theme_internal'].default_value); + setTheme(libSet['theme_internal'].default_value); } if (cur.page == 'albumArt') { - window.rootStub_dropdown.innerHTML = 'Root: ' + ppt.curRootImg_internal.default_value; - ppt.curRootImg_internal.value = ppt.curRootImg_internal.default_value; - window.noArtistStub_dropdown.innerHTML = 'No artist: ' + ppt.curNoArtistImg_internal.default_value; - ppt.curNoArtistImg_internal.value = ppt.curNoArtistImg_internal.default_value; - window.noCoverStub_dropdown.innerHTML = 'No cover: ' + ppt.curNoCoverImg_internal.default_value; - ppt.curNoCoverImg_internal.value = ppt.curNoCoverImg_internal.default_value; - setOverlay(ppt['itemOverlayType_internal'].default_value); - setthumbSize(ppt['thumbNailSize_internal'].default_value); + window.rootStub_dropdown.innerHTML = 'Root: ' + libSet.curRootImg_internal.default_value; + libSet.curRootImg_internal.value = libSet.curRootImg_internal.default_value; + window.noArtistStub_dropdown.innerHTML = 'No artist: ' + libSet.curNoArtistImg_internal.default_value; + libSet.curNoArtistImg_internal.value = libSet.curNoArtistImg_internal.default_value; + window.noCoverStub_dropdown.innerHTML = 'No cover: ' + libSet.curNoCoverImg_internal.default_value; + libSet.curNoCoverImg_internal.value = libSet.curNoCoverImg_internal.default_value; + setOverlay(libSet['itemOverlayType_internal'].default_value); + setthumbSize(libSet['thumbNailSize_internal'].default_value); } if (cur.page == 'custom') setCustomColours('default_value'); } diff --git a/profile/georgia-reborn/scripts/Library/lib-main.js b/profile/georgia-reborn/scripts/Library/lib-main.js index 42fa297e..2b95b4a9 100644 --- a/profile/georgia-reborn/scripts/Library/lib-main.js +++ b/profile/georgia-reborn/scripts/Library/lib-main.js @@ -1,31 +1,97 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Library * // -// * Author: TT * // -// * Org. Author: WilB * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 2.4.0 * // -// * Dev. started: 2016-10-18 * // -// * Last change: 2023-05-13 (Mod change 2024-01-08) * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Library * // +// * Author: TT * // +// * Org. Author: WilB * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 2.4.0 * // +// * Dev. started: 18-10-2016 * // +// * Last change: 13-05-2023 (Mod change 18-02-2024) * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; +/////////////////////////////////////////////////////////// +// ! IMPORTANT NOTICE AND DOCUMENTATION FOR OTHER DEVS ! // +/////////////////////////////////////////////////////////// +// The original library script is designed for use in isolated panel containers to prevent interference with global variables and classes. +// The Georgia-ReBORN theme uses only one Spider Monkey Panel, which shares its global namespace with all scripts. +// To avoid conflicts, several global variables and classes were renamed with a 'lib' prefix to indicate they are library-specific. +// All library class instances were also added to the `lib` main object. + +// * GLOBAL VARS * // +// const requiredVersionStr -> libRequiredVersionStr +// const doc -> libDoc +// const fso -> libFSO +// const tooltip -> libTooltip +// const WshShell -> libWshShell +// const $ -> $Lib +// const ease -> libEase +// const md5 -> libMD5 + +// const img -> libImg + +// const lib -> the added lib main object + +// let colourSelector -> libColourSelector +// let sync -> libSync +// const syncer -> libSyncer + +// const MF_GRAYED -> LIB_MF_GRAYED +// const MF_STRING -> LIB_MF_STRING +// const clearArr -> libClearArr +// const menu -> libMenu +// const fMenu -> libFMenu +// const sMenu -> libSMenu +// const searchMenu -> libSearchMenu + +// let properties -> libProperties +// const ppt -> libSet + +// const my_utils -> lib_my_utils + +// * FUNCTIONS * // +// function is_compatible -> lib_is_compatible +// function Bezier -> LibBezier +// function MD5 -> LibMD5 + +// * CLASSES * // +// class Buttons -> LibButtons +// class Btn -> LibBtn +// class Tooltip -> LibTooltip +// class TooltipTimer -> LibTooltipTimer +// class Transition -> LibTransition +// class Helpers -> LibHelpers +// class Images -> LibImages +// class UserInterface -> LibUserInterface +// class Vkeys -> LibVkeys +// class Library -> LibLibrary +// class MenuManager -> LibMenuManager +// class MenuItems -> LibMenuItems +// class Panel -> LibPanel +// class Populate -> LibPopulate +// class PopUpBox -> LibPopUpBox +// class PanelProperty -> LibPanelProperty +// class PanelProperties -> LibPanelProperties +// class Scrollbar -> LibScrollbar +// class Search -> LibSearch +// class Find -> LibFind +// class Timers -> LibTimers + + //////////////////////////////////////////////// // ! ALL FILES LOADED IN GR-ASYNC-LOADER.JS ! // //////////////////////////////////////////////// -let libraryInitialized = false; - -if (typeof my_utilsLib === 'undefined') include(`${basePath}scripts\\library\\scripts\\lib-utils.js`); +if (typeof lib_my_utils === 'undefined') include(`${grPath.base}scripts\\library\\scripts\\lib-utils.js`); // const loadAsync = window.GetProperty('Panel Library - Load Library Tree Asynchronously', true); // async function readFiles(files) { // for (const file of files) { // if (window.ID) { // fix pss issue -// await include(my_utilsLib.getScriptPath + file); +// await include(lib_my_utils.getScriptPath + file); // } // } // } @@ -55,5 +121,5 @@ if (typeof my_utilsLib === 'undefined') include(`${basePath}scripts\\library\\sc // window.Repaint(); // }); // } else { -// files.forEach(v => include(my_utilsLib.getScriptPath + v)); +// files.forEach(v => include(lib_my_utils.getScriptPath + v)); // } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-buttons.js b/profile/georgia-reborn/scripts/Library/scripts/lib-buttons.js index 4bb2b31b..249c4b64 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-buttons.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-buttons.js @@ -1,6 +1,6 @@ 'use strict'; -class Buttons { +class LibButtons { constructor() { this.alpha = 255; this.arc = 1; @@ -8,7 +8,7 @@ class Buttons { this.Dn = false; this.hoverArea = 4; this.hot_h = 4; - this.margin = Math.max(ppt.margin * 2 + 2, 12) / 4; + this.margin = Math.max(libSet.margin * 2 + 2, 12) / 4; this.trace = false; this.transition; this.vertical = true; @@ -32,13 +32,13 @@ class Buttons { iconFontName: 'Segoe UI Symbol', iconFontStyle: 0, img: null, - opaque: ui.getOpaque(), - pad: $Lib.clamp(ppt.sbarButPad / 100, -0.5, 0.3) + opaque: lib.ui.getOpaque(), + pad: $Lib.clamp(libSet.sbarButPad / 100, -0.5, 0.3) }; this.tooltipLib = { delay: true, - show: pref.showTooltipLibrary || pref.showTooltipTruncated, + show: grSet.showTooltipLibrary || grSet.showTooltipTruncated, start: Date.now() - 2000 }; @@ -49,13 +49,13 @@ class Buttons { // * METHODS * // checkScrollBtns(x, y, hover_btn) { - if (sbar.timer_but) { + if (lib.sbar.timer_but) { if ((this.btns.scrollUp.down || this.btns.scrollDn.down) && !this.btns.scrollUp.trace(x, y) && !this.btns.scrollDn.trace(x, y)) { this.btns.scrollUp.cs('normal'); this.btns.scrollDn.cs('normal'); - clearTimeout(sbar.timer_but); - sbar.timer_but = false; - sbar.count = -1; + clearTimeout(lib.sbar.timer_but); + lib.sbar.timer_but = false; + lib.sbar.count = -1; } } else if (hover_btn) { this.scr.btns.forEach(v => { if (hover_btn.name == v && hover_btn.down) { @@ -71,17 +71,17 @@ class Buttons { } createImages() { - let sz = this.scr.arrow == 0 ? Math.max(Math.round(ui.sbar.but_h * 1.666667), 1) : 100; + let sz = this.scr.arrow == 0 ? Math.max(Math.round(lib.ui.sbar.but_h * 1.666667), 1) : 100; const sc = sz / 100; const iconFont = gdi.Font(this.scr.iconFontName, sz, this.scr.iconFontStyle); - this.alpha = !ui.sbar.col ? [75, 192, 228] : [210, 255, 255]; - const hovAlpha = (!ui.sbar.col ? 75 : (!ui.sbar.type ? 68 : 51)) * 0.4; - this.scr.hover = !ui.sbar.col ? RGBA(ui.col.t, ui.col.t, ui.col.t, hovAlpha) : ui.col.text & RGBA(255, 255, 255, hovAlpha); + this.alpha = !lib.ui.sbar.col ? [75, 192, 228] : [210, 255, 255]; + const hovAlpha = (!lib.ui.sbar.col ? 75 : (!lib.ui.sbar.type ? 68 : 51)) * 0.4; + this.scr.hover = !lib.ui.sbar.col ? RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, hovAlpha) : lib.ui.col.text & RGBA(255, 255, 255, hovAlpha); this.q.s_img = $Lib.gr(100, 100, true, g => { g.SetSmoothingMode(2); - g.DrawLine(69, 71, 88, 90, 12, !ui.id.local ? ui.col.searchBtn : ui.col.searchBtn); - g.DrawEllipse(8, 11, 67, 67, 10, !ui.id.local ? ui.col.searchBtn : ui.col.searchBtn); - // g.FillEllipse(15, 17, 55, 55, ui.col.bg); // Don't need this + g.DrawLine(69, 71, 88, 90, 12, !lib.ui.id.local ? lib.ui.col.searchBtn : lib.ui.col.searchBtn); + g.DrawEllipse(8, 11, 67, 67, 10, !lib.ui.id.local ? lib.ui.col.searchBtn : lib.ui.col.searchBtn); + // g.FillEllipse(15, 17, 55, 55, lib.ui.col.bg); // Don't need this g.SetSmoothingMode(0); }); this.q.s_img.RotateFlip(4); @@ -89,12 +89,12 @@ class Buttons { this.scr.img = $Lib.gr(sz, sz, true, g => { g.SetTextRenderingHint(3); g.SetSmoothingMode(2); - if (ui.sbar.col) { - this.scr.arrow == 0 ? g.FillPolygon(ui.col.text, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : - g.DrawString(this.scr.arrow, iconFont, ui.col.sbarBtns, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); + if (lib.ui.sbar.col) { + this.scr.arrow == 0 ? g.FillPolygon(lib.ui.col.text, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : + g.DrawString(this.scr.arrow, iconFont, lib.ui.col.sbarBtns, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); } else { - this.scr.arrow == 0 ? g.FillPolygon(RGBA(ui.col.t, ui.col.t, ui.col.t, 255), 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : - g.DrawString(this.scr.arrow, iconFont, RGBA(ui.col.t, ui.col.t, ui.col.t, 255), 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); + this.scr.arrow == 0 ? g.FillPolygon(RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 255), 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : + g.DrawString(this.scr.arrow, iconFont, RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 255), 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); } g.SetSmoothingMode(0); }); @@ -102,11 +102,11 @@ class Buttons { this.scr.bg = $Lib.gr(sz, sz, true, g => { g.SetTextRenderingHint(3); g.SetSmoothingMode(2); - if (ui.sbar.col) { - this.scr.arrow == 0 ? g.FillPolygon(ui.col.bg, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, ui.col.bg, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); + if (lib.ui.sbar.col) { + this.scr.arrow == 0 ? g.FillPolygon(lib.ui.col.bg, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, lib.ui.col.bg, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); } else { - this.scr.arrow == 0 ? g.FillPolygon(ui.col.bg, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : - g.DrawString(this.scr.arrow, iconFont, ui.col.bg, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); + this.scr.arrow == 0 ? g.FillPolygon(lib.ui.col.bg, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : + g.DrawString(this.scr.arrow, iconFont, lib.ui.col.bg, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1)); } g.SetSmoothingMode(0); }); @@ -117,8 +117,8 @@ class Buttons { const nn = 31; const offset1 = 12; const offset2 = 2; - g.DrawLine(offset1, nn - offset2, 100 - nn * 2 + offset1, 100 - nn - offset2, 5, !ui.id.local ? ui.col.crossBtn : ui.col.crossBtn); - g.DrawLine(offset1, 100 - nn - offset2, 100 - nn * 2 + offset1, nn - offset2, 5, !ui.id.local ? ui.col.crossBtn : ui.col.crossBtn); + g.DrawLine(offset1, nn - offset2, 100 - nn * 2 + offset1, 100 - nn - offset2, 5, !lib.ui.id.local ? lib.ui.col.crossBtn : lib.ui.col.crossBtn); + g.DrawLine(offset1, 100 - nn - offset2, 100 - nn * 2 + offset1, nn - offset2, 5, !lib.ui.id.local ? lib.ui.col.crossBtn : lib.ui.col.crossBtn); g.SetSmoothingMode(0); }); this.cross.hover = $Lib.gr(sz, sz, true, g => { @@ -127,8 +127,8 @@ class Buttons { const nn = 28; const offset1 = 9; const offset2 = 2; - g.DrawLine(offset1, nn - offset2, 100 - nn * 2 + offset1, 100 - nn - offset2, 5, !ui.id.local ? ui.col.crossBtn : ui.col.crossBtn); - g.DrawLine(offset1, 100 - nn - offset2, 100 - nn * 2 + offset1, nn - offset2, 5, !ui.id.local ? ui.col.crossBtn : ui.col.crossBtn); + g.DrawLine(offset1, nn - offset2, 100 - nn * 2 + offset1, 100 - nn - offset2, 5, !lib.ui.id.local ? lib.ui.col.crossBtn : lib.ui.col.crossBtn); + g.DrawLine(offset1, 100 - nn - offset2, 100 - nn * 2 + offset1, nn - offset2, 5, !lib.ui.id.local ? lib.ui.col.crossBtn : lib.ui.col.crossBtn); g.SetSmoothingMode(0); }); } @@ -179,7 +179,7 @@ class Buttons { if (hover_btn) { hand = hover_btn.hand; } - pop.hand = hand; + lib.pop.hand = hand; if (hover_btn && hover_btn.hide) { if (this.cur) { this.cur.cs('normal'); @@ -195,7 +195,7 @@ class Buttons { } // return prev btn to normal state if (hover_btn && !(hover_btn.down && hover_btn.type < 4)) { hover_btn.cs('hover'); - if (pref.showTooltipLibrary && this.tooltipLib.show && hover_btn.tiptext) hover_btn.tt.show(hover_btn.tiptext()); + if (grSet.showTooltipLibrary && this.tooltipLib.show && hover_btn.tiptext) hover_btn.tt.show(hover_btn.tiptext()); this.transition.start(); } this.cur = hover_btn; @@ -211,27 +211,27 @@ class Buttons { } setSbarIcon() { - switch (ppt.sbarButType) { + switch (libSet.sbarButType) { case 0: this.scr.iconFontName = 'Segoe UI Symbol'; this.scr.iconFontStyle = 0; - if (!ui.sbar.type) { - this.scr.arrow = ui.sbar.but_w < Math.round(14 * $Lib.scale) ? '\uE018' : '\uE0A0'; - this.scr.pad = ui.sbar.but_w < Math.round(15 * $Lib.scale) ? -0.3 : -0.22; + if (!lib.ui.sbar.type) { + this.scr.arrow = lib.ui.sbar.but_w < Math.round(14 * $Lib.scale) ? '\uE018' : '\uE0A0'; + this.scr.pad = lib.ui.sbar.but_w < Math.round(15 * $Lib.scale) ? -0.3 : -0.22; } else { - this.scr.arrow = ui.sbar.but_w < Math.round(14 * $Lib.scale) ? '\uE018' : '\uE0A0'; - this.scr.pad = ui.sbar.but_w < Math.round(14 * $Lib.scale) ? -0.26 : -0.22; + this.scr.arrow = lib.ui.sbar.but_w < Math.round(14 * $Lib.scale) ? '\uE018' : '\uE0A0'; + this.scr.pad = lib.ui.sbar.but_w < Math.round(14 * $Lib.scale) ? -0.26 : -0.22; } break; case 1: this.scr.arrow = 0; break; case 2: - this.scr.iconFontName = ppt.butCustIconFont; + this.scr.iconFontName = libSet.butCustIconFont; this.scr.iconFontStyle = 0; - this.scr.arrow = ppt.arrowSymbol.charAt().trim(); + this.scr.arrow = libSet.arrowSymbol.charAt().trim(); if (!this.scr.arrow.length) this.scr.arrow = 0; - this.scr.pad = $Lib.clamp(ppt.sbarButPad / 100, -0.5, 0.3); + this.scr.pad = $Lib.clamp(libSet.sbarButPad / 100, -0.5, 0.3); break; } } @@ -241,18 +241,18 @@ class Buttons { this.scr.btns.forEach(v => { if (this.btns[v]) this.btns[v].hide = set; }); - panel.treePaint(); + lib.panel.treePaint(); } else { - if (!this.btns || (!ppt.sbarShow && !set)) return; + if (!this.btns || (!libSet.sbarShow && !set)) return; this.scr.btns.forEach(v => { - if (this.btns[v]) this.btns[v].hide = sbar.scrollable_lines < 1 || !ppt.sbarShow; + if (this.btns[v]) this.btns[v].hide = lib.sbar.scrollable_lines < 1 || !libSet.sbarShow; }); } } setSearchBtnsHide() { - const noShow = !ppt.searchShow; - const searching = (ppt.filterShow || ppt.settingsShow) && panel.search.txt; + const noShow = !libSet.searchShow; + const searching = (libSet.filterShow || libSet.settingsShow) && lib.panel.search.txt; const o1 = this.btns.s_img; if (o1) o1.hide = noShow || searching; const o2 = this.btns.cross2; @@ -261,119 +261,119 @@ class Buttons { refresh(upd) { if (upd) { - this.b.x = ui.w - ui.sz.marginSearch - Math.round(ui.row.h * 0.59); - this.b.h = ui.row.h; - this.b.y = ui.y + Math.round((panel.search.sp - this.b.h * 0.4) / 2 - this.b.h * 0.27); - this.scr.opaque = ui.getOpaque(); - this.vertical = !panel.imgView || img.style.vertical; + this.b.x = lib.ui.w - lib.ui.sz.marginSearch - Math.round(lib.ui.row.h * 0.59); + this.b.h = lib.ui.row.h; + this.b.y = lib.ui.y + Math.round((lib.panel.search.sp - this.b.h * 0.4) / 2 - this.b.h * 0.27); + this.scr.opaque = lib.ui.getOpaque(); + this.vertical = !lib.panel.imgView || libImg.style.vertical; switch (true) { case this.vertical: - this.scr.x1 = panel.sbar_x + (RES_4K ? 6 : 0); - this.scr.yUp1 = sbar.y; - this.scr.yDn1 = sbar.y + sbar.h - ui.sbar.but_h; - if (ui.sbar.type != 2) { + this.scr.x1 = lib.panel.sbar_x + (RES._4K ? 6 : 0); + this.scr.yUp1 = lib.sbar.y; + this.scr.yDn1 = lib.sbar.y + lib.sbar.h - lib.ui.sbar.but_h; + if (lib.ui.sbar.type != 2) { this.scr.x1 -= 1; - this.scr.hotOffset = this.scr.yUp1 - panel.search.h; - this.scr.x2 = (ui.sbar.but_h - ui.sbar.but_w) / 2; - this.scr.yUp2 = -ui.sbar.arrowPad + this.scr.yUp1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2; - this.scr.yDn2 = ui.sbar.arrowPad + this.scr.yDn1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2; + this.scr.hotOffset = this.scr.yUp1 - lib.panel.search.h; + this.scr.x2 = (lib.ui.sbar.but_h - lib.ui.sbar.but_w) / 2; + this.scr.yUp2 = -lib.ui.sbar.arrowPad + this.scr.yUp1 + (lib.ui.sbar.but_h - 1 - lib.ui.sbar.but_w) / 2; + this.scr.yDn2 = lib.ui.sbar.arrowPad + this.scr.yDn1 + (lib.ui.sbar.but_h - 1 - lib.ui.sbar.but_w) / 2; } break; case !this.vertical: - this.scr.x1 = sbar.x; - this.scr.x3 = sbar.x + sbar.w - ui.sbar.but_h; - this.scr.xLeft1 = sbar.x; - this.scr.xRight1 = sbar.x + sbar.w - ui.sbar.but_h; - this.scr.y1 = panel.sbar_y; - if (ui.sbar.type != 2) { + this.scr.x1 = lib.sbar.x; + this.scr.x3 = lib.sbar.x + lib.sbar.w - lib.ui.sbar.but_h; + this.scr.xLeft1 = lib.sbar.x; + this.scr.xRight1 = lib.sbar.x + lib.sbar.w - lib.ui.sbar.but_h; + this.scr.y1 = lib.panel.sbar_y; + if (lib.ui.sbar.type != 2) { this.scr.y1 -= 1; this.scr.hotOffset = this.scr.xLeft1 - 0; - this.scr.y2 = (ui.sbar.but_h - ui.sbar.but_w) / 2; - this.scr.xLeft2 = -ui.sbar.arrowPad + this.scr.xLeft1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2; - this.scr.xRight2 = ui.sbar.arrowPad + this.scr.xRight1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2; + this.scr.y2 = (lib.ui.sbar.but_h - lib.ui.sbar.but_w) / 2; + this.scr.xLeft2 = -lib.ui.sbar.arrowPad + this.scr.xLeft1 + (lib.ui.sbar.but_h - 1 - lib.ui.sbar.but_w) / 2; + this.scr.xRight2 = lib.ui.sbar.arrowPad + this.scr.xRight1 + (lib.ui.sbar.but_h - 1 - lib.ui.sbar.but_w) / 2; } break; } - this.q.x = ui.x + ui.sz.marginSearch; - this.q.y = ui.y + (panel.search.sp - ui.row.h * 0.6) / 2 + (ui.row.h - ui.font.main.Size) % 2; - this.q.h = ui.row.h * 0.6; - this.hoverArea = Math.round(panel.search.sp / 8); - this.hot_h = Math.max(panel.search.sp - this.hoverArea * 4, 4); - this.margin = Math.max(ppt.margin * 2 + 2, 12) / 4; - this.arc = Math.max(Math.round(Math.min(panel.search.sp - this.hoverArea * 2, panel.settings.w + this.margin / 2) / 4), 1); - this.s.w1 = panel.settings.w + but.margin; - this.s.w2 = ui.w - ui.sz.marginSearch - 1 + panel.settings.offset; - this.s.x = this.s.w2 - panel.settings.w - but.margin / 2; + this.q.x = lib.ui.x + lib.ui.sz.marginSearch; + this.q.y = lib.ui.y + (lib.panel.search.sp - lib.ui.row.h * 0.6) / 2 + (lib.ui.row.h - lib.ui.font.main.Size) % 2; + this.q.h = lib.ui.row.h * 0.6; + this.hoverArea = Math.round(lib.panel.search.sp / 8); + this.hot_h = Math.max(lib.panel.search.sp - this.hoverArea * 4, 4); + this.margin = Math.max(libSet.margin * 2 + 2, 12) / 4; + this.arc = Math.max(Math.round(Math.min(lib.panel.search.sp - this.hoverArea * 2, lib.panel.settings.w + this.margin / 2) / 4), 1); + this.s.w1 = lib.panel.settings.w + lib.but.margin; + this.s.w2 = lib.ui.w - lib.ui.sz.marginSearch - 1 + lib.panel.settings.offset; + this.s.x = this.s.w2 - lib.panel.settings.w - lib.but.margin / 2; } - if (ppt.sbarShow) { - switch (ui.sbar.type) { + if (libSet.sbarShow) { + switch (lib.ui.sbar.type) { case 2: switch (true) { case this.vertical: - this.btns.scrollUp = new Btn(this.scr.x1, this.scr.yUp1, ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', { + this.btns.scrollUp = new LibBtn(this.scr.x1, this.scr.yUp1, lib.ui.sbar.but_h, lib.ui.sbar.but_h, 3, '', '', '', { normal: 1, hover: 2, down: 3 - }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp'); - this.btns.scrollDn = new Btn(this.scr.x1, this.scr.yDn1, ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', { + }, libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(1), '', '', false, 'scrollUp'); + this.btns.scrollDn = new LibBtn(this.scr.x1, this.scr.yDn1, lib.ui.sbar.but_h, lib.ui.sbar.but_h, 3, '', '', '', { normal: 5, hover: 6, down: 7 - }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn'); + }, libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(-1), '', '', false, 'scrollDn'); break; case !this.vertical: - this.btns.scrollUp = new Btn(ui.x + this.scr.xLeft1, this.scr.y1 - (RES_4K ? 46 : 19), ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', { + this.btns.scrollUp = new LibBtn(lib.ui.x + this.scr.xLeft1, this.scr.y1 - (RES._4K ? 46 : 19), lib.ui.sbar.but_h, lib.ui.sbar.but_h, 3, '', '', '', { normal: 9, hover: 10, down: 11 - }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp'); - this.btns.scrollDn = new Btn(this.scr.xRight1, this.scr.y1 - (RES_4K ? 46 : 19), ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', { + }, libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(1), '', '', false, 'scrollUp'); + this.btns.scrollDn = new LibBtn(this.scr.xRight1, this.scr.y1 - (RES._4K ? 46 : 19), lib.ui.sbar.but_h, lib.ui.sbar.but_h, 3, '', '', '', { normal: 13, hover: 14, down: 15 - }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn'); + }, libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(-1), '', '', false, 'scrollDn'); break; } break; default: switch (true) { case this.vertical: - this.btns.scrollUp = new Btn(this.scr.x1, this.scr.yUp1, ui.sbar.but_h, ui.sbar.but_h, 1, this.scr.x2, this.scr.yUp2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp'); - this.btns.scrollDn = new Btn(this.scr.x1, this.scr.yDn1, ui.sbar.but_h, ui.sbar.but_h, 2, this.scr.x2, this.scr.yDn2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn'); + this.btns.scrollUp = new LibBtn(this.scr.x1, this.scr.yUp1, lib.ui.sbar.but_h, lib.ui.sbar.but_h, 1, this.scr.x2, this.scr.yUp2, lib.ui.sbar.but_w, '', libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(1), '', '', false, 'scrollUp'); + this.btns.scrollDn = new LibBtn(this.scr.x1, this.scr.yDn1, lib.ui.sbar.but_h, lib.ui.sbar.but_h, 2, this.scr.x2, this.scr.yDn2, lib.ui.sbar.but_w, '', libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(-1), '', '', false, 'scrollDn'); break; case !this.vertical: - this.btns.scrollUp = new Btn(this.scr.xLeft1 - this.scr.hotOffset + SCALE(40), this.scr.y1 - (RES_4K ? 46 : 19), ui.sbar.but_h, ui.sbar.but_h + this.scr.hotOffset, 1, this.scr.y2, this.scr.xLeft2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp'); - this.btns.scrollDn = new Btn(this.scr.xRight1, this.scr.y1 - (RES_4K ? 46 : 19), ui.sbar.but_h, ui.sbar.but_h + this.scr.hotOffset, 2, this.scr.y2, this.scr.xRight2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn'); + this.btns.scrollUp = new LibBtn(this.scr.xLeft1 - this.scr.hotOffset + SCALE(40), this.scr.y1 - (RES._4K ? 46 : 19), lib.ui.sbar.but_h, lib.ui.sbar.but_h + this.scr.hotOffset, 1, this.scr.y2, this.scr.xLeft2, lib.ui.sbar.but_w, '', libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(1), '', '', false, 'scrollUp'); + this.btns.scrollDn = new LibBtn(this.scr.xRight1, this.scr.y1 - (RES._4K ? 46 : 19), lib.ui.sbar.but_h, lib.ui.sbar.but_h + this.scr.hotOffset, 2, this.scr.y2, this.scr.xRight2, lib.ui.sbar.but_w, '', libSet.sbarShow == 1 && lib.sbar.narrow.show || lib.sbar.scrollable_lines < 1, () => lib.sbar.but(-1), '', '', false, 'scrollDn'); break; } break; } } - this.transition = new Transition(this.btns, v => v.state !== 'normal'); - this.btns.s_img = new Btn(this.q.x - this.margin / 2, ui.y + this.hoverArea, this.q.h + this.margin, this.hot_h, 4, this.q.x, this.q.y, this.q.h, { + this.transition = new LibTransition(this.btns, v => v.state !== 'normal'); + this.btns.s_img = new LibBtn(this.q.x - this.margin / 2, lib.ui.y + this.hoverArea, this.q.h + this.margin, this.hot_h, 4, this.q.x, this.q.y, this.q.h, { normal: this.q.s_img - }, false, '', () => sMenu.load(this.q.x - this.margin / 2 - (RES_4K ? 22 : 9), ui.y + panel.search.h - SCALE(12)), () => 'History and query syntax help. Ctrl+E focuses search', true, 's_img'); + }, false, '', () => libSMenu.load(this.q.x - this.margin / 2 - (RES._4K ? 22 : 9), lib.ui.y + lib.panel.search.h - SCALE(12)), () => 'History and query syntax help. Ctrl+E focuses search', true, 's_img'); - this.btns.cross2 = new Btn(this.q.x - this.margin / 2, ui.y + this.hoverArea, this.q.h + this.margin, this.hot_h, 5, this.q.x, this.b.y, this.b.h, { + this.btns.cross2 = new LibBtn(this.q.x - this.margin / 2, lib.ui.y + this.hoverArea, this.q.h + this.margin, this.hot_h, 5, this.q.x, this.b.y, this.b.h, { normal: this.cross.normal, hover: this.cross.hover - }, true, '', () => search.clear(), () => panel.search.txt ? 'Clear search text (escape). Double click to show history' : 'No search text to clear', true, 'cross2'); - this.btns.filter = new Btn(ppt.searchShow ? panel.filter.x + this.margin / 2 : panel.filter.x - this.margin / 2, ui.y, ppt.searchShow ? panel.filter.w - this.margin : panel.filter.w + this.margin, panel.search.sp, 6, panel.filter.x, ppt.searchShow ? panel.cc : panel.lc, panel.filter.w, { - normal: ui.col.txt_box, - hover: !ui.id.local ? (!ui.img.blurDark ? ui.col.txt_box_h : ui.col.text) : ui.col.txt_box - }, !ppt.filterShow, '', () => fMenu.load(panel.filter.x - (RES_4K ? -29 : 5), ui.y + panel.search.h - SCALE(12)), () => 'Filter', true, 'filter'); - - this.btns.settings = new Btn(ui.x + this.s.x, ui.y + panel.settings.offset, this.s.w1, panel.search.sp, 7, this.s.w2, panel.search.sp, panel.settings.y, { - normal: ui.col.txt_box, - hover: !ui.id.local ? (!ui.img.blurDark ? ui.col.txt_box_h : ui.col.text) : ui.col.txt_box - }, !ppt.settingsShow, '', () => men.rbtn_up(ui.x + this.s.x - (RES_4K ? 21 : 28), ui.y + panel.search.h - SCALE(12), true), () => 'Settings', true, 'settings'); - - this.btns.cross1 = new Btn(this.b.x - this.margin / 2, ui.y + this.hoverArea, this.q.h + this.margin, this.hot_h, 5, this.b.x, this.b.y, this.b.h, { + }, true, '', () => lib.search.clear(), () => lib.panel.search.txt ? 'Clear search text (escape). Double click to show history' : 'No search text to clear', true, 'cross2'); + this.btns.filter = new LibBtn(libSet.searchShow ? lib.panel.filter.x + this.margin / 2 : lib.panel.filter.x - this.margin / 2, lib.ui.y, libSet.searchShow ? lib.panel.filter.w - this.margin : lib.panel.filter.w + this.margin, lib.panel.search.sp, 6, lib.panel.filter.x, libSet.searchShow ? lib.panel.cc : lib.panel.lc, lib.panel.filter.w, { + normal: lib.ui.col.txt_box, + hover: !lib.ui.id.local ? (!lib.ui.img.blurDark ? lib.ui.col.txt_box_h : lib.ui.col.text) : lib.ui.col.txt_box + }, !libSet.filterShow, '', () => libFMenu.load(lib.panel.filter.x - (RES._4K ? -29 : 5), lib.ui.y + lib.panel.search.h - SCALE(12)), () => 'Filter', true, 'filter'); + + this.btns.settings = new LibBtn(lib.ui.x + this.s.x, lib.ui.y + lib.panel.settings.offset, this.s.w1, lib.panel.search.sp, 7, this.s.w2, lib.panel.search.sp, lib.panel.settings.y, { + normal: lib.ui.col.txt_box, + hover: !lib.ui.id.local ? (!lib.ui.img.blurDark ? lib.ui.col.txt_box_h : lib.ui.col.text) : lib.ui.col.txt_box + }, !libSet.settingsShow, '', () => lib.men.rbtn_up(lib.ui.x + this.s.x - (RES._4K ? 21 : 28), lib.ui.y + lib.panel.search.h - SCALE(12), true), () => 'Settings', true, 'settings'); + + this.btns.cross1 = new LibBtn(this.b.x - this.margin / 2, lib.ui.y + this.hoverArea, this.q.h + this.margin, this.hot_h, 5, this.b.x, this.b.y, this.b.h, { normal: this.cross.normal, hover: this.cross.hover - }, !ppt.searchShow || ppt.filterShow || ppt.settingsShow, '', () => search.clear(), () => panel.search.txt ? 'Clear search text (escape)' : 'No search text to clear', true, 'cross1'); + }, !libSet.searchShow || libSet.filterShow || libSet.settingsShow, '', () => lib.search.clear(), () => lib.panel.search.txt ? 'Clear search text (escape)' : 'No search text to clear', true, 'cross1'); this.setSearchBtnsHide(); } @@ -383,14 +383,14 @@ class Buttons { } tt(n, force) { - if (tooltipLib.Text === n && !force) return; - pop.checkTooltipFont('btn'); - tooltipLib.Text = n; - tooltipLib.Activate(); + if (libTooltip.Text === n && !force) return; + lib.pop.checkTooltipFont('btn'); + libTooltip.Text = n; + libTooltip.Activate(); } } -class Btn { +class LibBtn { constructor(x, y, w, h, type, p1, p2, p3, item, hide, l_dn, l_up, tiptext, hand, name) { this.x = x; this.y = y; @@ -404,7 +404,7 @@ class Btn { this.hide = hide; this.l_dn = l_dn; this.l_up = l_up; - this.tt = new Tooltip(); + this.tt = new LibTooltip(); this.tiptext = tiptext; this.hand = hand; this.name = name; @@ -427,8 +427,8 @@ class Btn { this.drawScrollBtn(gr); break; case 3: - ui.theme.SetPartAndStateID(1, this.item[this.state]); - ui.theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h); + lib.ui.theme.SetPartAndStateID(1, this.item[this.state]); + lib.ui.theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h); break; case 4: this.drawSearch(gr); @@ -446,11 +446,11 @@ class Btn { } drawCross(gr) { - const a = !ui.id.local ? panel.search.txt ? (this.state !== 'down' ? Math.min(200 + (255 - 100) * this.transition_factor, 255) : 255) : 100 : 255; - const crossIm = this.state === 'normal' || !panel.search.txt ? this.item.normal : this.item.hover; - const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4; + const a = !lib.ui.id.local ? lib.panel.search.txt ? (this.state !== 'down' ? Math.min(200 + (255 - 100) * this.transition_factor, 255) : 255) : 100 : 255; + const crossIm = this.state === 'normal' || !lib.panel.search.txt ? this.item.normal : this.item.hover; + // const colRect = this.state !== 'down' ? lib.ui.getBlend(lib.ui.col.bg4, lib.ui.col.bg5, this.transition_factor, true) : lib.ui.col.bg4; gr.SetSmoothingMode(2); - // gr.FillRoundRect(this.x, this.y, this.w, this.h, but.arc, but.arc, colRect); // Hover effect + // gr.FillRoundRect(this.x, this.y, this.w, this.h, lib.but.arc, lib.but.arc, colRect); // Hover effect gr.SetSmoothingMode(0); gr.SetInterpolationMode(2); if (crossIm) gr.DrawImage(crossIm, this.p1, this.p2 - SCALE(2), this.p3, this.p3, 0, 0, crossIm.Width, crossIm.Height, 0, a); @@ -458,60 +458,60 @@ class Btn { } drawFilter(gr) { - const colText = !ui.id.local ? (this.state !== 'down' ? ui.getBlend(this.item.hover, this.item.normal, this.transition_factor, true) : this.item.hover) : this.item.normal; - const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4; + // const colText = !lib.ui.id.local ? (this.state !== 'down' ? lib.ui.getBlend(this.item.hover, this.item.normal, this.transition_factor, true) : this.item.hover) : this.item.normal; + // const colRect = this.state !== 'down' ? lib.ui.getBlend(lib.ui.col.bg4, lib.ui.col.bg5, this.transition_factor, true) : lib.ui.col.bg4; gr.SetSmoothingMode(2); - // gr.FillRoundRect(this.x, ui.y + but.hoverArea, this.w, but.hot_h, but.arc, but.arc, colRect); // Hover effect + // gr.FillRoundRect(this.x, lib.ui.y + lib.but.hoverArea, this.w, lib.but.hot_h, lib.but.arc, lib.but.arc, colRect); // Hover effect gr.SetSmoothingMode(0); - if (!ui.img.blurDark) gr.GdiDrawText(panel.filter.mode[ppt.filterBy].name, panel.filter.font, ui.col.filterBtn, this.p1 + (RES_4K ? 22 : 9), ui.y, this.p3, this.h, this.p2); + if (!lib.ui.img.blurDark) gr.GdiDrawText(lib.panel.filter.mode[libSet.filterBy].name, lib.panel.filter.font, lib.ui.col.filterBtn, this.p1 + (RES._4K ? 22 : 9), lib.ui.y, this.p3, this.h, this.p2); else { gr.SetTextRenderingHint(5); - gr.DrawString(panel.filter.mode[ppt.filterBy].name, panel.filter.font, ui.col.filterBtn, this.p1 + (RES_4K ? 21 : 8), ui.y - 1, this.p3, this.h, StringFormat(1, 1)); + gr.DrawString(lib.panel.filter.mode[libSet.filterBy].name, lib.panel.filter.font, lib.ui.col.filterBtn, this.p1 + (RES._4K ? 21 : 8), lib.ui.y - 1, this.p3, this.h, StringFormat(1, 1)); } } drawScrollBtn(gr) { - const a = this.state !== 'down' ? Math.min(but.alpha[0] + (but.alpha[1] - but.alpha[0]) * this.transition_factor, but.alpha[1]) : but.alpha[2]; + const a = this.state !== 'down' ? Math.min(lib.but.alpha[0] + (lib.but.alpha[1] - lib.but.alpha[0]) * this.transition_factor, lib.but.alpha[1]) : lib.but.alpha[2]; switch (true) { - case but.vertical: - if (this.state !== 'normal' && ui.sbar.type == 1) gr.FillSolidRect(sbar.x, this.y + (this.type == 1 ? but.scr.hotOffset - panel.sbar_o : 0), sbar.w, this.h - but.scr.hotOffset + panel.sbar_o, ui.col.bg); - if (but.scr.opaque && but.scr.bg) gr.DrawImage(but.scr.bg, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, but.scr.bg.Width, but.scr.bg.Height, this.type == 1 ? 0 : 180); - if (but.scr.img) gr.DrawImage(but.scr.img, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, but.scr.img.Width, but.scr.img.Height, this.type == 1 ? 0 : 180, a); + case lib.but.vertical: + if (this.state !== 'normal' && lib.ui.sbar.type == 1) gr.FillSolidRect(lib.sbar.x, this.y + (this.type == 1 ? lib.but.scr.hotOffset - lib.panel.sbar_o : 0), lib.sbar.w, this.h - lib.but.scr.hotOffset + lib.panel.sbar_o, lib.ui.col.bg); + if (lib.but.scr.opaque && lib.but.scr.bg) gr.DrawImage(lib.but.scr.bg, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, lib.but.scr.bg.Width, lib.but.scr.bg.Height, this.type == 1 ? 0 : 180); + if (lib.but.scr.img) gr.DrawImage(lib.but.scr.img, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, lib.but.scr.img.Width, lib.but.scr.img.Height, this.type == 1 ? 0 : 180, a); break; - case !but.vertical: - if (this.state !== 'normal' && ui.sbar.type == 1) gr.FillSolidRect(this.x + (this.type == 1 ? but.scr.hotOffset - panel.sbar_o : 0), sbar.y, this.w - but.scr.hotOffset + panel.sbar_o, sbar.h, ui.col.bg); - if (but.scr.opaque && but.scr.bg) gr.DrawImage(but.scr.bg, this.p2, this.y + this.p1, this.p3, this.p3, 0, 0, but.scr.bg.Width, but.scr.bg.Height, this.type == 1 ? 270 : 90); - if (but.scr.img) gr.DrawImage(but.scr.img, this.p2, this.y + this.p1, this.p3, this.p3, 0, 0, but.scr.img.Width, but.scr.img.Height, this.type == 1 ? 270 : 90, a); + case !lib.but.vertical: + if (this.state !== 'normal' && lib.ui.sbar.type == 1) gr.FillSolidRect(this.x + (this.type == 1 ? lib.but.scr.hotOffset - lib.panel.sbar_o : 0), lib.sbar.y, this.w - lib.but.scr.hotOffset + lib.panel.sbar_o, lib.sbar.h, lib.ui.col.bg); + if (lib.but.scr.opaque && lib.but.scr.bg) gr.DrawImage(lib.but.scr.bg, this.p2, this.y + this.p1, this.p3, this.p3, 0, 0, lib.but.scr.bg.Width, lib.but.scr.bg.Height, this.type == 1 ? 270 : 90); + if (lib.but.scr.img) gr.DrawImage(lib.but.scr.img, this.p2, this.y + this.p1, this.p3, this.p3, 0, 0, lib.but.scr.img.Width, lib.but.scr.img.Height, this.type == 1 ? 270 : 90, a); break; } } drawSearch(gr) { - const a = !ui.id.local ? (this.state !== 'down' ? Math.min(255 + (240 - 180) * this.transition_factor, 240) : 240) : 255; - // const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4; + const a = !lib.ui.id.local ? (this.state !== 'down' ? Math.min(255 + (240 - 180) * this.transition_factor, 240) : 240) : 255; + // const colRect = this.state !== 'down' ? lib.ui.getBlend(lib.ui.col.bg4, lib.ui.col.bg5, this.transition_factor, true) : lib.ui.col.bg4; // gr.SetSmoothingMode(2); - // gr.FillRoundRect(this.x, ui.y + SCALE(6), this.w, this.h, but.arc, but.arc, colRect); // Hover effect + // gr.FillRoundRect(this.x, lib.ui.y + SCALE(6), this.w, this.h, lib.but.arc, lib.but.arc, colRect); // Hover effect // gr.SetSmoothingMode(0); gr.SetInterpolationMode(2); - if (this.item.normal) gr.DrawImage(this.item.normal, this.p1, ui.y + SCALE(18), this.p3, this.p3, 0, 0, this.item.normal.Width, this.item.normal.Height, 0, a); + if (this.item.normal) gr.DrawImage(this.item.normal, this.p1, lib.ui.y + SCALE(18), this.p3, this.p3, 0, 0, this.item.normal.Width, this.item.normal.Height, 0, a); // gr.SetInterpolationMode(0); // Causes ugly rendering of lower bar flags when switching to Library } drawSettings(gr) { - const colText = !ui.id.local ? (this.state !== 'down' ? ui.getBlend(this.item.hover, this.item.normal, this.transition_factor, true) : this.item.hover) : this.item.normal; - const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4; + // const colText = !lib.ui.id.local ? (this.state !== 'down' ? lib.ui.getBlend(this.item.hover, this.item.normal, this.transition_factor, true) : this.item.hover) : this.item.normal; + // const colRect = this.state !== 'down' ? lib.ui.getBlend(lib.ui.col.bg4, lib.ui.col.bg5, this.transition_factor, true) : lib.ui.col.bg4; gr.SetSmoothingMode(2); - // gr.FillRoundRect(this.x, ui.y + but.hoverArea, this.w, but.hot_h, but.arc, but.arc, colRect); // Hover effect + // gr.FillRoundRect(this.x, lib.ui.y + lib.but.hoverArea, this.w, lib.but.hot_h, lib.but.arc, lib.but.arc, colRect); // Hover effect gr.SetSmoothingMode(0); - if (!ui.img.blurDark) gr.GdiDrawText(panel.settings.icon, panel.settings.font, ui.col.settingsBtn, ui.x + (RES_4K ? 1 : 0), ui.y + SCALE(2), this.p1, this.p2, panel.rc); + if (!lib.ui.img.blurDark) gr.GdiDrawText(lib.panel.settings.icon, lib.panel.settings.font, lib.ui.col.settingsBtn, lib.ui.x + (RES._4K ? 1 : 0), lib.ui.y + SCALE(2), this.p1, this.p2, lib.panel.rc); else { gr.SetTextRenderingHint(5); - gr.DrawString(panel.settings.icon, panel.settings.font, ui.col.settingsBtn, ui.x + (RES_4K ? 1 : 0), ui.y + SCALE(2) - 1, this.p1, this.p2, StringFormat(2, 1)); + gr.DrawString(lib.panel.settings.icon, lib.panel.settings.font, lib.ui.col.settingsBtn, lib.ui.x + (RES._4K ? 1 : 0), lib.ui.y + SCALE(2) - 1, this.p1, this.p2, StringFormat(2, 1)); } } lbtn_dn(x, y) { - if (!but.Dn) return; + if (!lib.but.Dn) return; this.l_dn && this.l_dn(x, y); } @@ -526,55 +526,55 @@ class Btn { } trace(x, y) { - but.trace = !this.hide && x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; - return but.trace; + lib.but.trace = !this.hide && x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; + return lib.but.trace; } } -class Tooltip { +class LibTooltip { constructor() { this.id = Math.ceil(Math.random().toFixed(8) * 1000); - this.tt_timer = new TooltipTimerLib(); + this.tt_timer = new LibTooltipTimer(); } // * METHODS * // clear() { - styledTooltipReady = false; + grm.ui.styledTooltipReady = false; this.tt_timer.stop(this.id); } show(text) { - if (Date.now() - but.tooltipLib.start > 2000 && but.tooltipLib.delay) this.showDelayed(text); + if (Date.now() - lib.but.tooltipLib.start > 2000 && lib.but.tooltipLib.delay) this.showDelayed(text); else this.showImmediate(text); - but.tooltipLib.start = Date.now(); + lib.but.tooltipLib.start = Date.now(); } showDelayed(text) { - styledTooltipText = text; - styledTooltipReady = true; - if (!pref.showStyledTooltips) { + grm.ui.styledTooltipText = text; + grm.ui.styledTooltipReady = true; + if (!grSet.showStyledTooltips) { this.tt_timer.start(this.id, text); } } showImmediate(text) { - styledTooltipText = text; - styledTooltipReady = true; - if (!pref.showStyledTooltips) { + grm.ui.styledTooltipText = text; + grm.ui.styledTooltipReady = true; + if (!grSet.showStyledTooltips) { this.tt_timer.set_id(this.id); this.tt_timer.stop(this.id); - but.tt(text); + lib.but.tt(text); } } stop() { - styledTooltipReady = false; + grm.ui.styledTooltipReady = false; this.tt_timer.forceStop(); } } -class TooltipTimerLib { +class LibTooltipTimer { constructor() { this.delay_timer; this.tt_caller = undefined; @@ -583,8 +583,8 @@ class TooltipTimerLib { // * METHODS * // forceStop() { - but.tooltipLib.delay = true; - but.tt(''); + lib.but.tooltipLib.delay = true; + lib.but.tt(''); if (this.delay_timer) { clearTimeout(this.delay_timer); this.delay_timer = null; @@ -598,12 +598,12 @@ class TooltipTimerLib { start(id, text) { const old_caller = this.tt_caller; this.tt_caller = id; - if (!this.delay_timer && tooltipLib.Text) but.tt(text, old_caller !== this.tt_caller); + if (!this.delay_timer && libTooltip.Text) lib.but.tt(text, old_caller !== this.tt_caller); else { this.forceStop(); if (!this.delay_timer) { this.delay_timer = setTimeout(() => { - but.tt(text); + lib.but.tt(text); this.delay_timer = null; }, 500); } @@ -615,7 +615,7 @@ class TooltipTimerLib { } } -class Transition { +class LibTransition { constructor(items, hover) { this.hover = hover; this.items = items; diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-callbacks.js b/profile/georgia-reborn/scripts/Library/scripts/lib-callbacks.js index 2dfc6565..a70482c8 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-callbacks.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-callbacks.js @@ -1,129 +1,155 @@ 'use strict'; -class LibraryCallbacks { +class LibCallbacks { + constructor() { + /** @type {string} */ + this.windowMetricsPath = grSet.customLibraryDir + ? `${grCfg.customLibraryDir}cache\\library\\themed\\windowMetrics.json` + : `${fb.ProfilePath}cache\\library\\themed\\windowMetrics.json`; + + this.on_queue_changed = $Lib.debounce(() => { + if (libSet.itemShowStatistics != 7) return; + lib.pop.tree.forEach(v => { + v.id = ''; + v.count = ''; + delete v.statistics; + delete v._statistics; + }); + lib.pop.cache = { + standard: {}, + search: {}, + filter: {} + } + lib.panel.treePaint(); + }, 250, { + leading: true, + trailing: true + }); + } + on_colours_changed(keepCache) { - ui.getColours(); + lib.ui.getColours(); - if (panel.colMarker) { - panel.getFields(ppt.viewBy, ppt.filterBy); - if (lib) { - lib.getLibrary(); - lib.rootNodes(true, true); + if (lib.panel.colMarker) { + lib.panel.getFields(libSet.viewBy, libSet.filterBy); + if (lib.lib) { + lib.lib.getLibrary(); + lib.lib.rootNodes(true, true); } } - sbar.setCol(); - pop.createImages(); - but.createImages(); - if (!keepCache) img.clearCache(); - img.createImages(); - but.refresh(true); - sbar.resetAuto(); - ui.createImages(); - if (!ppt.themed) ui.blurReset(); + lib.sbar.setCol(); + lib.pop.createImages(); + lib.but.createImages(); + if (!keepCache) libImg.clearCache(); + libImg.createImages(); + lib.but.refresh(true); + lib.sbar.resetAuto(); + lib.ui.createImages(); + if (!libSet.themed) lib.ui.blurReset(); window.Repaint(); } on_font_changed() { - sbar.logScroll(); - pop.deactivateTooltip(); - ui.getFont(); - panel.on_size(true); - if (ui.style.topBarShow || ppt.sbarShow) but.refresh(true); - sbar.resetAuto(); + lib.sbar.logScroll(); + lib.pop.deactivateTooltip(); + lib.ui.getFont(); + lib.panel.on_size(true); + if (lib.ui.style.topBarShow || libSet.sbarShow) lib.but.refresh(true); + lib.sbar.resetAuto(); window.Repaint(); - sbar.setScroll(); + lib.sbar.setScroll(); } on_char(code) { - pop.on_char(code); - find.on_char(code); - if (!ppt.searchShow) return; - search.on_char(code); + lib.pop.on_char(code); + lib.find.on_char(code); + if (!libSet.searchShow) return; + lib.search.on_char(code); } on_focus(is_focused) { if (!is_focused) { - timer.clear(timer.cursor); - panel.search.cursor = false; - panel.searchPaint(); + lib.timer.clear(lib.timer.cursor); + lib.panel.search.cursor = false; + lib.panel.searchPaint(); } - pop.on_focus(is_focused); + lib.pop.on_focus(is_focused); } on_get_album_art_done(handle, art_id, image, image_path) { - ui.on_get_album_art_done(handle, image, image_path); + lib.ui.on_get_album_art_done(handle, image, image_path); } on_item_focus_change(playlistIndex) { - lib.checkFilter(); - if (!pop.setFocus) { - if (ppt.followPlaylistFocus && playlistIndex == $Lib.pl_active && !ppt.libSource) { - setSelection(fb.GetFocusItem()); + lib.lib.checkFilter(); + if (!lib.pop.setFocus) { + if (libSet.followPlaylistFocus && playlistIndex == $Lib.pl_active && !libSet.libSource) { + this.setSelection(fb.GetFocusItem()); } - } else pop.setFocus = false; - ui.focus_changed(); + } else lib.pop.setFocus = false; + lib.ui.focus_changed(); } on_key_down(vkey) { - pop.on_key_down(vkey); - img.on_key_down(vkey); - if (!ppt.searchShow) return; - search.on_key_down(vkey); + lib.pop.on_key_down(vkey); + libImg.on_key_down(vkey); + if (!libSet.searchShow) return; + lib.search.on_key_down(vkey); } on_key_up(vkey) { - img.on_key_up(vkey); - if (!ppt.searchShow) return; - search.on_key_up(vkey); + libImg.on_key_up(vkey); + if (!libSet.searchShow) return; + lib.search.on_key_up(vkey); } on_library_items_added(handleList) { - if (ppt.libSource == 2) return; - if (lib.v2_init) { - lib.v2_init = false; - if (ui.w < 1 || !window.IsVisible) return; - lib.initialise(handleList); + if (libSet.libSource == 2) return; + if (lib.lib.v2_init) { + lib.lib.v2_init = false; + if (lib.ui.w < 1 || !window.IsVisible) return; + lib.lib.initialise(handleList); return; } - if (!libraryInitialized || !ppt.libAutoSync || ppt.fixedPlaylist || !ppt.libSource) return; - lib.treeState(false, 2, handleList, 0); + if (!lib.initialized || !libSet.libAutoSync || libSet.fixedPlaylist || !libSet.libSource) return; + lib.lib.treeState(false, 2, handleList, 0); } on_library_items_removed(handleList) { - if (!libraryInitialized || !ppt.libAutoSync || ppt.fixedPlaylist || !ppt.libSource) return; - if (ppt.libSource == 2) { - const libList = lib.list.Clone(); + if (!lib.initialized || !libSet.libAutoSync || libSet.fixedPlaylist || !libSet.libSource) return; + if (libSet.libSource == 2) { + const libList = lib.lib.list.Clone(); libList.Sort(); handleList.Sort(); handleList.MakeIntersection(libList); } - lib.treeState(false, 2, handleList, 2); + lib.lib.treeState(false, 2, handleList, 2); } on_library_items_changed(handleList) { - if (!libraryInitialized || !ppt.libAutoSync || ppt.fixedPlaylist || !ppt.libSource) return; - if (ppt.libSource == 2) { - const libList = lib.list.Clone(); + if (!lib.initialized || !libSet.libAutoSync || libSet.fixedPlaylist || !libSet.libSource) return; + if (libSet.libSource == 2) { + const libList = lib.lib.list.Clone(); libList.Sort(); handleList.Sort(); handleList.MakeIntersection(libList); } - lib.treeState(false, 2, handleList, 1); + lib.lib.treeState(false, 2, handleList, 1); } on_main_menu(index) { - pop.on_main_menu(index); + lib.pop.on_main_menu(index); } on_metadb_changed(handleList, isDatabase) { - if (isDatabase && !panel.statistics || lib.list.Count != lib.libNode.length) return; - if (ppt.fixedPlaylist || !ppt.libSource) { + if (isDatabase && !lib.panel.statistics || lib.lib.list.Count != lib.lib.libNode.length) return; + if (libSet.fixedPlaylist || !libSet.libSource) { handleList.Convert().some(h => { - const i = lib.full_list.Find(h); + const i = lib.lib.full_list.Find(h); if (i != -1) { - const isMainChanged = lib.isMainChanged(handleList); - if (isMainChanged) lib.treeState(false, 2); - ui.focus_changed(); + const isMainChanged = lib.lib.isMainChanged(handleList); + if (isMainChanged) lib.lib.treeState(false, 2); + lib.ui.focus_changed(); return true; } }); @@ -131,324 +157,255 @@ class LibraryCallbacks { } on_mouse_lbtn_dblclk(x, y) { - but.lbtn_dn(x, y); - if (ppt.searchShow) search.lbtn_dblclk(x, y); - pop.lbtn_dblclk(x, y); - sbar.lbtn_dblclk(x, y); + lib.but.lbtn_dn(x, y); + if (libSet.searchShow) lib.search.lbtn_dblclk(x, y); + lib.pop.lbtn_dblclk(x, y); + lib.sbar.lbtn_dblclk(x, y); } on_mouse_lbtn_down(x, y) { - if (ppt.touchControl) { - panel.last_pressed_coord = { + if (libSet.touchControl) { + lib.panel.last_pressed_coord = { x, y }; } - if (ui.style.topBarShow || ppt.sbarShow) but.lbtn_dn(x, y); - if (ppt.searchShow) search.lbtn_dn(x, y); - pop.lbtn_dn(x, y); - sbar.lbtn_dn(x, y); - ui.sz.y_start = y; + if (lib.ui.style.topBarShow || libSet.sbarShow) lib.but.lbtn_dn(x, y); + if (libSet.searchShow) lib.search.lbtn_dn(x, y); + lib.pop.lbtn_dn(x, y); + lib.sbar.lbtn_dn(x, y); + lib.ui.sz.y_start = y; } on_mouse_lbtn_up(x, y) { - pop.lbtn_up(x, y); - if (ppt.searchShow) search.lbtn_up(); - but.lbtn_up(x, y); - sbar.lbtn_up(); + lib.pop.lbtn_up(x, y); + if (libSet.searchShow) lib.search.lbtn_up(); + lib.but.lbtn_up(x, y); + lib.sbar.lbtn_up(); } on_mouse_leave() { - if (ui.style.topBarShow || ppt.sbarShow) but.leave(); - sbar.leave(); - pop.leave(); + if (lib.ui.style.topBarShow || libSet.sbarShow) lib.but.leave(); + lib.sbar.leave(); + lib.pop.leave(); } on_mouse_mbtn_dblclk(x, y, mask) { - pop.mbtnDblClickOrAltDblClick(x, y, mask, 'mbtn'); + lib.pop.mbtnDblClickOrAltDblClick(x, y, mask, 'mbtn'); } on_mouse_mbtn_down(x, y) { - pop.mbtn_dn(x, y); + lib.pop.mbtn_dn(x, y); } on_mouse_mbtn_up(x, y, mask) { // UIHacks at default settings blocks on_mouse_mbtn_up, at least in windows; workaround configure hacks: main window > move with > caption only & ensure pseudo-caption doesn't overlap buttons - pop.mbtnUpOrAltClickUp(x, y, mask, 'mbtn'); + lib.pop.mbtnUpOrAltClickUp(x, y, mask, 'mbtn'); } on_mouse_move(x, y) { - if (panel.m.x == x && panel.m.y == y) return; - pop.hand = false; - if (ui.style.topBarShow || ppt.sbarShow) but.move(x, y); - if (ppt.searchShow) search.move(x, y); - if (pref.libraryRowHover) pop.move(x, y); - pop.dragDrop(x, y); - sbar.move(x, y); - ui.zoomDrag(x, y); - panel.m.x = x; - panel.m.y = y; + if (lib.panel.m.x == x && lib.panel.m.y == y) return; + lib.pop.hand = false; + if (lib.ui.style.topBarShow || libSet.sbarShow) lib.but.move(x, y); + if (libSet.searchShow) lib.search.move(x, y); + if (grSet.libraryRowHover) lib.pop.move(x, y); + lib.pop.dragDrop(x, y); + lib.sbar.move(x, y); + lib.ui.zoomDrag(x, y); + lib.panel.m.x = x; + lib.panel.m.y = y; } on_mouse_rbtn_up(x, y) { - if (y < ui.y + panel.search.h && x > panel.search.x && x < panel.search.x + panel.search.w) { - if (ppt.searchShow) search.rbtn_up(x, y); - } else men.rbtn_up(x, y); + if (y < lib.ui.y + lib.panel.search.h && x > lib.panel.search.x && x < lib.panel.search.x + lib.panel.search.w) { + if (libSet.searchShow) lib.search.rbtn_up(x, y); + } else lib.men.rbtn_up(x, y); return true; } on_mouse_wheel(step) { - pop.deactivateTooltip(); - if (!vk.k('zoom')) sbar.wheel(step); - else ui.wheel(step); + lib.pop.deactivateTooltip(); + if (!lib.vk.k('zoom')) lib.sbar.wheel(step); + else lib.ui.wheel(step); } on_notify_data(name, info) { - if (ppt.libSource == 2 && name != 'bio_imgChange') { - const panelSelectionPlaylists = ppt.panelSelectionPlaylist.split(/\s*\|\s*/); + if (libSet.libSource == 2 && name != 'bio_imgChange') { + const panelSelectionPlaylists = libSet.panelSelectionPlaylist.split(/\s*\|\s*/); panelSelectionPlaylists.some(v => { if (name == v) { - lib.list = new FbMetadbHandleList(info); - if ($Lib.equalHandles(lib.list.Convert(), lib.full_list.Convert())) return; - lib.full_list = lib.list.Clone(); - ppt.lastPanelSelectionPlaylist = `${v} Cache`; + lib.lib.list = new FbMetadbHandleList(info); + if ($Lib.equalHandles(lib.lib.list.Convert(), lib.lib.full_list.Convert())) return; + lib.lib.full_list = lib.lib.list.Clone(); + libSet.lastPanelSelectionPlaylist = `${v} Cache`; const pln = plman.FindOrCreatePlaylist(`${v} Cache`, false); plman.ClearPlaylist(pln); - plman.InsertPlaylistItems(pln, 0, lib.list); - lib.searchCache = {}; - pop.clearTree(); - pop.cache = { + plman.InsertPlaylistItems(pln, 0, lib.lib.list); + lib.lib.searchCache = {}; + lib.pop.clearTree(); + lib.pop.cache = { standard: {}, search: {}, filter: {} } - lib.treeState(false, 2, null, 3); - ui.expandHandle = lib.list.Count ? lib.list[0] : null; - ui.on_playback_new_track(); - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, 2, null, 3); + lib.ui.expandHandle = lib.lib.list.Count ? lib.lib.list[0] : null; + lib.ui.on_playback_new_track(); + lib.lib.treeState(false, libSet.rememberTree); } }); } switch (name) { case '!!.tags update': - lib.treeState(false, 2); + lib.lib.treeState(false, 2); break; case 'newThemeColours': - if (!ppt.themed) break; - ppt.theme = info.theme; - ppt.themeBgImage = info.themeBgImage; - ppt.themeColour = info.themeColour; + if (!libSet.themed) break; + libSet.theme = info.theme; + libSet.themeBgImage = info.themeBgImage; + libSet.themeColour = info.themeColour; on_colours_changed(true); break; case 'Sync col': { - if (!ppt.themed) break; - const themeLight = ppt.themeLight; + if (!libSet.themed) break; + const themeLight = libSet.themeLight; if (themeLight != info.themeLight) { - ppt.themeLight = info.themeLight; + libSet.themeLight = info.themeLight; on_colours_changed(true); } break; } case 'Sync image': - if (!ppt.themed) break; - sync.image(new GdiBitmap(info.image), info.id); + if (!libSet.themed) break; + libSync.image(new GdiBitmap(info.image), info.id); break; } - if (ui.id.local && name.startsWith('opt_')) { + if (lib.ui.id.local && name.startsWith('opt_')) { const clone = typeof info === 'string' ? String(info) : info; on_notify(name, clone); } } - // on_paint(gr) { - // if (!lib.initialised) { - // lib.initialise(); - // } - // ui.draw(gr); - // lib.checkTree(); - // img.draw(gr); - // ui.drawLine(gr); - // search.draw(gr); - // pop.draw(gr); - // sbar.draw(gr); - // but.draw(gr); - // find.draw(gr); - // } + on_paint(gr) { + if (!lib.lib.initialised) { + lib.lib.initialise(); + } + lib.ui.draw(gr); + lib.lib.checkTree(); + libImg.draw(gr); + lib.ui.drawLine(gr); + lib.search.draw(gr); + lib.pop.draw(gr); + lib.sbar.draw(gr); + lib.but.draw(gr); + lib.find.draw(gr); + + if (libSet.albumArtFlowMode && lib.panel.imgView) { + gr.FillSolidRect(this.x, this.y, SCALE(20), this.h, lib.ui.col.bg); // Margin left and masking for horizontal flow mode + gr.FillSolidRect(this.x + this.w - SCALE(20), this.y, SCALE(20), this.h, lib.ui.col.bg); // Margin right and masking for horizontal flow mode + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) { + gr.DrawImage(grCol.imgBlended, this.x - this.w + SCALE(20), this.y, grm.ui.ww, grm.ui.wh, this.x - this.w + SCALE(20), this.y, grCol.imgBlended.Width, grCol.imgBlended.Height); + gr.DrawImage(grCol.imgBlended, this.x + this.w - SCALE(20), this.y, grm.ui.ww, grm.ui.wh, this.x + this.w - SCALE(20), this.y, grCol.imgBlended.Width, grCol.imgBlended.Height); + } + } + + gr.FillSolidRect(this.x, 0, this.w, grm.ui.topMenuHeight, grCol.bg); // Hides top row that shouldn't be visible in album art mode + gr.FillSolidRect(this.x, this.y + this.h, this.w, this.h, grCol.bg); // Hides bottom row that shouldn't be visible in album art mode + if (UIHacks.Aero.Effect === 2) gr.DrawLine(this.x, 0, grm.ui.ww, 0, 1, grCol.bg); // UIHacks aero glass shadow frame fix - needed for style Blend + + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) { + gr.DrawImage(grCol.imgBlended, this.x, this.y - this.h - grm.ui.topMenuHeight - grm.ui.lowerBarHeight, grm.ui.ww, grm.ui.wh, this.x, this.y - this.h - grm.ui.topMenuHeight - grm.ui.lowerBarHeight, grCol.imgBlended.Width, grCol.imgBlended.Height); + gr.DrawImage(grCol.imgBlended, this.x, this.y + this.h, grm.ui.ww, grm.ui.wh, this.x, this.y + this.h, grCol.imgBlended.Width, grCol.imgBlended.Height); + } + } on_playback_new_track(handle) { - lib.checkFilter(); - pop.getNowplaying(handle); - if (pref.libraryAutoScrollNowPlaying) pop.nowPlayingShow(); - if (!ppt.recItemImage || ppt.libSource != 2) ui.on_playback_new_track(handle); + lib.lib.checkFilter(); + lib.pop.getNowplaying(handle); + if (grSet.libraryAutoScrollNowPlaying) lib.pop.nowPlayingShow(); + if (!libSet.recItemImage || libSet.libSource != 2) lib.ui.on_playback_new_track(handle); } on_playback_stop(reason) { if (reason == 2) return; - pop.getNowplaying('', true); + lib.pop.getNowplaying('', true); on_item_focus_change(); } on_playback_queue_changed() { - on_queue_changed(); + this.on_queue_changed(); } on_playlists_changed() { - men.playlists_changed(); + lib.men.playlists_changed(); if ($Lib.pl_active != plman.ActivePlaylist) $Lib.pl_active = plman.ActivePlaylist; let fixedPlaylistIndex = -1; - if (ppt.fixedPlaylist) { - fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName); + if (libSet.fixedPlaylist) { + fixedPlaylistIndex = plman.FindPlaylist(libSet.fixedPlaylistName); if (fixedPlaylistIndex == -1) { - ppt.fixedPlaylist = false; - ppt.libSource = 0; - if (panel.imgView) img.clearCache(); - lib.playlist_update(); + libSet.fixedPlaylist = false; + libSet.libSource = 0; + if (lib.panel.imgView) libImg.clearCache(); + lib.lib.playlist_update(); } } } on_playlist_items_added(playlistIndex) { - if (ppt.fixedPlaylist) { - const fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName); + if (libSet.fixedPlaylist) { + const fixedPlaylistIndex = plman.FindPlaylist(libSet.fixedPlaylistName); if (playlistIndex == fixedPlaylistIndex) { - lib.playlist_update(playlistIndex); + lib.lib.playlist_update(playlistIndex); return; } } - if (!ppt.libSource && playlistIndex == $Lib.pl_active) { - lib.playlist_update(playlistIndex); + if (!libSet.libSource && playlistIndex == $Lib.pl_active) { + lib.lib.playlist_update(playlistIndex); } - if (!playlist) return; // Abort if Playlist was not initialized - if (pref.playlistSortOrderAuto) setPlaylistSortOrder(); - initPlaylist(); // Update Playlist when adding items from Library - playlist.on_size(ww, wh); + if (!pl.playlist) return; // Abort if Playlist was not initialized + if (grSet.playlistSortOrderAuto) grm.ui.setPlaylistSortOrder(); + grm.ui.initPlaylist(); // Update Playlist when adding items from Library + pl.call.on_size(grm.ui.ww, grm.ui.wh); } on_playlist_items_removed(playlistIndex) { - if (ppt.fixedPlaylist) { - const fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName); + if (libSet.fixedPlaylist) { + const fixedPlaylistIndex = plman.FindPlaylist(libSet.fixedPlaylistName); if (playlistIndex == fixedPlaylistIndex) { - lib.playlist_update(playlistIndex); + lib.lib.playlist_update(playlistIndex); return; } } - if (!ppt.libSource && playlistIndex == $Lib.pl_active) { - lib.playlist_update(playlistIndex); + if (!libSet.libSource && playlistIndex == $Lib.pl_active) { + lib.lib.playlist_update(playlistIndex); } } on_playlist_items_reordered(playlistIndex) { - if (!ppt.libSource && playlistIndex == $Lib.pl_active) { - lib.playlist_update(playlistIndex); + if (!libSet.libSource && playlistIndex == $Lib.pl_active) { + lib.lib.playlist_update(playlistIndex); } } on_playlist_switch() { $Lib.pl_active = plman.ActivePlaylist; - if (!ppt.libSource) { - lib.playlist_update(); + if (!libSet.libSource) { + lib.lib.playlist_update(); } - ui.focus_changed(); + lib.ui.focus_changed(); } on_script_unload() { - but.on_script_unload(); - pop.deactivateTooltip(); + lib.but.on_script_unload(); + lib.pop.deactivateTooltip(); } on_selection_changed() { - if (!panel.setSelection()) return; - setSelection(fb.GetSelection()); - } - - // on_size() { - // ui.w = window.Width; - // ui.h = window.Height; - // if (!ui.w || !ui.h) return; - - // pop.deactivateTooltip(); - // tooltipLib.SetMaxWidth(Math.max(ui.w, SCALE(pref.layout !== 'default' ? 600 : 800))); - // ui.blurReset(); - // ui.calcText(true) - - // if (ppt.themed && ppt.theme) { - // const themed_image = `${fb.ProfilePath}settings\\themed\\themed_image.bmp`; - // if ($Lib.file(themed_image)) sync.image(gdi.Image(themed_image)); - // } - - // panel.on_size(); - // if (ui.style.topBarShow || ppt.sbarShow) but.refresh(true); - // sbar.resetAuto(); - // find.on_size(); - // but.createImages(); - // pop.createImages(); - - // if (!ppt.themed) return; - // const windowMetrics = $Lib.jsonParse(windowMetricsPath, {}, 'file'); - // windowMetrics[window.Name] = { - // w: ui.w, - // h: ui.h - // } - // $Lib.save(windowMetricsPath, JSON.stringify(windowMetrics, null, 3), true); - // } - - setSelection(handle) { - if (!handle || !panel.list.Count) return; - const item = panel.list.Find(handle); - let idx = -1; - pop.tree.forEach((v, i) => { - if (!v.root && pop.inRange(item, v.item)) idx = i; - }); - if (idx != -1) { - if (!panel.imgView) pop.focusShow(idx); - else pop.showItem(idx, 'focus'); - } - } -} - - -class LibraryPanel { - constructor() { - this.x = -1; // not set - this.y = -1; // not set - this.w = -1; // not set - this.h = -1; // not set - } - - on_paint(gr) { - if (!lib.initialised) { - lib.initialise(); - } - ui.draw(gr); - lib.checkTree(); - img.draw(gr); - ui.drawLine(gr); - search.draw(gr); - pop.draw(gr); - sbar.draw(gr); - but.draw(gr); - find.draw(gr); - - if (ppt.albumArtFlowMode && panel.imgView) { - gr.FillSolidRect(this.x, this.y, SCALE(20), this.h, ui.col.bg); // Margin left and masking for horizontal flow mode - gr.FillSolidRect(this.x + this.w - SCALE(20), this.y, SCALE(20), this.h, ui.col.bg); // Margin right and masking for horizontal flow mode - if (pref.styleBlend && albumArt && blendedImg) { - gr.DrawImage(blendedImg, this.x - this.w + SCALE(20), this.y, ww, wh, this.x - this.w + SCALE(20), this.y, blendedImg.Width, blendedImg.Height); - gr.DrawImage(blendedImg, this.x + this.w - SCALE(20), this.y, ww, wh, this.x + this.w - SCALE(20), this.y, blendedImg.Width, blendedImg.Height); - } - } - - gr.FillSolidRect(this.x, 0, this.w, geo.topMenuHeight, col.bg); // Hides top row that shouldn't be visible in album art mode - gr.FillSolidRect(this.x, this.y + this.h, this.w, this.h, col.bg); // Hides bottom row that shouldn't be visible in album art mode - if (UIHacks.Aero.Effect === 2) gr.DrawLine(this.x, 0, ww, 0, 1, col.bg); // UIHacks aero glass shadow frame fix - needed for style Blend - - if (pref.styleBlend && albumArt && blendedImg) { - gr.DrawImage(blendedImg, this.x, this.y - this.h - geo.topMenuHeight - geo.lowerBarHeight, ww, wh, this.x, this.y - this.h - geo.topMenuHeight - geo.lowerBarHeight, blendedImg.Width, blendedImg.Height); - gr.DrawImage(blendedImg, this.x, this.y + this.h, ww, wh, this.x, this.y + this.h, blendedImg.Width, blendedImg.Height); - } + if (!lib.panel.setSelection()) return; + this.setSelection(fb.GetSelection()); } on_size(x, y, width, height) { @@ -456,112 +413,58 @@ class LibraryPanel { this.y = y; this.w = width; this.h = height; - ui.x = x; - ui.y = y; - ui.w = width; - ui.h = height; + lib.ui.x = x; + lib.ui.y = y; + lib.ui.w = width; + lib.ui.h = height; - if (!ui.w || !ui.h) return; - if (panel.imgView) autoThumbnailSize(); + if (!lib.ui.w || !lib.ui.h) return; + if (lib.panel.imgView) grm.ui.autoThumbnailSize(); // * Set guard for fixed Library margin sizes in case user changed them in Library options - ppt.margin = SCALE(20); - ppt.verticalPad = 5; // Setup default line padding value needed, otherwise 0 on reset - ppt.zoomNode = 100; // Sets correct node zoom value, i.e when switching to 4K - panel.setTopBar(); // Resets filter font in case the zoom was reset, also needed when changing font size - - pop.deactivateTooltip(); - tooltipLib.SetMaxWidth(Math.max(ui.w, SCALE(pref.layout !== 'default' ? 600 : 800))); - ui.blurReset(); - ui.calcText(true); - - if (ppt.themed && ppt.theme) { - const themed_image = pref.customLibraryDir ? `${globals.customLibraryDir}cache\\library\\themed\\themed_image.bmp` : `${fb.ProfilePath}cache\\library\\themed\\themed_image.bmp`; - if ($Lib.file(themed_image)) sync.image(gdi.Image(themed_image)); + libSet.margin = SCALE(20); + libSet.verticalPad = 5; // Setup default line padding value needed, otherwise 0 on reset + libSet.zoomNode = 100; // Sets correct node zoom value, i.e when switching to 4K + lib.panel.setTopBar(); // Resets filter font in case the zoom was reset, also needed when changing font size + + lib.pop.deactivateTooltip(); + libTooltip.SetMaxWidth(Math.max(lib.ui.w, SCALE(grSet.layout !== 'default' ? 600 : 800))); + lib.ui.blurReset(); + lib.ui.calcText(true); + + if (libSet.themed && libSet.theme) { + const themed_image = grSet.customLibraryDir ? `${grCfg.customLibraryDir}cache\\library\\themed\\themed_image.bmp` : `${fb.ProfilePath}cache\\library\\themed\\themed_image.bmp`; + if ($Lib.file(themed_image)) libSync.image(gdi.Image(themed_image)); } - panel.on_size(); - if (ui.style.topBarShow || ppt.sbarShow) but.refresh(true); - sbar.resetAuto(); - find.on_size(); - but.createImages(); - pop.createImages(); + lib.panel.on_size(); + if (lib.ui.style.topBarShow || libSet.sbarShow) lib.but.refresh(true); + lib.sbar.resetAuto(); + lib.find.on_size(); + lib.but.createImages(); + lib.pop.createImages(); - if (!ppt.themed) return; - const windowMetrics = $Lib.jsonParse(windowMetricsPath, {}, 'file'); + if (!libSet.themed) return; + const windowMetrics = $Lib.jsonParse(this.windowMetricsPath, {}, 'file'); windowMetrics[window.Name] = { - w: ui.w, - h: ui.h + w: lib.ui.w, + h: lib.ui.h } - $Lib.save(windowMetricsPath, JSON.stringify(windowMetrics, null, 3), true); + $Lib.save(this.windowMetricsPath, JSON.stringify(windowMetrics, null, 3), true); } -} + setSelection(handle) { + if (!handle || !lib.panel.list.Count) return; + const item = lib.panel.list.Find(handle); + let idx = -1; + lib.pop.tree.forEach((v, i) => { + if (!v.root && lib.pop.inRange(item, v.item)) idx = i; + }); + if (idx != -1) { + if (!lib.panel.imgView) lib.pop.focusShow(idx); + else lib.pop.showItem(idx, 'focus'); + } + } +} -//////////////////////// -// * INIT CALLBACKS * // -//////////////////////// -/** @type {LibraryCallbacks} */ -let library = new LibraryCallbacks(); -/** @type {LibraryPanel} */ -let libraryPanel = new LibraryPanel(); - - -this.on_colours_changed = () => library.on_colours_changed(); -this.on_font_changed = () => library.on_font_changed(); -this.on_char = (code) => library.on_char(code); -this.on_focus = (is_focused) => library.on_focus(is_focused); -this.on_get_album_art_done = (handle, art_id, image, image_path) => library.on_get_album_art_done(handle, art_id, image, image_path); -this.on_metadb_changed = (handleList, isDatabase) => library.on_metadb_changed(handleList, isDatabase); -this.on_item_focus_change = (playlistIndex) => library.on_item_focus_change(playlistIndex); -this.on_selection_changed = () => library.on_selection_changed(); -this.on_key_down = (vkey) => library.on_key_down(vkey); -this.on_key_up = (vkey) => library.on_key_up(vkey); -this.on_library_items_added = (handleList) => library.on_library_items_added(handleList); -this.on_library_items_removed = (handleList) => library.on_library_items_removed(handleList); -this.on_library_items_changed = (handleList) => library.on_library_items_changed(handleList); -this.on_main_menu = (index) => library.on_main_menu(index); -this.on_mouse_lbtn_dblclk = (x, y) => library.on_mouse_lbtn_dblclk(x, y); -this.on_mouse_lbtn_down = (x, y) => library.on_mouse_lbtn_down(x, y); -this.on_mouse_lbtn_up = (x, y) => library.on_mouse_lbtn_up(x, y); -this.on_mouse_leave = () => library.on_mouse_leave(); -this.on_mouse_mbtn_dblclk = (x, y, mask) => library.on_mouse_mbtn_dblclk(x, y, mask); -this.on_mouse_mbtn_down = (x, y) => library.on_mouse_mbtn_down(x, y); -this.on_mouse_mbtn_up = (x, y, mask) => library.on_mouse_mbtn_up(x, y, mask); -this.on_mouse_move = (x, y) => library.on_mouse_move(x, y); -this.on_mouse_rbtn_up = (x, y) => library.on_mouse_rbtn_up(x, y); -this.on_mouse_wheel = (step) => library.on_mouse_wheel(step); -this.on_notify_data = (name, info) => library.on_notify_data(name, info); -this.on_playback_new_track = (handle) => library.on_playback_new_track(handle); -this.on_playback_stop = (reason) => library.on_playback_stop(reason); -this.on_playback_queue_changed = () => library.on_playback_queue_changed(); -this.on_playlists_changed = () => library.on_playlists_changed(); -this.on_playlist_items_added = (playlistIndex) => library.on_playlist_items_added(playlistIndex); -this.on_playlist_items_removed = (playlistIndex) => library.on_playlist_items_removed(playlistIndex); -this.on_playlist_items_reordered = (playlistIndex) => library.on_playlist_items_reordered(playlistIndex); -this.on_playlist_switch = () => library.on_playlist_switch(); -this.on_script_unload = () => library.on_script_unload(); -this.on_selection_changed = () => library.on_selection_changed(); -this.setSelection = (handle) => library.setSelection(handle); - - -const on_queue_changed = $Lib.debounce(() => { - if (ppt.itemShowStatistics != 7) return; - pop.tree.forEach(v => { - v.id = ''; - v.count = ''; - delete v.statistics; - delete v._statistics; - }); - pop.cache = { - standard: {}, - search: {}, - filter: {} - } - panel.treePaint(); - }, 250, { - leading: true, - trailing: true -}); - -const windowMetricsPath = pref.customLibraryDir ? `${globals.customLibraryDir}cache\\library\\themed\\windowMetrics.json` : `${fb.ProfilePath}cache\\library\\themed\\windowMetrics.json`; +lib.call = new LibCallbacks(); diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-helpers.js b/profile/georgia-reborn/scripts/Library/scripts/lib-helpers.js index 107ac9cb..6a1c6c29 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-helpers.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-helpers.js @@ -1,9 +1,10 @@ 'use strict'; -const requiredVersionStrLib = '1.5.2'; +/** @global @type {string} */ +const libRequiredVersionStr = '1.5.2'; -function is_compatible(requiredVersionStrLib) { - const requiredVersion = requiredVersionStrLib.split('.'); +function lib_is_compatible(libRequiredVersionStr) { + const requiredVersion = libRequiredVersionStr.split('.'); const currentVersion = utils.Version.split('.'); if (currentVersion.length > 3) currentVersion.length = 3; for (let i = 0; i < currentVersion.length; ++i) { @@ -11,14 +12,18 @@ function is_compatible(requiredVersionStrLib) { } return true; } -if (!is_compatible(requiredVersionStrLib)) fb.ShowPopupMessage(`Library Tree requires v${requiredVersionStrLib}. Current component version is v${utils.Version}.`); - -// const doc = new ActiveXObject('htmlfile'); -const fsoLib = new ActiveXObject('Scripting.FileSystemObject'); -const tooltipLib = window.Tooltip; -const WshShellLib = new ActiveXObject('WScript.Shell'); - -class HelpersLib { +if (!lib_is_compatible(libRequiredVersionStr)) fb.ShowPopupMessage(`Library Tree requires v${libRequiredVersionStr}. Current component version is v${utils.Version}.`); + +/** @global @type {ActiveXObject} */ +const libDoc = new ActiveXObject('htmlfile'); +/** @global @type {ActiveXObject} */ +const libFSO = new ActiveXObject('Scripting.FileSystemObject'); +/** @global @type {FbTooltip} */ +const libTooltip = window.Tooltip; +/** @global @type {ActiveXObject} */ +const libWshShell = new ActiveXObject('WScript.Shell'); + +class LibHelpers { constructor() { this.pl_active = plman.ActivePlaylist; this.scale = this.getDpi(); @@ -56,7 +61,7 @@ class HelpersLib { create(fo) { try { - if (!this.folder(fo)) fsoLib.CreateFolder(fo); + if (!this.folder(fo)) libFSO.CreateFolder(fo); } catch (e) {} } @@ -85,11 +90,11 @@ class HelpersLib { } file(f) { - return typeof f === 'string' && fsoLib.FileExists(f); + return typeof f === 'string' && libFSO.FileExists(f); } folder(fo) { - return typeof fo === 'string' && fsoLib.FolderExists(fo); + return typeof fo === 'string' && libFSO.FolderExists(fo); } getClipboardData() { @@ -97,7 +102,7 @@ class HelpersLib { return utils.GetClipboardText(); } catch (e) { try { - return doc.parentWindow.clipboardData.getData('Text'); + return libDoc.parentWindow.clipboardData.getData('Text'); } catch (e) { return null; } @@ -107,7 +112,7 @@ class HelpersLib { getDpi() { let dpi = 120; try { - dpi = WshShellLib.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI'); + dpi = libWshShell.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI'); } catch (e) {} return Math.max(dpi / 120, 1); } @@ -210,7 +215,7 @@ class HelpersLib { run(c) { try { - WshShellLib.Run(c); + libWshShell.Run(c); return true; } catch (e) { return false; @@ -230,7 +235,7 @@ class HelpersLib { utils.SetClipboardText(n); } catch (e) { try { - doc.parentWindow.clipboardData.setData('Text', n); + libDoc.parentWindow.clipboardData.setData('Text', n); } catch (e) { this.trace('unable to set clipboard text'); } @@ -247,7 +252,7 @@ class HelpersLib { } throttle(e, i, t) { - let n = !0; let r = !0; if (typeof e != 'function') throw new TypeError('throttle: invalid function'); return this.isObject(t) && (n = 'leading' in t ? !!txt.leading : n, r = 'trailing' in t ? !!txt.trailing : r), this.debounce(e, i, { leading:n, maxWait:i, trailing:r }); + let n = !0; let r = !0; if (typeof e != 'function') throw new TypeError('throttle: invalid function'); return this.isObject(t) && (n = 'leading' in t ? !!bio.txt.leading : n, r = 'trailing' in t ? !!bio.txt.trailing : r), this.debounce(e, i, { leading:n, maxWait:i, trailing:r }); } titlecase(n) { @@ -287,7 +292,7 @@ class HelpersLib { wshPopup(prompt, caption) { try { - const ns = WshShell.Popup(prompt, 0, caption, 1); + const ns = libWshShell.Popup(prompt, 0, caption, 1); if (ns == 1) return true; return false; } catch (e) { @@ -296,7 +301,12 @@ class HelpersLib { } } -const $Lib = new HelpersLib(); +/** + * The instance of `LibHelpers` class for library helper operations. + * @typedef {LibHelpers} + * @global + */ +const $Lib = new LibHelpers(); function RGB(r, g, b) { return 0xff000000 | r << 16 | g << 8 | b; @@ -327,8 +337,21 @@ function StringFormat() { } /* eslint-disable */ -function Bezier(){const i=4,c=.001,o=1e-7,v=10,l=11,s=1/(l-1),n=typeof Float32Array==='function';function e(r,n){return 1-3*n+3*r;}function u(r,n){return 3*n-6*r;}function a(r){return 3*r;}function w(r,n,t){return((e(n,t)*r+u(n,t))*r+a(n))*r;}function y(r,n,t){return 3*e(n,t)*r*r+2*u(n,t)*r+a(n);}function h(r,n,t,e,u){let a,f,i=0;do{f=n+(t-n)/2;a=w(f,e,u)-r;if(a>0){t=f;}else{n=f;}}while(Math.abs(a)>o&&++i=c){return A(r,a,i,o);}else if(f===0){return a;}else{return h(r,n,n+s,i,o);}}return function r(n){if(n===0){return 0;}if(n===1){return 1;}return w(u(n),t,e);};} this.scroll = bezier(0.25, 0.1, 0.25, 1); this.full = this.scroll; this.step = this.scroll; this.bar = bezier(0.165,0.84,0.44,1); this.barFast = bezier(0.19, 1, 0.22, 1); this.inertia = bezier(0.23, 1, 0.32, 1);} -const ease = new Bezier; - -function MD5(){const b=function(l,n){let m=l[0],j=l[1],p=l[2],o=l[3];m+=(j&p|~j&o)+n[0]-680876936|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[1]-389564586|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[2]+606105819|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[3]-1044525330|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[4]-176418897|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[5]+1200080426|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[6]-1473231341|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[7]-45705983|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[8]+1770035416|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[9]-1958414417|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[10]-42063|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[11]-1990404162|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[12]+1804603682|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[13]-40341101|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[14]-1502002290|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[15]+1236535329|0;j=(j<<22|j>>>10)+p|0;m+=(j&o|p&~o)+n[1]-165796510|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[6]-1069501632|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[11]+643717713|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[0]-373897302|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[5]-701558691|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[10]+38016083|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[15]-660478335|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[4]-405537848|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[9]+568446438|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[14]-1019803690|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[3]-187363961|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[8]+1163531501|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[13]-1444681467|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[2]-51403784|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[7]+1735328473|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[12]-1926607734|0;j=(j<<20|j>>>12)+p|0;m+=(j^p^o)+n[5]-378558|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[8]-2022574463|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[11]+1839030562|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[14]-35309556|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[1]-1530992060|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[4]+1272893353|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[7]-155497632|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[10]-1094730640|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[13]+681279174|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[0]-358537222|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[3]-722521979|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[6]+76029189|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[9]-640364487|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[12]-421815835|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[15]+530742520|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[2]-995338651|0;j=(j<<23|j>>>9)+p|0;m+=(p^(j|~o))+n[0]-198630844|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[7]+1126891415|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[14]-1416354905|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[5]-57434055|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[12]+1700485571|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[3]-1894986606|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[10]-1051523|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[1]-2054922799|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[8]+1873313359|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[15]-30611744|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[6]-1560198380|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[13]+1309151649|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[4]-145523070|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[11]-1120210379|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[2]+718787259|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[9]-343485551|0;j=(j<<21|j>>>11)+p|0;l[0]=m+l[0]|0;l[1]=j+l[1]|0;l[2]=p+l[2]|0;l[3]=o+l[3]|0;};const e='0123456789abcdef';const d=[];const c=function(k){const q=e;const o=d;let r,p,l;for(let m=0;m<4;m++){p=m*8;r=k[m];for(l=0;l<8;l+=2){o[p+1+l]=q.charAt(r&15);r>>>=4;o[p+0+l]=q.charAt(r&15);r>>>=4;}}return o.join('');};const i=function(){this._dataLength=0;this._state=new Int32Array(4);this._buffer=new ArrayBuffer(68);this._bufferLength=0;this._buffer8=new Uint8Array(this._buffer,0,68);this._buffer32=new Uint32Array(this._buffer,0,17);this.start();};const a=new Int32Array([1732584193,-271733879,-1732584194,271733878]);const h=new Int32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);i.prototype.appendStr=function(n){const k=this._buffer8;const j=this._buffer32;let o=this._bufferLength;for(let l=0;l>>6)+192;k[o++]=m&63|128;}else{if(m<55296||m>56319){k[o++]=(m>>>12)+224;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}else{m=((m-55296)*1024)+(n.charCodeAt(++l)-56320)+65536;if(m>1114111){throw'Unicode standard supports code points up to U+10FFFF';}k[o++]=(m>>>18)+240;k[o++]=(m>>>12&63)|128;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}}}if(o>=64){this._dataLength+=64;b(this._state,j);o-=64;j[0]=j[16];}}this._bufferLength=o;return this;};i.prototype.appendAsciiStr=function(o){const l=this._buffer8;const k=this._buffer32;let p=this._bufferLength;let n=0,m=0;for(;;){n=Math.min(o.length-m,64-p);while(n--){l[p++]=o.charCodeAt(m++);}if(p<64){break;}this._dataLength+=64;b(this._state,k);p=0;}this._bufferLength=p;return this;};i.prototype.start=function(){this._dataLength=0;this._bufferLength=0;this._state.set(a);return this;};i.prototype.end=function(){const q=this._bufferLength;this._dataLength+=q;const r=this._buffer8;r[q]=128;r[q+1]=r[q+2]=r[q+3]=0;const k=this._buffer32;const m=(q>>2)+1;k.set(h.subarray(m),m);if(q>55){b(this._state,k);k.set(h);}const j=this._dataLength*8;if(j<=4294967295){k[14]=j;}else{const n=j.toString(16).match(/(.*?)(.{0,8})$/);const o=parseInt(n[2],16);const l=parseInt(n[1],16)||0;k[14]=o;k[15]=l;}b(this._state,k);return c(this._state);};const f=new i();i.hashStr=function(k){return f.start().appendStr(k).end();};return i;} // https://github.com/gorhill/yamd5.js -const md5 = new MD5; +function LibBezier(){const i=4,c=.001,o=1e-7,v=10,l=11,s=1/(l-1),n=typeof Float32Array==='function';function e(r,n){return 1-3*n+3*r;}function u(r,n){return 3*n-6*r;}function a(r){return 3*r;}function w(r,n,t){return((e(n,t)*r+u(n,t))*r+a(n))*r;}function y(r,n,t){return 3*e(n,t)*r*r+2*u(n,t)*r+a(n);}function h(r,n,t,e,u){let a,f,i=0;do{f=n+(t-n)/2;a=w(f,e,u)-r;if(a>0){t=f;}else{n=f;}}while(Math.abs(a)>o&&++i=c){return A(r,a,i,o);}else if(f===0){return a;}else{return h(r,n,n+s,i,o);}}return function r(n){if(n===0){return 0;}if(n===1){return 1;}return w(u(n),t,e);};} this.scroll = bezier(0.25, 0.1, 0.25, 1); this.full = this.scroll; this.step = this.scroll; this.bar = bezier(0.165,0.84,0.44,1); this.barFast = bezier(0.19, 1, 0.22, 1); this.inertia = bezier(0.23, 1, 0.32, 1);} + +/** + * The instance of `LibBezier` function for calculating points on a Bezier curve. + * This can be used for animations, transitions, or any operations that require Bezier curve computations. + * @typedef {LibBezier} + * @global + */ +const libEase = new LibBezier; + +function LibMD5(){const b=function(l,n){let m=l[0],j=l[1],p=l[2],o=l[3];m+=(j&p|~j&o)+n[0]-680876936|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[1]-389564586|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[2]+606105819|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[3]-1044525330|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[4]-176418897|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[5]+1200080426|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[6]-1473231341|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[7]-45705983|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[8]+1770035416|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[9]-1958414417|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[10]-42063|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[11]-1990404162|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[12]+1804603682|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[13]-40341101|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[14]-1502002290|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[15]+1236535329|0;j=(j<<22|j>>>10)+p|0;m+=(j&o|p&~o)+n[1]-165796510|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[6]-1069501632|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[11]+643717713|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[0]-373897302|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[5]-701558691|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[10]+38016083|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[15]-660478335|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[4]-405537848|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[9]+568446438|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[14]-1019803690|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[3]-187363961|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[8]+1163531501|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[13]-1444681467|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[2]-51403784|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[7]+1735328473|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[12]-1926607734|0;j=(j<<20|j>>>12)+p|0;m+=(j^p^o)+n[5]-378558|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[8]-2022574463|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[11]+1839030562|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[14]-35309556|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[1]-1530992060|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[4]+1272893353|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[7]-155497632|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[10]-1094730640|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[13]+681279174|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[0]-358537222|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[3]-722521979|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[6]+76029189|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[9]-640364487|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[12]-421815835|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[15]+530742520|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[2]-995338651|0;j=(j<<23|j>>>9)+p|0;m+=(p^(j|~o))+n[0]-198630844|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[7]+1126891415|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[14]-1416354905|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[5]-57434055|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[12]+1700485571|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[3]-1894986606|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[10]-1051523|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[1]-2054922799|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[8]+1873313359|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[15]-30611744|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[6]-1560198380|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[13]+1309151649|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[4]-145523070|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[11]-1120210379|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[2]+718787259|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[9]-343485551|0;j=(j<<21|j>>>11)+p|0;l[0]=m+l[0]|0;l[1]=j+l[1]|0;l[2]=p+l[2]|0;l[3]=o+l[3]|0;};const e='0123456789abcdef';const d=[];const c=function(k){const q=e;const o=d;let r,p,l;for(let m=0;m<4;m++){p=m*8;r=k[m];for(l=0;l<8;l+=2){o[p+1+l]=q.charAt(r&15);r>>>=4;o[p+0+l]=q.charAt(r&15);r>>>=4;}}return o.join('');};const i=function(){this._dataLength=0;this._state=new Int32Array(4);this._buffer=new ArrayBuffer(68);this._bufferLength=0;this._buffer8=new Uint8Array(this._buffer,0,68);this._buffer32=new Uint32Array(this._buffer,0,17);this.start();};const a=new Int32Array([1732584193,-271733879,-1732584194,271733878]);const h=new Int32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);i.prototype.appendStr=function(n){const k=this._buffer8;const j=this._buffer32;let o=this._bufferLength;for(let l=0;l>>6)+192;k[o++]=m&63|128;}else{if(m<55296||m>56319){k[o++]=(m>>>12)+224;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}else{m=((m-55296)*1024)+(n.charCodeAt(++l)-56320)+65536;if(m>1114111){throw'Unicode standard supports code points up to U+10FFFF';}k[o++]=(m>>>18)+240;k[o++]=(m>>>12&63)|128;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128;}}}if(o>=64){this._dataLength+=64;b(this._state,j);o-=64;j[0]=j[16];}}this._bufferLength=o;return this;};i.prototype.appendAsciiStr=function(o){const l=this._buffer8;const k=this._buffer32;let p=this._bufferLength;let n=0,m=0;for(;;){n=Math.min(o.length-m,64-p);while(n--){l[p++]=o.charCodeAt(m++);}if(p<64){break;}this._dataLength+=64;b(this._state,k);p=0;}this._bufferLength=p;return this;};i.prototype.start=function(){this._dataLength=0;this._bufferLength=0;this._state.set(a);return this;};i.prototype.end=function(){const q=this._bufferLength;this._dataLength+=q;const r=this._buffer8;r[q]=128;r[q+1]=r[q+2]=r[q+3]=0;const k=this._buffer32;const m=(q>>2)+1;k.set(h.subarray(m),m);if(q>55){b(this._state,k);k.set(h);}const j=this._dataLength*8;if(j<=4294967295){k[14]=j;}else{const n=j.toString(16).match(/(.*?)(.{0,8})$/);const o=parseInt(n[2],16);const l=parseInt(n[1],16)||0;k[14]=o;k[15]=l;}b(this._state,k);return c(this._state);};const f=new i();i.hashStr=function(k){return f.start().appendStr(k).end();};return i;} // https://github.com/gorhill/yamd5.js + +/** + * The instance of `libMD5` function for library MD5 hash generation. + * @typedef {libMD5} + * @global + */ +const libMD5 = new LibMD5; diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-images.js b/profile/georgia-reborn/scripts/Library/scripts/lib-images.js index 6f48a369..4a25ac48 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-images.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-images.js @@ -1,11 +1,11 @@ 'use strict'; -class Images { +class LibImages { constructor() { this.accessed = 0; this.asyncBypass = 0; this.blockWidth = 150; - this.cachePath = pref.customLibraryDir ? `${globals.customLibraryDir}library-tree-cache\\` : `${fb.ProfilePath}cache\\library\\library-tree-cache\\`; + this.cachePath = grSet.customLibraryDir ? `${grCfg.customLibraryDir}library-tree-cache\\` : `${fb.ProfilePath}cache\\library\\library-tree-cache\\`; this.cellWidth = 200; this.column = 0; this.columnWidth = 150; @@ -49,8 +49,8 @@ class Images { this.style = { image: 0, - rootComposite: ppt.rootNode && ppt.curRootImg == 3, - vertical: !ppt.albumArtFlowMode ? true : ui.h - panel.search.h > ui.w - ui.sbar.w, + rootComposite: libSet.rootNode && libSet.curRootImg == 3, + vertical: !libSet.albumArtFlowMode ? true : lib.ui.h - lib.panel.search.h > lib.ui.w - lib.ui.sbar.w, y: 25 }; @@ -65,12 +65,12 @@ class Images { preLoad: 7 }; - this.labels = { statistics: ppt.itemShowStatistics ? 1 : 0 } + this.labels = { statistics: libSet.itemShowStatistics ? 1 : 0 } this.letter = { - albumArtYearAuto: ppt.albumArtYearAuto, + albumArtYearAuto: libSet.albumArtYearAuto, no: 1, - show: ppt.albumArtLetter, + show: libSet.albumArtLetter, w: 0 }; @@ -98,11 +98,11 @@ class Images { }; this.drawDebounce = $Lib.debounce(() => { - panel.treePaint(); + lib.panel.treePaint(); }, 500); this.loadThrottle = $Lib.throttle(() => { - if (!panel.imgView) return; + if (!lib.panel.imgView) return; this.getImages(); }, 40); @@ -114,10 +114,10 @@ class Images { }); this.sizeDebounce = $Lib.debounce(() => { - if (!panel.imgView) return; + if (!lib.panel.imgView) return; this.clearCache(); this.metrics(); - if (sbar.scroll > sbar.max_scroll) sbar.checkScroll(sbar.max_scroll); + if (lib.sbar.scroll > lib.sbar.max_scroll) lib.sbar.checkScroll(lib.sbar.max_scroll); }, 100); this.setRoot(); @@ -130,7 +130,7 @@ class Images { async get_album_art_async(handle, art_id, key, ix) { const result = await utils.GetAlbumArtAsyncV2(0, handle, art_id, false); const o = this.cache[key]; - const saveName = `${md5.hashStr(result.path)}.jpg`; + const saveName = `${libMD5.hashStr(result.path)}.jpg`; if (o && o.img == 'called') this.cacheIt(result.image, key, ix, saveName); } @@ -178,7 +178,7 @@ class Images { } this.checkCache(); - this.format(image, ppt.artId, ['default', 'crop', 'circular'][this.style.image], this.im.w, this.im.w, ppt.albumArtLabelType == 3, 'display', ix, key); + this.format(image, libSet.artId, ['default', 'crop', 'circular'][this.style.image], this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'display', ix, key); if (this.style.rootComposite && ix < this.rootNo) this.rootDebounce(); } @@ -215,18 +215,18 @@ class Images { try { if (image) { this.checkCache(); - this.format(image, ppt.artId, ['default', 'crop', 'circular'][this.style.image], this.im.w, this.im.w, ppt.albumArtLabelType == 3, 'displayPreload', ix, key); + this.format(image, libSet.artId, ['default', 'crop', 'circular'][this.style.image], this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'displayPreload', ix, key); } if (this.style.rootComposite && ix < this.rootNo) this.rootDebounce(); } catch (e) { $Lib.trace(`unable to load thumbnail image: ${key}`); } - panel.treePaint(); + lib.panel.treePaint(); } checkCache() { if (!this.memoryLimit()) return; - const ln = this.columns * panel.rows * 3; + const ln = this.columns * lib.panel.rows * 3; if (this.toSave.length > ln) this.toSave.length = ln; this.preLoadItems = []; clearInterval(this.timer.preLoad); @@ -236,8 +236,8 @@ class Images { this.timer.load = null; let keys = Object.keys(this.cache); const cacheLength = keys.length; - if (pop.tree.length) { - const o = this.cache[pop.tree[0].key]; + if (lib.pop.tree.length) { + const o = this.cache[lib.pop.tree[0].key]; if (o) o.accessed = Infinity; } this.cache = this.sortCache(this.cache, 'accessed'); @@ -249,15 +249,15 @@ class Images { } checkNowPlaying(item) { - if (!ppt.highLightNowplaying) return false; - return !item.root && pop.inRange(pop.nowp, item.item); + if (!libSet.highLightNowplaying) return false; + return !item.root && lib.pop.inRange(lib.pop.nowp, item.item); } checkRootImg() { - const key = pop.tree.length ? pop.tree[0].key : null; + const key = lib.pop.tree.length ? lib.pop.tree[0].key : null; if (!key) return; let o = this.cache[key]; - const imgsAvailable = Math.min(Math.round((this.panel.h + this.row.h) / this.row.h) * this.columns, pop.tree.length) - 1; + const imgsAvailable = Math.min(Math.round((this.panel.h + this.row.h) / this.row.h) * this.columns, lib.pop.tree.length) - 1; const n = Math.max(Math.min(Math.floor(Math.sqrt(imgsAvailable)), Infinity), 2); // auto set collage size: limited by no imgs available (per screen): reduce by changing infinity const cells = Math.pow(n, 2); this.rootNo = n * n + 1; @@ -272,18 +272,18 @@ class Images { if (o.img) { if (this.style.image == 2) this.circularMask(o.img, o.img.Width, o.img.Height); o.img = o.img.Resize(this.im.w, this.im.w, 7); - if (ppt.albumArtLabelType == 3) this.fadeMask(o.img, o.img.Width, o.img.Height); + if (libSet.albumArtLabelType == 3) this.fadeMask(o.img, o.img.Width, o.img.Height); } - panel.treePaint(); + lib.panel.treePaint(); } checkTooltip(gr, item, x, y1, y2, y3, w, tt1, tt2, tt3, font1, font2, font3) { - if (panel.colMarker) { + if (lib.panel.colMarker) { if (tt1) tt1 = tt1.replace(/@!#.*?@!#/g, ''); if (tt2) tt2 = tt2.replace(/@!#.*?@!#/g, ''); } let text = tt1 || ''; - if (tt2 && (panel.lines == 2 || panel.lines == 1 && this.labels.statistics)) text += `\n${tt2}`; + if (tt2 && (lib.panel.lines == 2 || lib.panel.lines == 1 && this.labels.statistics)) text += `\n${tt2}`; if (tt3 && this.labels.statistics) text += `\n${tt3}`; item.tt = { text, @@ -318,14 +318,14 @@ class Images { for (let column = 0; column < columns; column++) { const idx = column + row * columns + 1; if (idx <= cells) { - let img = pop.tree.length && pop.tree[idx] ? this.getRootImg(pop.tree[idx].key) : null; + let img = lib.pop.tree.length && lib.pop.tree[idx] ? this.getRootImg(lib.pop.tree[idx].key) : null; if (!img) img = this.stub.noImg; if (img) { let cx = 0; let cy = 0; let cw = img.Width; let ch = img.Height; - if (ppt.albumArtLabelType == 3) { + if (libSet.albumArtLabelType == 3) { if (this.style.image != 2) { ch -= this.overlayHeight; } else { @@ -341,7 +341,7 @@ class Images { ch *= 0.8; } img = img.Clone(cx, cy, cw, ch); - img = this.format(img, ppt.artId, 'crop', this.cellWidth, this.cellWidth, false, 'root'); + img = this.format(img, libSet.artId, 'crop', this.cellWidth, this.cellWidth, false, 'root'); g.DrawImage(img, x, y, img.Width, img.Height, 0, 0, img.Width, img.Height); } x += cellWidth; @@ -354,15 +354,15 @@ class Images { y = 0; for (let column = 0; column < columns; column++) { x += cellWidth; - if (this.style.image != 2) g.DrawLine(x, 0, x, cellWidth * columns, ui.l.w, ui.col.rootBlend); + if (this.style.image != 2) g.DrawLine(x, 0, x, cellWidth * columns, lib.ui.l.w, lib.ui.col.rootBlend); } x = 0; y = 0; for (let row = 0; row < rows; row++) { y += cellHeight; - if (this.style.image != 2) g.DrawLine(x, y, cellWidth * columns, y, ui.l.w, ui.col.rootBlend); + if (this.style.image != 2) g.DrawLine(x, y, cellWidth * columns, y, lib.ui.l.w, lib.ui.col.rootBlend); } - if (this.style.image != 2) g.DrawRect(0, 0, cellWidth * columns - 1, cellWidth * columns - 1, 1, ui.col.rootBlend); + if (this.style.image != 2) g.DrawRect(0, 0, cellWidth * columns - 1, cellWidth * columns - 1, 1, lib.ui.col.rootBlend); } createImages() { @@ -378,7 +378,7 @@ class Images { } draw(gr) { - if (!panel.imgView) return; + if (!lib.panel.imgView) return; let box_x; let box_y; let iw; @@ -387,50 +387,50 @@ class Images { this.column = 0; for (let i = this.start; i < this.end; i++) { const row = this.style.vertical ? Math.floor(i / this.columns) : 0; - box_x = this.style.vertical ? Math.floor(ui.x + this.panel.x + this.column * this.columnWidth + this.bor.side) : Math.floor(ui.x + this.panel.x + i * this.columnWidth + this.bor.side - sbar.delta + (ppt.albumArtFlowMode ? SCALE(18) : 0)); - box_y = this.style.vertical ? Math.floor(ui.y + this.panel.y + row * this.row.h - sbar.delta) : ui.y + this.style.y; + box_x = this.style.vertical ? Math.floor(lib.ui.x + this.panel.x + this.column * this.columnWidth + this.bor.side) : Math.floor(lib.ui.x + this.panel.x + i * this.columnWidth + this.bor.side - lib.sbar.delta + (libSet.albumArtFlowMode ? SCALE(18) : 0)); + box_y = this.style.vertical ? Math.floor(lib.ui.y + this.panel.y + row * this.row.h - lib.sbar.delta) : lib.ui.y + this.style.y; if (box_y >= 0 - this.row.h && box_y < this.panel.y + this.panel.h) { - const item = pop.tree[i]; - pop.getItemCount(item); + const item = lib.pop.tree[i]; + lib.pop.getItemCount(item); const grp = item.grp; const lot = item.lot; const statistics = this.labels.statistics ? (!item.root && this.labels.counts ? item.count + (item.count && item._statistics ? ' | ' : '') : '') + item._statistics : ''; const cur_img = !this.zooming ? this.getImg(item.key) : null; const nowp = this.checkNowPlaying(item); - const grpCol = this.getGrpCol(item, nowp, pop.highlight.text && i == pop.m.i); - const lotCol = this.getLotCol(item, nowp, pop.highlight.text && i == pop.m.i); - const coversRightBottom = ['coversLabelsRight', 'coversLabelsBottom'].includes(pref.libraryDesign) || ppt.albumArtLabelType === 2; - const updatedNowpBg = g_pl_colors.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing + // const grpCol = this.getGrpCol(item, nowp, lib.pop.highlight.text && i == lib.pop.m.i); + // const lotCol = this.getLotCol(item, nowp, lib.pop.highlight.text && i == lib.pop.m.i); + const coversRightBottom = ['coversLabelsRight', 'coversLabelsBottom'].includes(grSet.libraryDesign) || libSet.albumArtLabelType === 2; + const updatedNowpBg = pl.col.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing this.drawSelBg(gr, cur_img, box_x, box_y, i, nowp); // * Now playing bg for labels overlay mode ( album art ) - if (this.labels.overlay && !item.root && pop.inRange(pop.nowp, item.item) && updatedNowpBg) { - gr.FillSolidRect(box_x, box_y, this.box.w, this.box.h, ui.col.nowPlayingBg); + if (this.labels.overlay && !item.root && lib.pop.inRange(lib.pop.nowp, item.item) && updatedNowpBg) { + gr.FillSolidRect(box_x, box_y, this.box.w, this.box.h, lib.ui.col.nowPlayingBg); } // * Now playing bg selection with now playing deactivated ( album art ) - if (item.sel && ppt.albumArtShow && !coversRightBottom && updatedNowpBg) { - gr.FillSolidRect(box_x, box_y, this.box.w, this.box.h, ui.col.nowPlayingBg); + if (item.sel && libSet.albumArtShow && !coversRightBottom && updatedNowpBg) { + gr.FillSolidRect(box_x, box_y, this.box.w, this.box.h, lib.ui.col.nowPlayingBg); } // * Now playing bg selection with now playing deactivated - if (!pop.highlight.nowPlaying && item.sel && coversRightBottom && updatedNowpBg) { - gr.FillSolidRect(ui.x, box_y, sbar.w ? ui.w - SCALE(42) : ui.w, this.box.h, ui.col.nowPlayingBg); - gr.FillSolidRect(ui.x, box_y, ui.sz.sideMarker, this.box.h, ui.col.sideMarker); + if (!lib.pop.highlight.nowPlaying && item.sel && coversRightBottom && updatedNowpBg) { + gr.FillSolidRect(lib.ui.x, box_y, lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w, this.box.h, lib.ui.col.nowPlayingBg); + gr.FillSolidRect(lib.ui.x, box_y, lib.ui.sz.sideMarker, this.box.h, lib.ui.col.sideMarker); } // * Marker selection with now playing active - if (pop.highlight.nowPlaying && item.sel && pref.libraryDesign !== 'flowMode') { - gr.DrawRect(ui.x, box_y, sbar.w ? ui.w - SCALE(42) - 1 : ui.w, this.box.h, 1, ui.col.selectionFrame); - gr.FillSolidRect(ui.x, box_y, ui.sz.sideMarker, this.box.h + 1, ui.col.sideMarker); + if (lib.pop.highlight.nowPlaying && item.sel && grSet.libraryDesign !== 'flowMode') { + gr.DrawRect(lib.ui.x, box_y, lib.sbar.w ? lib.ui.w - SCALE(42) - 1 : lib.ui.w, this.box.h, 1, lib.ui.col.selectionFrame); + gr.FillSolidRect(lib.ui.x, box_y, lib.ui.sz.sideMarker, this.box.h + 1, lib.ui.col.sideMarker); } // * Hide DrawRect gaps when all songs are completely selected and mask lines when selecting now playing - if ((['white', 'black', 'cream'].includes(pref.theme) && !pref.styleBlackAndWhite2) && (pop.highlight.nowPlaying && item.sel && !item.root && pop.inRange(pop.nowp, item.item) && coversRightBottom) && updatedNowpBg) { - gr.DrawRect(ui.x, box_y, pop.fullLineSelection ? sbar.w ? ui.w - SCALE(42) : ui.w : ui.w + ui.sz.margin + box_x - ui.x - ui.sz.sideMarker, this.box.h, 1, ui.col.nowPlayingBg); + if ((['white', 'black', 'cream'].includes(grSet.theme) && !grSet.styleBlackAndWhite2) && (lib.pop.highlight.nowPlaying && item.sel && !item.root && lib.pop.inRange(lib.pop.nowp, item.item) && coversRightBottom) && updatedNowpBg) { + gr.DrawRect(lib.ui.x, box_y, lib.pop.fullLineSelection ? lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w : lib.ui.w + lib.ui.sz.margin + box_x - lib.ui.x - lib.ui.sz.sideMarker, this.box.h, 1, lib.ui.col.nowPlayingBg); } - this.im.y = this.labels.overlay ? this.im.offset + box_y + ppt.thumbNailGapCompact / 2 : this.im.offset + box_y; - if (pop.rowStripes && this.labels.right) { - if (i % 2 == 0) gr.FillSolidRect(0, box_y + 1, panel.tree.stripe.w, this.row.h, ui.col.rowStripes /*ui.col.bg1*/); - else gr.FillSolidRect(0, box_y, panel.tree.stripe.w, this.row.h, ui.col.bg2); + this.im.y = this.labels.overlay ? this.im.offset + box_y + libSet.thumbNailGapCompact / 2 : this.im.offset + box_y; + if (lib.pop.rowStripes && this.labels.right) { + if (i % 2 == 0) gr.FillSolidRect(0, box_y + 1, lib.panel.tree.stripe.w, this.row.h, lib.ui.col.rowStripes /*ui.col.bg1*/); + else gr.FillSolidRect(0, box_y, lib.panel.tree.stripe.w, this.row.h, lib.ui.col.bg2); } let x1 = 0; const x2 = Math.round(box_x + (this.bor.cov) / 2); @@ -460,17 +460,17 @@ class Images { } } - gr.DrawImage(cur_img, x1, y1, w, h, pref.libraryThumbnailBorder === 'border' ? 0 : 1, pref.libraryThumbnailBorder === 'border' ? 0 : 1, iw, ih); + gr.DrawImage(cur_img, x1, y1, w, h, grSet.libraryThumbnailBorder === 'border' ? 0 : 1, grSet.libraryThumbnailBorder === 'border' ? 0 : 1, iw, ih); if (this.labels.overlayDark) { if (item.sel || nowp) gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, RGBA(150, 150, 150, 150)); gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, this.getSelBgCol(item, nowp)); } - if (pref.libraryThumbnailBorder !== 'none' && (!item.sel || !this.labels.overlay || this.style.image != 2)) { - if (this.style.image != 2) gr.DrawRect(x1, y1, iw - 1, ih - 1, 1, ui.col.imgBor); + if (grSet.libraryThumbnailBorder !== 'none' && (!item.sel || !this.labels.overlay || this.style.image != 2)) { + if (this.style.image != 2) gr.DrawRect(x1, y1, iw - 1, ih - 1, 1, lib.ui.col.imgBor); else { gr.SetSmoothingMode(2); - gr.DrawEllipse(x1, y1, iw - 1, ih - 1, 1, ui.col.imgBor); + gr.DrawEllipse(x1, y1, iw - 1, ih - 1, 1, lib.ui.col.imgBor); gr.SetSmoothingMode(0); } } @@ -499,8 +499,8 @@ class Images { else if (!this.style.rootComposite && this.stub.root) gr.DrawImage(this.stub.root, x1, y1, iw, ih, 0, 0, iw, ih); if (this.labels.overlay) { - gr.FillGradRect(x1, y2 - 1, iw / 2, ui.l.w, 1, RGBA(0, 0, 0, 0), ui.col.imgBor); - gr.FillGradRect(x1 + iw / 2, y2 - 1, iw / 2, ui.l.w, 1, ui.col.imgBor, RGBA(0, 0, 0, 0)); + gr.FillGradRect(x1, y2 - 1, iw / 2, lib.ui.l.w, 1, RGBA(0, 0, 0, 0), lib.ui.col.imgBor); + gr.FillGradRect(x1 + iw / 2, y2 - 1, iw / 2, lib.ui.l.w, 1, lib.ui.col.imgBor, RGBA(0, 0, 0, 0)); } if (this.labels.overlayDark) { if (item.sel || nowp) gr.FillSolidRect(x2, y2, this.im.w, this.overlayHeight, RGBA(150, 150, 150, 150)); @@ -508,54 +508,54 @@ class Images { } } this.drawItemOverlay(gr, item, x1, y1, iw, ih); - if (i == pop.m.i) { - if (pop.highlight.row == 3 || pop.highlight.row == 2 && (((this.labels.overlay || this.labels.hide) && this.style.image != 2))) { - if (!ppt.frameImage) this.drawFrame(gr, box_x, box_y, /*ui.col.frameImgSel*/ ui.col.selectionFrame2, !this.labels.overlay && !this.labels.hide ? 'stnd' : 'thick'); - else this.drawImageFrame(gr, x1, y1, iw, ih, /*ui.col.frameImgSel*/ ui.col.selectionFrame2); - } // else if (pop.highlight.row == 1 && !sbar.draw_timer) gr.FillSolidRect(ui.l.w, y1, ui.sz.sideMarker, this.im.w, ui.col.sideMarker); + if (i == lib.pop.m.i) { + if (lib.pop.highlight.row == 3 || lib.pop.highlight.row == 2 && (((this.labels.overlay || this.labels.hide) && this.style.image != 2))) { + if (!libSet.frameImage) this.drawFrame(gr, box_x, box_y, /*ui.col.frameImgSel*/ lib.ui.col.selectionFrame2, !this.labels.overlay && !this.labels.hide ? 'stnd' : 'thick'); + else this.drawImageFrame(gr, x1, y1, iw, ih, /*ui.col.frameImgSel*/ lib.ui.col.selectionFrame2); + } // else if (lib.pop.highlight.row == 1 && !lib.sbar.draw_timer) gr.FillSolidRect(lib.ui.l.w, y1, lib.ui.sz.sideMarker, this.im.w, lib.ui.col.sideMarker); } if (item.sel) { - // if (this.labels.overlay && this.style.image != 2) this.drawFrame(gr, box_x, box_y, /*ui.col.frameImgSel*/ ui.col.selectionFrame2, 'thick'); - // else if (this.labels.hide && pop.highlight.row == 3 && ppt.frameImage) this.drawImageFrame(gr, x1, y1, iw, ih, /*ui.col.frameImgSel*/ ui.col.selectionFrame2); - if (this.labels.hide && pop.highlight.row == 3 && ppt.frameImage) this.drawImageFrame(gr, x1, y1, iw, ih, /*ui.col.frameImgSel*/ ui.col.selectionFrame2); + // if (this.labels.overlay && this.style.image != 2) this.drawFrame(gr, box_x, box_y, /* lib.ui.col.frameImgSel */ lib.ui.col.selectionFrame2, 'thick'); + // else if (this.labels.hide && lib.pop.highlight.row == 3 && libSet.frameImage) this.drawImageFrame(gr, x1, y1, iw, ih, /* lib.ui.col.frameImgSel */ lib.ui.col.selectionFrame2); + if (this.labels.hide && lib.pop.highlight.row == 3 && libSet.frameImage) this.drawImageFrame(gr, x1, y1, iw, ih, /* lib.ui.col.frameImgSel */ lib.ui.col.selectionFrame2); } if (!this.labels.hide) { const x = box_x + this.text.x; let type = 0; const txt_c = - pop.highlight.nowPlaying && !item.root && pop.inRange(pop.nowp, item.item) && updatedNowpBg ? ui.col.text_nowp : - item.sel ? ui.col.textSel : - pop.m.i === i ? ui.col.text_h : ui.col.text; + lib.pop.highlight.nowPlaying && !item.root && lib.pop.inRange(lib.pop.nowp, item.item) && updatedNowpBg ? lib.ui.col.text_nowp : + item.sel ? lib.ui.col.textSel : + lib.pop.m.i === i ? lib.ui.col.text_h : lib.ui.col.text; - if (panel.colMarker) type = item.sel ? 2 : pop.highlight.text && i == pop.m.i ? 1 : 0; + if (lib.panel.colMarker) type = item.sel ? 2 : lib.pop.highlight.text && i == lib.pop.m.i ? 1 : 0; if (!this.labels.overlay) { y1 = this.im.y + this.text.y1; y2 = this.im.y + this.text.y2; const y3 = this.im.y + this.text.y3; - if (panel.lines == 2) { - this.checkTooltip(gr, item, x, y1, y2, y3, this.text.w, grp, lot, statistics, ui.font.group, /*ui.font.lot,*/ ui.font.group, ui.font.statistics); - !panel.colMarker ? gr.GdiDrawText(grp, ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[1] ? panel.cc : panel.lc) : pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, ui.font.group, ui.font.groupEllipsisSpace, 'group'); - !panel.colMarker ? gr.GdiDrawText(lot, ui.font.lot, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[2] ? panel.cc : panel.lc) : pop.cusCol(gr, lot, item, x, y2, this.text.w, this.text.h, type, nowp, ui.font.lot, ui.font.lotEllipsisSpace, 'lott'); - if (statistics) gr.GdiDrawText(statistics, ui.font.statistics, txt_c /*lotCol*/, x, y3, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[2] ? panel.cc : panel.lc); + if (lib.panel.lines == 2) { + this.checkTooltip(gr, item, x, y1, y2, y3, this.text.w, grp, lot, statistics, lib.ui.font.group, /*ui.font.lot,*/ lib.ui.font.group, lib.ui.font.statistics); + !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.groupEllipsisSpace, 'group'); + !lib.panel.colMarker ? gr.GdiDrawText(lot, lib.ui.font.lot, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[2] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, lot, item, x, y2, this.text.w, this.text.h, type, nowp, lib.ui.font.lot, lib.ui.font.lotEllipsisSpace, 'lott'); + if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y3, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[2] ? lib.panel.cc : lib.panel.lc); } else { - this.checkTooltip(gr, item, x, y1, statistics ? y2 : -1, -1, this.text.w, grp, statistics, false, ui.font.group, ui.font.statistics); - !panel.colMarker ? gr.GdiDrawText(grp, ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[1] ? panel.cc : panel.lc) : pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, ui.font.group, ui.font.mainEllipsisSpace, 'group'); - if (statistics) gr.GdiDrawText(statistics, ui.font.statistics, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[2] ? panel.cc : panel.lc); + this.checkTooltip(gr, item, x, y1, statistics ? y2 : -1, -1, this.text.w, grp, statistics, false, lib.ui.font.group, lib.ui.font.statistics); + !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.mainEllipsisSpace, 'group'); + if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !this.labels.right && !item.tt[2] ? lib.panel.cc : lib.panel.lc); } } else { y1 = this.im.y + this.text.y1; y2 = y1 + this.text.h * (this.labels.statistics ? 0.93 : 0.9); const y3 = y2 + this.text.h * 0.95; - if (panel.lines == 2) { - this.checkTooltip(gr, item, x, y1, y2, y3, this.text.w, grp, lot, statistics, ui.font.group, /*ui.font.lot,*/ ui.font.group, ui.font.statistics); - !panel.colMarker ? gr.GdiDrawText(grp, ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !item.tt[1] ? panel.cc : panel.lc) : pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, ui.font.group, ui.font.groupEllipsisSpace, 'lott'); - !panel.colMarker ? gr.GdiDrawText(lot, ui.font.lot, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !item.tt[2] ? panel.cc : panel.lc) : pop.cusCol(gr, lot, item, x, y2, this.text.w, this.text.h, type, nowp, ui.font.lot, ui.font.lotEllipsisSpace, 'group'); - if (statistics) gr.GdiDrawText(statistics, ui.font.statistics, txt_c /*lotCol*/, x, y3, this.text.w, this.text.h, this.style.image != 1 && !item.tt[3] ? panel.cc : panel.lc); + if (lib.panel.lines == 2) { + this.checkTooltip(gr, item, x, y1, y2, y3, this.text.w, grp, lot, statistics, lib.ui.font.group, /*ui.font.lot,*/ lib.ui.font.group, lib.ui.font.statistics); + !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.groupEllipsisSpace, 'lott'); + !lib.panel.colMarker ? gr.GdiDrawText(lot, lib.ui.font.lot, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !item.tt[2] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, lot, item, x, y2, this.text.w, this.text.h, type, nowp, lib.ui.font.lot, lib.ui.font.lotEllipsisSpace, 'group'); + if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y3, this.text.w, this.text.h, this.style.image != 1 && !item.tt[3] ? lib.panel.cc : lib.panel.lc); } else { - this.checkTooltip(gr, item, x, y1, statistics ? y2 : -1, -1, this.text.w, grp, statistics, false, ui.font.group, /*ui.font.lot,*/ ui.font.group, ui.font.statistics); - !panel.colMarker ? gr.GdiDrawText(grp, ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !item.tt[1] ? panel.cc : panel.lc) : pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, ui.font.group, ui.font.groupEllipsisSpace, 'group'); - if (statistics) gr.GdiDrawText(statistics, ui.font.statistics, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !item.tt[2] ? panel.cc : panel.lc); + this.checkTooltip(gr, item, x, y1, statistics ? y2 : -1, -1, this.text.w, grp, statistics, false, lib.ui.font.group, /*ui.font.lot,*/ lib.ui.font.group, lib.ui.font.statistics); + !lib.panel.colMarker ? gr.GdiDrawText(grp, lib.ui.font.group, txt_c /*grpCol*/, x, y1, this.text.w, this.text.h, this.style.image != 1 && !item.tt[1] ? lib.panel.cc : lib.panel.lc) : lib.pop.cusCol(gr, grp, item, x, y1, this.text.w, this.text.h, type, nowp, lib.ui.font.group, lib.ui.font.groupEllipsisSpace, 'group'); + if (statistics) gr.GdiDrawText(statistics, lib.ui.font.statistics, txt_c /*lotCol*/, x, y2, this.text.w, this.text.h, this.style.image != 1 && !item.tt[2] ? lib.panel.cc : lib.panel.lc); } } } @@ -563,7 +563,7 @@ class Images { if (this.column == this.columns - 1) this.column = 0; else this.column++; } - ui.drawTopBarUnderlay(gr); + lib.ui.drawTopBarUnderlay(gr); } drawFrame(gr, box_x, box_y, col, weight) { @@ -574,9 +574,9 @@ class Images { let l_w; switch (weight) { case 'stnd': - x = !this.labels.right ? box_x + 1 : ui.sz.pad + 1; + x = !this.labels.right ? box_x + 1 : lib.ui.sz.pad + 1; y = box_y + (!this.labels.right ? 1 : 1); - w = !this.labels.right ? this.box.w - 2 : panel.tree.sel.w; + w = !this.labels.right ? this.box.w - 2 : lib.panel.tree.sel.w; h = this.box.h - (!this.labels.right ? 2 : 0); l_w = 2; break; @@ -601,11 +601,11 @@ class Images { drawItemOverlay(gr, item, x, y, w) { if (item.root) return; - switch (ppt.itemOverlayType) { + switch (libSet.itemOverlayType) { case 1: { if (!item.count) break; - let count_w = Math.max(gr.CalcTextWidth(`${item.count} `, ui.font.tracks), 8); - const count_h = Math.max(gr.CalcTextHeight(item.count, ui.font.tracks), 8); + let count_w = Math.max(gr.CalcTextWidth(`${item.count} `, lib.ui.font.tracks), 8); + const count_h = Math.max(gr.CalcTextHeight(item.count, lib.ui.font.tracks), 8); let count_x = x + (this.style.image != 2 ? w - count_w - 3 : (w - count_w - 2) / 2); const count_y = y + (this.style.image != 2 ? 0 : count_h / 1.67); let count = item.count; @@ -613,30 +613,30 @@ class Images { if (count_w > this.im.w) { count = item.count.split(' '); count_h2 = count_h * 2; - count_w = Math.max(gr.CalcTextWidth(count[0], ui.font.tracks), gr.CalcTextWidth(count[1], ui.font.tracks)); + count_w = Math.max(gr.CalcTextWidth(count[0], lib.ui.font.tracks), gr.CalcTextWidth(count[1], lib.ui.font.tracks)); count_x = x + (this.style.image != 2 ? w - count_w - 3 : (w - count_w - 2) / 2); gr.SetSmoothingMode(2); gr.FillSolidRect(count_x, count_y, count_w + 2, count_h2, RGBA(0, 0, 0, 115)); - gr.GdiDrawText(count[0], ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y, count_w, count_h, this.style.image != 2 ? panel.rc : panel.cc); - gr.GdiDrawText(count[1], ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y + count_h, count_w, count_h, this.style.image != 2 ? panel.rc : panel.cc); + gr.GdiDrawText(count[0], lib.ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y, count_w, count_h, this.style.image != 2 ? lib.panel.rc : lib.panel.cc); + gr.GdiDrawText(count[1], lib.ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y + count_h, count_w, count_h, this.style.image != 2 ? lib.panel.rc : lib.panel.cc); gr.SetSmoothingMode(0); } else { gr.SetSmoothingMode(2); gr.FillSolidRect(count_x, count_y, count_w + 2, count_h2, RGBA(0, 0, 0, 115)); - gr.GdiDrawText(count, ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y, count_w, count_h, panel.cc); + gr.GdiDrawText(count, lib.ui.font.tracks, RGB(255, 255, 255), count_x + 1, count_y, count_w, count_h, lib.panel.cc); gr.SetSmoothingMode(0); } break; } case 2: { if (!item.year) break; - const year_w = Math.max(gr.CalcTextWidth(`${item.year} `, ui.font.tracks), 8); - const year_h = Math.max(gr.CalcTextHeight(item.year, ui.font.tracks), 8); + const year_w = Math.max(gr.CalcTextWidth(`${item.year} `, lib.ui.font.tracks), 8); + const year_h = Math.max(gr.CalcTextHeight(item.year, lib.ui.font.tracks), 8); const year_x = x + (this.style.image != 2 ? 0 : (w - year_w - 2) / 2); const year_y = y + (this.style.image != 2 ? 0 : year_h / 1.67); gr.SetSmoothingMode(2); gr.FillSolidRect(year_x, year_y, year_w + 2, year_h, RGBA(0, 0, 0, 115)); - gr.GdiDrawText(item.year, ui.font.tracks, RGB(255, 255, 255), year_x + 1, year_y, year_w, year_h, panel.cc); + gr.GdiDrawText(item.year, lib.ui.font.tracks, RGB(255, 255, 255), year_x + 1, year_y, year_w, year_h, lib.panel.cc); gr.SetSmoothingMode(0); break; } @@ -644,54 +644,54 @@ class Images { } drawSelBg(gr, cur_img, box_x, box_y, i, nowpOrSel) { - if (this.labels.hide && (this.style.image != 2 || pop.highlight.row == 3 && ppt.frameImage)) return; + if (this.labels.hide && (this.style.image != 2 || lib.pop.highlight.row == 3 && libSet.frameImage)) return; let x; let y; let w; let h; switch (true) { case nowpOrSel: - // col = ui.col.imgBgSel; + // col = lib.ui.col.imgBgSel; switch (this.labels.overlay || this.labels.hide) { case true: x = box_x + Math.round((this.box.w - (cur_img ? cur_img.Width + 1 : this.im.w + 2)) / 2); - y = box_y + (cur_img ? ppt.thumbNailGapCompact / 2 + this.im.w - cur_img.Height + 1 : ppt.thumbNailGapCompact / 2 + 2); + y = box_y + (cur_img ? libSet.thumbNailGapCompact / 2 + this.im.w - cur_img.Height + 1 : libSet.thumbNailGapCompact / 2 + 2); w = cur_img ? cur_img.Width : this.im.w; h = cur_img ? cur_img.Height : this.im.w; break; case false: - x = !this.labels.right ? box_x : ui.x; + x = !this.labels.right ? box_x : lib.ui.x; y = box_y + (!this.labels.right ? 1 : 1); - w = !this.labels.right ? this.box.w : panel.tree.sel.w; + w = !this.labels.right ? this.box.w : lib.panel.tree.sel.w; h = this.box.h - 1; break; } break; - case pop.highlight.row == 2 && i == pop.m.i: - // col = ui.col.bg_h; + case lib.pop.highlight.row == 2 && i == lib.pop.m.i: + // col = lib.ui.col.bg_h; if ((this.labels.overlay || this.labels.hide) && this.style.image == 2) { x = box_x + Math.round((this.box.w - (cur_img ? cur_img.Width : this.im.w)) / 2); y = box_y + (cur_img ? this.im.w - cur_img.Height : 0); w = cur_img ? cur_img.Width : this.im.w; h = cur_img ? cur_img.Height : this.im.w; } else { - x = !this.labels.right ? box_x : ui.sz.pad; + x = !this.labels.right ? box_x : lib.ui.sz.pad; y = box_y + ((this.labels.overlay || this.labels.hide) ? 0 : (!this.labels.right ? 2 : 1)); - w = !this.labels.right ? this.box.w : panel.tree.sel.w; + w = !this.labels.right ? this.box.w : lib.panel.tree.sel.w; h = this.box.h + ((this.labels.overlay || this.labels.hide) ? 2 : 0); } break; } - x = this.labels.overlay ? box_x + Math.round((this.box.w - (cur_img ? cur_img.Width + 1 : this.im.w + 2)) / 2) : !this.labels.right ? box_x : ui.x; - y = this.labels.overlay ? box_y + (cur_img ? ppt.thumbNailGapCompact / 2 + this.im.w - cur_img.Height + 1 : ppt.thumbNailGapCompact / 2 + 2) : box_y + (!this.labels.right ? 1 : 1); - const coversRight = pref.libraryDesign === 'coversLabelsRight' || ppt.albumArtLabelType === 2; + x = this.labels.overlay ? box_x + Math.round((this.box.w - (cur_img ? cur_img.Width + 1 : this.im.w + 2)) / 2) : !this.labels.right ? box_x : lib.ui.x; + y = this.labels.overlay ? box_y + (cur_img ? libSet.thumbNailGapCompact / 2 + this.im.w - cur_img.Height + 1 : libSet.thumbNailGapCompact / 2 + 2) : box_y + (!this.labels.right ? 1 : 1); + const coversRight = grSet.libraryDesign === 'coversLabelsRight' || libSet.albumArtLabelType === 2; - gr.FillSolidRect(x, coversRight ? y - 1 : y, w - (sbar.w && coversRight ? SCALE(42) : 0), coversRight ? h + 2 : h, ui.col.nowPlayingBg); + gr.FillSolidRect(x, coversRight ? y - 1 : y, w - (lib.sbar.w && coversRight ? SCALE(42) : 0), coversRight ? h + 2 : h, lib.ui.col.nowPlayingBg); - if ((pref.theme !== 'white' && pref.theme !== 'black') && (!ppt.albumArtShow || ppt.albumArtShow && coversRight) || - (pref.styleBlackAndWhite || pref.styleBlackAndWhite2) && ppt.albumArtShow && coversRight) { - gr.FillSolidRect(x, y - 1, ui.sz.sideMarker, h + 2, ui.col.sideMarker); + if ((grSet.theme !== 'white' && grSet.theme !== 'black') && (!libSet.albumArtShow || libSet.albumArtShow && coversRight) || + (grSet.styleBlackAndWhite || grSet.styleBlackAndWhite2) && libSet.albumArtShow && coversRight) { + gr.FillSolidRect(x, y - 1, lib.ui.sz.sideMarker, h + 2, lib.ui.col.sideMarker); } } @@ -745,16 +745,16 @@ class Images { } getCurrentDatabase() { - this.albumArtDiskCache = ppt.albumArtDiskCache; + this.albumArtDiskCache = libSet.albumArtDiskCache; if (!this.albumArtDiskCache) return; const cacheFolder = this.cacheFolder; $Lib.buildPth(this.cachePath); this.saveSize = this.im.w > 500 ? 750 : this.im.w > 250 ? 500 : 250; this.interval = { cache: this.saveSize == 250 ? 1 : this.saveSize == 500 ? 4 : 9, - preLoad: this.saveSize == 250 ? (ppt.albumArtLabelType != 3 ? 7 : 15) : this.saveSize == 500 ? 20 : 45 + preLoad: this.saveSize == 250 ? (libSet.albumArtLabelType != 3 ? 7 : 15) : this.saveSize == 500 ? 20 : 45 } - this.cacheFolder = `${this.cachePath + ['front', 'back', 'disc', 'icon', 'artist'][ppt.artId] + (this.saveSize == 250 ? '' : this.saveSize)}\\`; + this.cacheFolder = `${this.cachePath + ['front', 'back', 'disc', 'icon', 'artist'][libSet.artId] + (this.saveSize == 250 ? '' : this.saveSize)}\\`; $Lib.create(this.cacheFolder); this.database = $Lib.jsonParse(`${this.cacheFolder}database.dat`, this.newDatabase(), 'file'); if (this.cacheFolder != cacheFolder) { @@ -784,46 +784,46 @@ class Images { } getGrpCol(item, nowp, hover) { - return nowp ? ui.col.nowp : hover ? (panel.textDiffHighlight ? ui.col.nowp : ui.col.text_h) : item.sel ? !this.labels.overlayDark ? ui.col.textSel : ui.col.text : !this.labels.overlayDark ? ui.col.text : RGB(240, 240, 240); + return nowp ? lib.ui.col.nowp : hover ? (lib.panel.textDiffHighlight ? lib.ui.col.nowp : lib.ui.col.text_h) : item.sel ? !this.labels.overlayDark ? lib.ui.col.textSel : lib.ui.col.text : !this.labels.overlayDark ? lib.ui.col.text : RGB(240, 240, 240); } getImages() { - const extraRows = this.albumArtDiskCache ? panel.rows * 2 : panel.rows; // will load any extra including those after any preLoad + const extraRows = this.albumArtDiskCache ? lib.panel.rows * 2 : lib.panel.rows; // will load any extra including those after any preLoad - if (!panel.imgView) return; + if (!lib.panel.imgView) return; this.items = []; - let begin = this.start == 0 ? ppt.rootNode ? 1 : 0 : this.start; - const end = this.end != 0 ? Math.min(this.end + this.columns * extraRows, pop.tree.length) : this.end; + let begin = this.start == 0 ? libSet.rootNode ? 1 : 0 : this.start; + const end = this.end != 0 ? Math.min(this.end + this.columns * extraRows, lib.pop.tree.length) : this.end; for (let i = begin; i < end; i++) { - if (!pop.tree[i]) continue; - const key = pop.tree[i].key; + if (!lib.pop.tree[i]) continue; + const key = lib.pop.tree[i].key; if (key && !this.cache[key]) { this.items.push({ ix: i, - handle: pop.tree[i].handle, + handle: lib.pop.tree[i].handle, key }); } } - begin = Math.max(ppt.rootNode ? 1 : 0, begin - this.columns * extraRows); + begin = Math.max(libSet.rootNode ? 1 : 0, begin - this.columns * extraRows); let i = end; while (i--) { if (i < begin) break; - if (!pop.tree[i]) continue; - const key = pop.tree[i].key; + if (!lib.pop.tree[i]) continue; + const key = lib.pop.tree[i].key; if (key && !this.cache[key]) { this.items.push({ ix: i, - handle: pop.tree[i].handle, + handle: lib.pop.tree[i].handle, key }); } } if (!this.items.length) return; - let interval = !sbar.bar.isDragging && !sbar.touch.dn ? 5 : 50; + let interval = !lib.sbar.bar.isDragging && !lib.sbar.touch.dn ? 5 : 50; const allCached = this.albumArtDiskCache ? this.items.every(v => v.key && this.database[v.key]) : false; if (allCached) interval = this.interval.cache; @@ -846,7 +846,7 @@ class Images { img: 'called', accessed: ++this.accessed }; - if (v.handle) this.get_album_art_async(v.handle, ppt.artId, key, v.ix); + if (v.handle) this.get_album_art_async(v.handle, libSet.artId, key, v.ix); } } j++; @@ -865,10 +865,10 @@ class Images { } getItem(i) { - if (!pop.tree[i]) { + if (!lib.pop.tree[i]) { return null; } - const key = pop.tree[i].key; + const key = lib.pop.tree[i].key; if (!this.cache[key] && this.database[key] && this.database[key] != 'noAlbumArt') { if ($Lib.file(this.cacheFolder + this.database[key])) { // cacheItPreload if file exists return { @@ -883,23 +883,23 @@ class Images { getItemsToDraw(preLoad) { switch (true) { case this.style.vertical: - if (pop.tree.length <= panel.rows * this.columns) { + if (lib.pop.tree.length <= lib.panel.rows * this.columns) { this.start = 0; - this.end = pop.tree.length; + this.end = lib.pop.tree.length; } else { - this.start = Math.round(sbar.delta / this.row.h) * this.columns; + this.start = Math.round(lib.sbar.delta / this.row.h) * this.columns; this.start = $Lib.clamp(this.start, 0, this.start - this.columns); - this.end = Math.ceil((sbar.delta + this.panel.h) / this.row.h) * this.columns; - this.end = Math.min(this.end, pop.tree.length); + this.end = Math.ceil((lib.sbar.delta + this.panel.h) / this.row.h) * this.columns; + this.end = Math.min(this.end, lib.pop.tree.length); } break; case !this.style.vertical: - if (pop.tree.length <= panel.rows) { + if (lib.pop.tree.length <= lib.panel.rows) { this.start = 0; - this.end = pop.tree.length; + this.end = lib.pop.tree.length; } else { - this.start = Math.round(sbar.delta / this.blockWidth); - this.end = Math.min(this.start + panel.rows + 2, pop.tree.length); + this.start = Math.round(lib.sbar.delta / this.blockWidth); + this.end = Math.min(this.start + lib.panel.rows + 2, lib.pop.tree.length); this.start = $Lib.clamp(this.start, 0, this.start - 1); } break; @@ -908,7 +908,7 @@ class Images { } getLotCol(item, nowp, hover) { - return nowp ? ui.col.nowp : hover ? (panel.textDiffHighlight ? ui.col.nowp : ui.col.text_h) : item.sel ? !this.labels.overlayDark ? ui.col.selBlend : ui.col.lotBlend : !this.labels.overlayDark ? ui.col.lotBlend : RGB(220, 220, 220); + return nowp ? lib.ui.col.nowp : hover ? (lib.panel.textDiffHighlight ? lib.ui.col.nowp : lib.ui.col.text_h) : item.sel ? !this.labels.overlayDark ? lib.ui.col.selBlend : lib.ui.col.lotBlend : !this.labels.overlayDark ? lib.ui.col.lotBlend : RGB(220, 220, 220); } getMostFrequentField(arr) { @@ -918,7 +918,7 @@ class Images { }, {}); const maxCount = Math.max(...Object.values(counts)); const mostFrequent = Object.keys(counts).filter(k => counts[k] === maxCount); - return panel.grp[ppt.viewBy].type.includes(mostFrequent[0]) ? mostFrequent[0] : ''; + return lib.panel.grp[libSet.viewBy].type.includes(mostFrequent[0]) ? mostFrequent[0] : ''; } getShadow() { @@ -933,17 +933,17 @@ class Images { this.shadow.StackBlur(4); } wh = this.im.w * 0.985; // always drawn at actual size - if (ppt.artId == 4) { - if (ppt.curNoArtistImg == 0 || ppt.curNoArtistImg == 2 || this.style.image == 2) { + if (libSet.artId == 4) { + if (libSet.curNoArtistImg == 0 || libSet.curNoArtistImg == 2 || this.style.image == 2) { this.shadowStub = $Lib.gr(sz, sz, true, g => g.FillEllipse(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); this.shadowStub.StackBlur(4); - } else if (ppt.curNoArtistImg != 4) { + } else if (libSet.curNoArtistImg != 4) { this.shadowStub = $Lib.gr(sz, sz, true, g => g.FillSolidRect(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); this.shadowStub.StackBlur(5); } else { this.shadowStub = null; } - } else if (ppt.curNoCoverImg > 2) { + } else if (libSet.curNoCoverImg > 2) { this.shadowStub = $Lib.gr(sz, sz, true, g => g.FillSolidRect(xy, xy, wh, wh, RGBA(0, 0, 0, 128))); this.shadowStub.StackBlur(5); } else { @@ -959,129 +959,129 @@ class Images { } getSelBgCol(item, nowp) { - return nowp || item.sel ? this.albumArtShowLabels ? ui.col.imgBgSel : ui.col.imgOverlaySel : RGBA(0, 0, 0, 175); + return nowp || item.sel ? this.albumArtShowLabels ? lib.ui.col.imgBgSel : lib.ui.col.imgOverlaySel : RGBA(0, 0, 0, 175); } getStyle() { - switch (ppt.artId) { + switch (libSet.artId) { case 0: - return ppt.imgStyleFront; + return libSet.imgStyleFront; case 1: - return ppt.imgStyleBack; + return libSet.imgStyleBack; case 2: - return ppt.imgStyleDisc; + return libSet.imgStyleDisc; case 3: - return ppt.imgStyleIcon; + return libSet.imgStyleIcon; case 4: - return ppt.imgStyleArtist; + return libSet.imgStyleArtist; } } load() { - const albumArtGrpNames = $Lib.jsonParse(ppt.albumArtGrpNames, {}); + const albumArtGrpNames = $Lib.jsonParse(libSet.albumArtGrpNames, {}); const fields = []; - const mod = pop.tree.length < 1000 ? 1 : pop.tree.length < 3500 ? Math.round(pop.tree.length / 1000) : 3; + const mod = lib.pop.tree.length < 1000 ? 1 : lib.pop.tree.length < 3500 ? Math.round(lib.pop.tree.length / 1000) : 3; const tf_d = FbTitleFormat('[$year(%date%)]'); - this.groupField = albumArtGrpNames[`${panel.grp[ppt.viewBy].type.trim()}${panel.lines}`]; + this.groupField = albumArtGrpNames[`${lib.panel.grp[libSet.viewBy].type.trim()}${lib.panel.lines}`]; - pop.tree.forEach((v, i) => { + lib.pop.tree.forEach((v, i) => { const item = v.item[0].start; - if (item >= panel.list.Count) return; - const handle = panel.list[item]; + if (item >= lib.panel.list.Count) return; + const handle = lib.panel.list[item]; v.handle = handle; - const arr = pop.tree[i].name.split('^@^'); - v.grp = panel.lines == 1 || !ppt.albumArtFlipLabels ? arr[0] : arr[1]; - v.lot = panel.lines == 2 ? !ppt.albumArtFlipLabels ? arr[1] : arr[0] : ''; - v.key = md5.hashStr(handle.Path + handle.SubSong + (panel.lines == 1 ? (arr[0] || 'Unknown') : (`${arr[0] || 'Unknown'} - ${arr[1] || 'Unknown'}`)) + ppt.artId); - if (ppt.itemOverlayType == 2) v.year = tf_d.EvalWithMetadb(handle).replace('0000', ''); - if (!this.groupField && !panel.folderView && i % mod === 0) this.getField(handle, panel.lines == 1 || ppt.albumArtFlipLabels ? v.grp : v.lot, fields); + const arr = lib.pop.tree[i].name.split('^@^'); + v.grp = lib.panel.lines == 1 || !libSet.albumArtFlipLabels ? arr[0] : arr[1]; + v.lot = lib.panel.lines == 2 ? !libSet.albumArtFlipLabels ? arr[1] : arr[0] : ''; + v.key = libMD5.hashStr(handle.Path + handle.SubSong + (lib.panel.lines == 1 ? (arr[0] || 'Unknown') : (`${arr[0] || 'Unknown'} - ${arr[1] || 'Unknown'}`)) + libSet.artId); + if (libSet.itemOverlayType == 2) v.year = tf_d.EvalWithMetadb(handle).replace('0000', ''); + if (!this.groupField && !lib.panel.folderView && i % mod === 0) this.getField(handle, lib.panel.lines == 1 || libSet.albumArtFlipLabels ? v.grp : v.lot, fields); }); - if (!this.groupField && !panel.folderView) { + if (!this.groupField && !lib.panel.folderView) { this.groupField = this.getMostFrequentField(fields) || 'Item'; this.groupField = $Lib.titlecase(this.groupField); } - if (ppt.rootNode) { - if (!pop.tree[0]) return; + if (libSet.rootNode) { + if (!lib.pop.tree[0]) return; if (!this.groupField) this.groupField = 'Item'; const plurals = this.groupField.split(' ').map(v => pluralize(v)); const pluralField = plurals.join(' ').replace(/(album|artist|top|track)s\s/gi, '$1 ').replace(/(similar artist)\s/gi, '$1s ').replace(/years - albums/gi, 'Year - Albums'); - pop.tree[0].key = pop.tree[0].name; - const ln1 = pop.tree.length - 1; - const ln2 = panel.list.Count; - const nm = `${!ppt.showSource ? 'All' : panel.sourceName} (${ln1}${ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`})`; - if (ppt.rootNode == 3) pop.tree[0].grp = nm; - else if (panel.lines == 1) pop.tree[0].grp = panel.rootName + (ppt.nodeCounts ? ` (${ppt.nodeCounts == 2 && ppt.rootNode != 3 ? ln1 + (ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`) : ln2 + (ln2 > 1 ? ' tracks' : ' track')})` : ''); - if (panel.lines == 2) { - if (ppt.rootNode != 3) pop.tree[0].grp = panel.rootName; - pop.tree[0].lot = ppt.nodeCounts == 2 && ppt.rootNode != 3 ? ln1 + (ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`) : ln2 + (ln2 > 1 ? ' tracks' : ' track'); + lib.pop.tree[0].key = lib.pop.tree[0].name; + const ln1 = lib.pop.tree.length - 1; + const ln2 = lib.panel.list.Count; + const nm = `${!libSet.showSource ? 'All' : lib.panel.sourceName} (${ln1}${ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`})`; + if (libSet.rootNode == 3) lib.pop.tree[0].grp = nm; + else if (lib.panel.lines == 1) lib.pop.tree[0].grp = lib.panel.rootName + (libSet.nodeCounts ? ` (${libSet.nodeCounts == 2 && libSet.rootNode != 3 ? ln1 + (ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`) : ln2 + (ln2 > 1 ? ' tracks' : ' track')})` : ''); + if (lib.panel.lines == 2) { + if (libSet.rootNode != 3) lib.pop.tree[0].grp = lib.panel.rootName; + lib.pop.tree[0].lot = libSet.nodeCounts == 2 && libSet.rootNode != 3 ? ln1 + (ln1 > 1 ? ` ${pluralField}` : ` ${this.groupField}`) : ln2 + (ln2 > 1 ? ' tracks' : ' track'); } } this.metrics(); - panel.treePaint(); + lib.panel.treePaint(); } memoryLimit() { if (!window.JsMemoryStats) return; - const limit = !ppt.memoryLimit ? window.JsMemoryStats.TotalMemoryLimit * 0.5 : Math.min(ppt.memoryLimit * 1048576, window.JsMemoryStats.TotalMemoryLimit * 0.8); + const limit = !libSet.memoryLimit ? window.JsMemoryStats.TotalMemoryLimit * 0.5 : Math.min(libSet.memoryLimit * 1048576, window.JsMemoryStats.TotalMemoryLimit * 0.8); return window.JsMemoryStats.TotalMemoryUsage > limit; } metrics() { - if (!ui.w || !ui.h) return; + if (!lib.ui.w || !lib.ui.h) return; $Lib.gr(1, 1, false, g => { - const lineSpacing = this.labels.hide || this.labels.overlay ? Math.max(ppt.verticalAlbumArtPad - 2, 0) : ppt.verticalAlbumArtPad; - this.letter.w = Math.round(g.CalcTextWidth('W', ui.font.main)); - this.text.h = Math.max(Math.round(g.CalcTextHeight('String', ui.font.group)) + lineSpacing, Math.round(g.CalcTextHeight('String', ui.font.lot)) + lineSpacing, 10); + const lineSpacing = this.labels.hide || this.labels.overlay ? Math.max(libSet.verticalAlbumArtPad - 2, 0) : libSet.verticalAlbumArtPad; + this.letter.w = Math.round(g.CalcTextWidth('W', lib.ui.font.main)); + this.text.h = Math.max(Math.round(g.CalcTextHeight('String', lib.ui.font.group)) + lineSpacing, Math.round(g.CalcTextHeight('String', lib.ui.font.lot)) + lineSpacing, 10); }); this.style = { - dropShadow: ppt.albumArtDropShadow && ppt.albumArtLabelType != 3, - dropShadowStub: ppt.albumArtDropShadow && ppt.albumArtLabelType != 3 && (ppt.artId == 4 || ppt.curNoCoverImg > 2), + dropShadow: libSet.albumArtDropShadow && libSet.albumArtLabelType != 3, + dropShadowStub: libSet.albumArtDropShadow && libSet.albumArtLabelType != 3 && (libSet.artId == 4 || libSet.curNoCoverImg > 2), image: this.getStyle(), - rootComposite: ppt.rootNode && ppt.curRootImg == 3, - vertical: !ppt.albumArtFlowMode ? true : ui.h - panel.search.h > ui.w - ui.sbar.w + rootComposite: libSet.rootNode && libSet.curRootImg == 3, + vertical: !libSet.albumArtFlowMode ? true : lib.ui.h - lib.panel.search.h > lib.ui.w - lib.ui.sbar.w } - this.style.dropGrad = ppt.albumArtDropShadow && !this.style.dropShadow; - this.style.dropGradStub = ppt.albumArtDropShadow && !this.style.dropShadowStub; + this.style.dropGrad = libSet.albumArtDropShadow && !this.style.dropShadow; + this.style.dropGradStub = libSet.albumArtDropShadow && !this.style.dropShadowStub; - this.letter.show = ppt.albumArtLetter; - this.letter.no = ppt.albumArtLetterNo; - this.letter.albumArtYearAuto = ppt.albumArtYearAuto; + this.letter.show = libSet.albumArtLetter; + this.letter.no = libSet.albumArtLetterNo; + this.letter.albumArtYearAuto = libSet.albumArtYearAuto; switch (this.style.vertical) { case true: { this.labels = { - hide: !ppt.albumArtLabelType, - bottom: ppt.albumArtLabelType == 1 || ppt.albumArtFlowMode && ppt.albumArtLabelType == 2, - right: !ppt.albumArtFlowMode ? ppt.albumArtLabelType == 2 : false, - overlay: ppt.albumArtLabelType == 3 || ppt.albumArtLabelType == 4, - overlayDark: ppt.albumArtLabelType == 4, - flip: ppt.albumArtFlipLabels, - statistics: ppt.itemShowStatistics ? 1 : 0 + hide: !libSet.albumArtLabelType, + bottom: libSet.albumArtLabelType == 1 || libSet.albumArtFlowMode && libSet.albumArtLabelType == 2, + right: !libSet.albumArtFlowMode ? libSet.albumArtLabelType == 2 : false, + overlay: libSet.albumArtLabelType == 3 || libSet.albumArtLabelType == 4, + overlayDark: libSet.albumArtLabelType == 4, + flip: libSet.albumArtFlipLabels, + statistics: libSet.itemShowStatistics ? 1 : 0 }; - this.bor.pad = !this.labels.hide && !this.labels.overlay ? (ppt.thumbNailGapStnd == 0 ? Math.round(this.text.h * (!this.labels.right && pref.libraryThumbnailSize !== 'playlist' ? 1.05 : 0.75)) : ppt.thumbNailGapStnd - Math.round(2 * $Lib.scale)) : ppt.thumbNailGapCompact; + this.bor.pad = !this.labels.hide && !this.labels.overlay ? (libSet.thumbNailGapStnd == 0 ? Math.round(this.text.h * (!this.labels.right && grSet.libraryThumbnailSize !== 'playlist' ? 1.05 : 0.75)) : libSet.thumbNailGapStnd - Math.round(2 * $Lib.scale)) : libSet.thumbNailGapCompact; this.im.offset = Math.round(!this.labels.hide && !this.labels.overlay ? this.bor.pad / 2 : -2); if (this.labels.hide || this.labels.overlay) { - this.panel.y = panel.search.h + Math.round(this.bor.pad / 2); + this.panel.y = lib.panel.search.h + Math.round(this.bor.pad / 2); this.bor.bot = 0; this.bor.side = 0; - this.bor.cov = ppt.thumbNailGapCompact; + this.bor.cov = libSet.thumbNailGapCompact; } else { - this.panel.y = panel.search.h; + this.panel.y = lib.panel.search.h; this.bor.cov = Math.round(this.bor.pad / 2); this.bor.side = Math.round(2 * $Lib.scale); this.bor.bot = this.bor.side * 2; } - const margin = ppt.margin; - this.panel.x = (ppt.sbarShow != 2 ? Math.max(margin, ui.sbar.w) : margin) + ui.l.w - SCALE(3); - this.panel.w = ui.w - ui.l.w * 2 - (ui.sbar.type == 0 || ppt.sbarShow != 2 ? Math.max(margin, ui.sbar.w) * 2 + SCALE(20) : (margin * 2 + ui.sbar.w) + SCALE(20)); - this.panel.h = ui.h - this.panel.y; + const margin = libSet.margin; + this.panel.x = (libSet.sbarShow != 2 ? Math.max(margin, lib.ui.sbar.w) : margin) + lib.ui.l.w - SCALE(3); + this.panel.w = lib.ui.w - lib.ui.l.w * 2 - (lib.ui.sbar.type == 0 || libSet.sbarShow != 2 ? Math.max(margin, lib.ui.sbar.w) * 2 + SCALE(20) : (margin * 2 + lib.ui.sbar.w) + SCALE(20)); + this.panel.h = lib.ui.h - this.panel.y; - this.blockWidth = pref.libraryThumbnailSize === 'playlist' ? playlistThumbSize + (this.bor.side * 2 + this.bor.cov * 2) : - Math.round(ui.row.h * 4 * $Lib.scale * ppt.zoomImg / 100 * + this.blockWidth = grSet.libraryThumbnailSize === 'playlist' ? pl.thumbnail_size + (this.bor.side * 2 + this.bor.cov * 2) : + Math.round(lib.ui.row.h * 4 * $Lib.scale * libSet.zoomImg / 100 * [// Thumbnail size 0.66, // Mini 1, // Small @@ -1091,13 +1091,13 @@ class Images { 3, // XL 3.5, // XXL 5 // MAX - ][ppt.thumbNailSize]); + ][libSet.thumbNailSize]); - this.columns = ppt.albumArtFlowMode || this.labels.right ? 1 : Math.max(Math.floor(this.panel.w / this.blockWidth), 1); + this.columns = libSet.albumArtFlowMode || this.labels.right ? 1 : Math.max(Math.floor(this.panel.w / this.blockWidth), 1); let gap = this.panel.w - this.columns * this.blockWidth; gap = Math.floor(gap / this.columns); - this.columnWidth = !this.labels.right ? $Lib.clamp(this.blockWidth + (pref.libraryThumbnailSize === 'playlist' ? 0 : gap), 10, Math.min(this.panel.w, this.panel.h)) : $Lib.clamp(this.blockWidth, 10, Math.min(this.panel.w, this.panel.h)); - this.overlayHeight = !this.labels.overlay ? 0 : (panel.lines != 2 ? this.text.h * (1.2 + this.labels.statistics) : Math.round(this.text.h * (2.1 + this.labels.statistics))); + this.columnWidth = !this.labels.right ? $Lib.clamp(this.blockWidth + (grSet.libraryThumbnailSize === 'playlist' ? 0 : gap), 10, Math.min(this.panel.w, this.panel.h)) : $Lib.clamp(this.blockWidth, 10, Math.min(this.panel.w, this.panel.h)); + this.overlayHeight = !this.labels.overlay ? 0 : (lib.panel.lines != 2 ? this.text.h * (1.2 + this.labels.statistics) : Math.round(this.text.h * (2.1 + this.labels.statistics))); this.im.w = Math.round(Math.max(this.columnWidth - this.bor.side * 2 - this.bor.cov * 2 - (this.labels.hide || this.labels.overlay ? 1 : 0), 10)); if (this.labels.hide || this.labels.overlay) { @@ -1105,7 +1105,7 @@ class Images { this.row.h = this.im.w + this.bor.cov; } else { this.im.w = Math.round(Math.max(this.columnWidth - this.bor.cov * 2 - this.bor.side * 2, 10)); - this.row.h = !this.labels.right ? this.im.w + this.text.h * (panel.lines + this.labels.statistics) + this.bor.cov * 2 + this.bor.side * 2 : this.im.w + this.bor.pad + 2; + this.row.h = !this.labels.right ? this.im.w + this.text.h * (lib.panel.lines + this.labels.statistics) + this.bor.cov * 2 + this.bor.side * 2 : this.im.w + this.bor.pad + 2; } if (this.row.h > this.panel.h) { this.im.w -= this.row.h - this.panel.h; @@ -1114,27 +1114,27 @@ class Images { } this.box.w = this.columnWidth - this.bor.side * 2; this.box.h = this.row.h - (!this.labels.right ? this.bor.side * 2 : 0); - panel.rows = Math.max(Math.floor(this.panel.h / this.row.h)); - sbar.metrics(sbar.x, sbar.y, sbar.w, sbar.h, panel.rows, this.row.h, this.style.vertical); - sbar.setRows(Math.ceil(pop.tree.length / this.columns)); + lib.panel.rows = Math.max(Math.floor(this.panel.h / this.row.h)); + lib.sbar.metrics(lib.sbar.x, lib.sbar.y, lib.sbar.w, lib.sbar.h, lib.panel.rows, this.row.h, this.style.vertical); + lib.sbar.setRows(Math.ceil(lib.pop.tree.length / this.columns)); break; } case false: { // only H-Flow this.labels = { - hide: !ppt.albumArtLabelType, - bottom: ppt.albumArtLabelType == 1 || ppt.albumArtLabelType == 2, + hide: !libSet.albumArtLabelType, + bottom: libSet.albumArtLabelType == 1 || libSet.albumArtLabelType == 2, right: false, - overlay: ppt.albumArtLabelType == 3 || ppt.albumArtLabelType == 4, - overlayDark: ppt.albumArtLabelType == 4, - flip: ppt.albumArtFlipLabels, - statistics: ppt.itemShowStatistics ? 1 : 0 + overlay: libSet.albumArtLabelType == 3 || libSet.albumArtLabelType == 4, + overlayDark: libSet.albumArtLabelType == 4, + flip: libSet.albumArtFlipLabels, + statistics: libSet.itemShowStatistics ? 1 : 0 }; - this.bor.pad = !this.labels.hide && !this.labels.overlay ? (ppt.thumbNailGapStnd == 0 ? Math.round(this.text.h * 1.05) : ppt.thumbNailGapStnd - Math.round(2 * $Lib.scale)) : ppt.thumbNailGapCompact; + this.bor.pad = !this.labels.hide && !this.labels.overlay ? (libSet.thumbNailGapStnd == 0 ? Math.round(this.text.h * 1.05) : libSet.thumbNailGapStnd - Math.round(2 * $Lib.scale)) : libSet.thumbNailGapCompact; this.im.offset = Math.round(!this.labels.hide && !this.labels.overlay ? this.bor.pad / 2 : -2); if (this.labels.hide || this.labels.overlay) { this.bor.bot = 0; this.bor.side = 0; - this.bor.cov = ppt.thumbNailGapCompact; + this.bor.cov = libSet.thumbNailGapCompact; } else { this.bor.cov = Math.round(this.bor.pad / 2); this.bor.side = Math.round(2 * $Lib.scale); @@ -1142,13 +1142,13 @@ class Images { } this.panel.x = 0; const spacer = this.letter.show ? (this.labels.bottom ? this.text.h * 0.5 - this.bor.pad / 4 : this.text.h * 0.75) : (this.labels.bottom ? 0 : Math.round(this.bor.pad / 2)); - this.panel.y = panel.search.h + spacer - SCALE(3); - this.panel.h = ui.h - this.panel.y - ui.l.w * 3 - spacer - ui.sbar.w - SCALE(34); + this.panel.y = lib.panel.search.h + spacer - SCALE(3); + this.panel.h = lib.ui.h - this.panel.y - lib.ui.l.w * 3 - spacer - lib.ui.sbar.w - SCALE(34); - this.panel.w = ui.w; + this.panel.w = lib.ui.w; if (!this.labels.hide && !this.labels.overlay) { this.row.h = this.panel.h; - const extra = this.text.h * (panel.lines + this.labels.statistics) + this.bor.cov * 2 + this.bor.side * 2; + const extra = this.text.h * (lib.panel.lines + this.labels.statistics) + this.bor.cov * 2 + this.bor.side * 2; this.im.w = Math.min(this.panel.h - extra, this.panel.h - extra); this.im.w = $Lib.clamp(this.im.w, 10, Math.round(this.panel.w - (this.bor.cov * 2 + this.bor.side * 2))); this.blockWidth = this.im.w + this.bor.cov * 2 + this.bor.side * 2; @@ -1161,50 +1161,50 @@ class Images { this.row.h = this.im.w + extra; } this.columns = Math.max(Math.floor(this.panel.w / this.blockWidth), 1); - this.overlayHeight = !this.labels.overlay ? 0 : (panel.lines != 2 ? this.text.h * (1.2 + this.labels.statistics) : Math.round(this.text.h * (2.1 + this.labels.statistics))); + this.overlayHeight = !this.labels.overlay ? 0 : (lib.panel.lines != 2 ? this.text.h * (1.2 + this.labels.statistics) : Math.round(this.text.h * (2.1 + this.labels.statistics))); this.box.w = this.blockWidth - this.bor.side * 2; this.box.h = this.row.h - this.bor.bot; - panel.rows = Math.max(Math.floor(this.panel.w / this.blockWidth)); + lib.panel.rows = Math.max(Math.floor(this.panel.w / this.blockWidth)); this.columnWidth = this.blockWidth; - sbar.metrics(sbar.x, sbar.y, ui.w, ui.sbar.w, panel.rows, this.blockWidth, this.style.vertical); - sbar.setRows(Math.ceil(pop.tree.length)); + lib.sbar.metrics(lib.sbar.x, lib.sbar.y, lib.ui.w, lib.ui.sbar.w, lib.panel.rows, this.blockWidth, this.style.vertical); + lib.sbar.setRows(Math.ceil(lib.pop.tree.length)); break; } } this.cellWidth = Math.max(200, this.im.w / 2); - this.labels.counts = ppt.itemOverlayType != 1 && ppt.nodeCounts; - this.style.y = this.style.vertical ? Math.floor(this.panel.y + (!this.labels.hide && !this.labels.overlay ? ppt.thumbNailGapStnd / 2 : ppt.thumbNailGapCompact / 2)) : this.panel.y; + this.labels.counts = libSet.itemOverlayType != 1 && libSet.nodeCounts; + this.style.y = this.style.vertical ? Math.floor(this.panel.y + (!this.labels.hide && !this.labels.overlay ? libSet.thumbNailGapStnd / 2 : libSet.thumbNailGapCompact / 2)) : this.panel.y; if (this.style.dropShadow) this.getShadow(); if (!this.labels.hide) { if (!this.labels.overlay) { this.text.x = !this.labels.right ? Math.round((this.box.w - this.im.w) / 2) : Math.max(Math.round((this.box.w - this.im.w) / 2), 5 * $Lib.scale) * 2 + this.im.w; - this.text.y1 = !this.labels.right ? this.im.w + Math.round(this.bor.cov * 0.5) : Math.round((this.im.w - this.text.h * panel.lines) / 2) - (this.labels.statistics ? this.text.h / 2 : 0); + this.text.y1 = !this.labels.right ? this.im.w + Math.round(this.bor.cov * 0.5) : Math.round((this.im.w - this.text.h * lib.panel.lines) / 2) - (this.labels.statistics ? this.text.h / 2 : 0); this.text.y2 = !this.labels.right ? Math.round(this.text.y1 + this.text.h * 0.95) : this.text.y1 + this.text.h; this.text.y3 = !this.labels.right ? Math.round(this.text.y2 + this.text.h * 0.95) : this.text.y2 + this.text.h; this.text.w = !this.labels.right ? this.im.w : this.panel.w - this.text.x - 12; } else { - this.text.x = Math.round(10 + (ppt.thumbNailGapCompact - 3) / 2); - this.text.y1 = Math.round(this.im.w - this.overlayHeight + 2 + (this.overlayHeight - this.text.h * (panel.lines + this.labels.statistics)) / 2); - this.text.w = this.box.w - 20 - ppt.thumbNailGapCompact - 6; - ppt.thumbNailGapCompact = 22; + this.text.x = Math.round(10 + (libSet.thumbNailGapCompact - 3) / 2); + this.text.y1 = Math.round(this.im.w - this.overlayHeight + 2 + (this.overlayHeight - this.text.h * (lib.panel.lines + this.labels.statistics)) / 2); + this.text.w = this.box.w - 20 - libSet.thumbNailGapCompact - 6; + libSet.thumbNailGapCompact = 22; } } - this.cachesize.min = panel.rows * this.columns * 3 + (this.albumArtDiskCache ? panel.rows * 2 : panel.rows) * this.columns * 2; + this.cachesize.min = lib.panel.rows * this.columns * 3 + (this.albumArtDiskCache ? lib.panel.rows * 2 : lib.panel.rows) * this.columns * 2; this.createImages(); this.getCurrentDatabase(); - if (ppt.albumArtPreLoad && !this.zooming && this.albumArtDiskCache) this.getItemsToDraw(true); + if (libSet.albumArtPreLoad && !this.zooming && this.albumArtDiskCache) this.getItemsToDraw(true); this.setNoArtist(); this.setNoCover(); this.setRoot(); if (this.style.rootComposite) this.checkRootImg(); - const stub = ppt.artId != 4 ? this.no_cover_img : this.no_artist_img; - if (stub) this.stub.noImg = this.format(stub, ppt.artId, ['default', 'default', 'circular'][this.style.image], this.im.w, this.im.w, ppt.albumArtLabelType == 3, 'noImg'); - if (this.root_img) this.stub.root = this.format(this.root_img, ppt.artId, 'default', this.im.w, this.im.w, ppt.albumArtLabelType == 3, 'root'); - panel.treePaint(); + const stub = libSet.artId != 4 ? this.no_cover_img : this.no_artist_img; + if (stub) this.stub.noImg = this.format(stub, libSet.artId, ['default', 'default', 'circular'][this.style.image], this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'noImg'); + if (this.root_img) this.stub.root = this.format(this.root_img, libSet.artId, 'default', this.im.w, this.im.w, libSet.albumArtLabelType == 3, 'root'); + lib.panel.treePaint(); } needTrim(n, ratio) { @@ -1218,7 +1218,7 @@ class Images { } on_key_down() { - this.zooming = vk.k('zoom'); + this.zooming = lib.vk.k('zoom'); if (this.zooming) { clearInterval(this.timer.preLoad); this.timer.preLoad = null; @@ -1226,18 +1226,18 @@ class Images { } on_key_up() { - if (this.zooming && this.zooming != vk.k('zoom')) { + if (this.zooming && this.zooming != lib.vk.k('zoom')) { this.zooming = false; - if (ppt.albumArtPreLoad && this.albumArtDiskCache && panel.imgView) this.metrics(); - panel.treePaint(); + if (libSet.albumArtPreLoad && this.albumArtDiskCache && lib.panel.imgView) this.metrics(); + lib.panel.treePaint(); } } preLoad() { - if (!panel.imgView) return; + if (!lib.panel.imgView) return; this.preLoadItems = []; - const begin = this.start == 0 ? ppt.rootNode ? 1 : 0 : this.start; - const end = this.end != 0 ? Math.min(this.end + this.columns, pop.tree.length) : this.end; + const begin = this.start == 0 ? libSet.rootNode ? 1 : 0 : this.start; + const end = this.end != 0 ? Math.min(this.end + this.columns, lib.pop.tree.length) : this.end; for (let i = begin; i < end; i++) { const v = this.getItem(i); @@ -1253,10 +1253,10 @@ class Images { } } - const upBegin = this.start == 0 ? ppt.rootNode ? 1 : 0 : this.start - 1; - const upEnd = ppt.rootNode ? 1 : 0; - const downBegin = this.end != 0 ? Math.min(this.end + 1 + this.columns, pop.tree.length) : this.end; - const downEnd = pop.tree.length; + const upBegin = this.start == 0 ? libSet.rootNode ? 1 : 0 : this.start - 1; + const upEnd = libSet.rootNode ? 1 : 0; + const downBegin = this.end != 0 ? Math.min(this.end + 1 + this.columns, lib.pop.tree.length) : this.end; + const downEnd = lib.pop.tree.length; const doPreload = () => { clearInterval(this.timer.preLoad); @@ -1304,7 +1304,7 @@ class Images { refresh(items) { let itemsToRemove = []; if (items === 'all') { - if (!ppt.albumArtDiskCache) return; + if (!libSet.albumArtDiskCache) return; const continue_confirmation = (status, confirmed) => { if (confirmed) { try { @@ -1318,17 +1318,17 @@ class Images { }; const caption = 'Reset All Images'; const prompt = 'This action resets the library tree thumbnail disk cache\n\nContinue?'; - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', false, 'center', continue_confirmation) : true; + const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', false, 'center', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); return; } - const allSelected = pop.sel_items.length == fb.GetLibraryItems().Count; - const base = this.cachePath + ['front', 'back', 'disc', 'icon', 'artist'][ppt.artId]; + const allSelected = lib.pop.sel_items.length == fb.GetLibraryItems().Count; + const base = this.cachePath + ['front', 'back', 'disc', 'icon', 'artist'][libSet.artId]; const databases = [`${base}\\database.dat`, `${base}500\\database.dat`, `${base}750\\database.dat`]; if (allSelected) { this.clearCache(); // full clear of working cache - if (!ppt.albumArtDiskCache) return; + if (!libSet.albumArtDiskCache) return; this.database = this.newDatabase(); // full clear of databases for current image type databases.forEach(v => { if ($Lib.file(v)) $Lib.save(v, JSON.stringify(this.newDatabase(), null, 3), true); @@ -1338,16 +1338,16 @@ class Images { // refresh selected images items.Convert().forEach(v => { - const item = panel.list.Find(v); + const item = lib.panel.list.Find(v); let ind = -1; - pop.tree.forEach((v, j) => { - if (!v.root && pop.inRange(item, v.item)) ind = j; + lib.pop.tree.forEach((v, j) => { + if (!v.root && lib.pop.inRange(item, v.item)) ind = j; }); - if (ind != -1) itemsToRemove.push(pop.tree[ind].key); + if (ind != -1) itemsToRemove.push(lib.pop.tree[ind].key); }); itemsToRemove = [...new Set(itemsToRemove)]; itemsToRemove.forEach(v => this.trimCache(v)); // clear working cache of selected keys: won't check if same images are used with other keys - if (!ppt.albumArtDiskCache) return; + if (!libSet.albumArtDiskCache) return; let imgsToRemove = itemsToRemove.map(v => this.database[v]); imgsToRemove = [...new Set(imgsToRemove)]; databases.forEach(v => { @@ -1374,42 +1374,42 @@ class Images { } setNoArtist() { - this.artist_images = my_utilsLib.getImageAssets('noArtist').sort(); - ppt.curNoArtistImg = $Lib.clamp($Lib.value(ppt.curNoArtistImg, 0, 0), 0, this.artist_images.length - 1); + this.artist_images = lib_my_utils.getImageAssets('noArtist').sort(); + libSet.curNoArtistImg = $Lib.clamp($Lib.value(libSet.curNoArtistImg, 0, 0), 0, this.artist_images.length - 1); const artistImages = this.artist_images.map(v => ({ name: utils.SplitFilePath(v)[1], path: `file://${v.replace('noArtist', 'noArtist/small')}` })); - this.no_artist_img = gdi.Image(this.artist_images[ppt.curNoArtistImg]); - ppt.noArtistImages = JSON.stringify(artistImages); + this.no_artist_img = gdi.Image(this.artist_images[libSet.curNoArtistImg]); + libSet.noArtistImages = JSON.stringify(artistImages); } setNoCover() { - this.cover_images = my_utilsLib.getImageAssets('noCover').sort(); - ppt.curNoCoverImg = $Lib.clamp($Lib.value(ppt.curNoCoverImg, 0, 0), 0, this.cover_images.length - 1); + this.cover_images = lib_my_utils.getImageAssets('noCover').sort(); + libSet.curNoCoverImg = $Lib.clamp($Lib.value(libSet.curNoCoverImg, 0, 0), 0, this.cover_images.length - 1); const coverImages = this.cover_images.map(v => ({ name: utils.SplitFilePath(v)[1], path: `file://${v.replace('noCover', 'noCover/small')}` })); - this.no_cover_img = gdi.Image(this.cover_images[ppt.curNoCoverImg]); - ppt.noCoverImages = JSON.stringify(coverImages); + this.no_cover_img = gdi.Image(this.cover_images[libSet.curNoCoverImg]); + libSet.noCoverImages = JSON.stringify(coverImages); } setRoot() { - this.root_images = my_utilsLib.getImageAssets('root').sort(); - ppt.curRootImg = $Lib.clamp($Lib.value(ppt.curRootImg, 0, 0), 0, this.root_images.length - 1); + this.root_images = lib_my_utils.getImageAssets('root').sort(); + libSet.curRootImg = $Lib.clamp($Lib.value(libSet.curRootImg, 0, 0), 0, this.root_images.length - 1); const rootImages = this.root_images.map(v => ({ name: utils.SplitFilePath(v)[1], path: `file://${v.replace('root', 'root/small')}` })); - if (ppt.rootNode && ppt.curRootImg == 3) { + if (libSet.rootNode && libSet.curRootImg == 3) { this.style.rootComposite = true; this.root_img = null; } else { this.style.rootComposite = false; - this.root_img = gdi.Image(this.root_images[ppt.curRootImg]); + this.root_img = gdi.Image(this.root_images[libSet.curRootImg]); } - ppt.rootImages = JSON.stringify(rootImages); + libSet.rootImages = JSON.stringify(rootImages); } sort(data, prop) { @@ -1427,4 +1427,10 @@ class Images { delete this.cache[key]; } } -const img = new Images(); + +/** + * The instance of `LibImages` class for library image operations. + * @typedef {LibImages} + * @global + */ +const libImg = new LibImages(); diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-initialise.js b/profile/georgia-reborn/scripts/Library/scripts/lib-initialise.js index d9c83c42..fac649eb 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-initialise.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-initialise.js @@ -1,30 +1,39 @@ 'use strict'; -let pop; -/** @type {UserInterface} */ -let ui = new UserInterface(); -/** @type {Panel} */ -let panel = new Panel(); -/** @type {Scrollbar} */ -let sbar = new Scrollbar(); -/** @type {Vkeys} */ -let vk = new Vkeys(); -/** @type {Library} */ -let lib = new Library(); -/** @type {Populate} */ -pop = new Populate(); -/** @type {Search} */ -let search = new Search(); -/** @type {Find} */ -let find = new Find(); -/** @type {Buttons} */ -let but = new Buttons(); -/** @type {PopUpBox} */ -let popUpBox = new PopUpBox(); -/** @type {MenuItems} */ -let men = new MenuItems(); -/** @type {Timers} */ -let timer = new Timers(); +/** + * A collection of all library class instances. + * @typedef {object} lib - The library main object. + * @property {LibUserInterface} ui - The instance of `LibUserInterface` class for ui operations. + * @property {LibPanel} panel - The instance of `LibPanel` class for panel operations. + * @property {LibScrollbar} sbar - The instance of `LibScrollbar` class for scrollbar operations. + * @property {LibVkeys} vk - The instance of `LibVkeys` class for vkeys operations. + * @property {LibLibrary} lib - The instance of `LibLibrary` class for lib operations. + * @property {LibPopulate} pop - The instance of `LibPopulate` class for populating library operations. + * @property {LibSearch} search - The instance of `LibSearch` class for search operations. + * @property {LibFind} find - The instance of `LibFind` class for find operations. + * @property {LibButtons} but - The instance of `LibButtons` class for buttons operations. + * @property {LibPopUpBox} popUpBox - The instance of `LibPopUpBox` class for popup operations. + * @property {LibMenuItems} men - The instance of `LibMenuItems` class for menu operations. + * @property {LibTimers} timer - The instance of `LibTimers` class for timer operations. + * @property {LibCallbacks} call - The instance of `LibCallbacks` class for callback operations. + */ +/** @global @type {lib} */ +const lib = {}; -// if (!ppt.get('Panel Library - Software Notice Checked', false)) fb.ShowPopupMessage('License\r\n\r\nCopyright (c) 2021-2022 WilB\r\n\r\nThe above copyright notice shall be included in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.', 'Library Tree'); -// ppt.set('Panel Library - Software Notice Checked', true); +lib.initialized = false; + +lib.ui = new LibUserInterface(); +lib.panel = new LibPanel(); +lib.sbar = new LibScrollbar(); +lib.vk = new LibVkeys(); +lib.lib = new LibLibrary(); +lib.pop = new LibPopulate(); +lib.search = new LibSearch(); +lib.find = new LibFind(); +lib.but = new LibButtons(); +lib.popUpBox = new LibPopUpBox(); +lib.men = new LibMenuItems(); +lib.timer = new LibTimers(); + +if (!libSet.get('Panel Library - Software Notice Checked', true)) fb.ShowPopupMessage('License\r\n\r\nCopyright (c) 2021-2022 WilB\r\n\r\nThe above copyright notice shall be included in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.', 'Library Tree'); + libSet.set('Panel Library - Software Notice Checked', true); diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-interface.js b/profile/georgia-reborn/scripts/Library/scripts/lib-interface.js index aa312599..9ccfd5dd 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-interface.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-interface.js @@ -2,14 +2,14 @@ window.DlgCode = 0x004; -class UserInterface { +class LibUserInterface { constructor() { this.cur_handle = null; this.dui = window.InstanceType; this.h = 0; this.w = 0; - ppt.sbarCol = $Lib.clamp(ppt.sbarCol, 0, 1); - if (!ppt.albumArtOptionsShow) ppt.albumArtShow = false; + libSet.sbarCol = $Lib.clamp(libSet.sbarCol, 0, 1); + if (!libSet.albumArtOptionsShow) libSet.albumArtShow = false; this.col = { bg1: 0x04ffffff, @@ -30,7 +30,7 @@ class UserInterface { } this.icon = { - char: ppt.iconCustom, + char: libSet.iconCustom, col_c: '', col_e: '', col_h: '', @@ -56,10 +56,10 @@ class UserInterface { } this.img = { - blendAlpha: $Lib.clamp($Lib.clamp(ppt.blurAlpha, 0, 100) * 105 / 30, 0, 255), - blurAlpha: $Lib.clamp(ppt.blurAlpha, 0, 100) / 30, + blendAlpha: $Lib.clamp($Lib.clamp(libSet.blurAlpha, 0, 100) * 105 / 30, 0, 255), + blurAlpha: $Lib.clamp(libSet.blurAlpha, 0, 100) / 30, blurLevel: 100, - covAlpha: $Lib.clamp(ppt.covAlpha * 2.55, 0, 255), + covAlpha: $Lib.clamp(libSet.covAlpha * 2.55, 0, 255), cur: null, cur_pth: '', isBlur: false, @@ -80,18 +80,18 @@ class UserInterface { }; this.sbar = { - arrowPad: ppt.sbarPad, + arrowPad: libSet.sbarPad, but_h: 11, but_w: 11, - col: ppt.sbarCol, + col: libSet.sbarCol, narrowWidth: 2, sp: 12, type: 0, w: 11 }; - if (!ppt.butCustIconFont.length) ppt.butCustIconFont = 'Segoe UI Symbol'; - if (ppt.narrowSbarWidth != 0) ppt.narrowSbarWidth = $Lib.clamp(ppt.narrowSbarWidth, 2, 10); + if (!libSet.butCustIconFont.length) libSet.butCustIconFont = 'Segoe UI Symbol'; + if (libSet.narrowSbarWidth != 0) libSet.narrowSbarWidth = $Lib.clamp(libSet.narrowSbarWidth, 2, 10); this.style = { bg: false, @@ -100,12 +100,12 @@ class UserInterface { pen_c: 0x55888888, squareNode: true, symb: window.CreateThemeManager('TREEVIEW'), - topBarShow: ppt.filterShow || ppt.searchShow || ppt.settingsShow + topBarShow: libSet.filterShow || libSet.searchShow || libSet.settingsShow }; this.sz = { - margin: ppt.margin, - marginRight: ppt.margin, + margin: libSet.margin, + marginRight: libSet.margin, marginSearch: 0, node: Math.round(11 * $Lib.scale), node_base: Math.round(11 * $Lib.scale), @@ -121,7 +121,7 @@ class UserInterface { this.themeColour = {} this.focus_changed = $Lib.debounce(() => { - if (!ppt.recItemImage || ppt.libSource != 2) this.on_playback_new_track(); + if (!libSet.recItemImage || libSet.libSource != 2) this.on_playback_new_track(); }, 250); this.setNodes(); @@ -130,7 +130,7 @@ class UserInterface { this.createImages(); this.setSbar(); - ppt.zoomImg = Math.round($Lib.clamp(ppt.zoomImg, 10, 500)); + libSet.zoomImg = Math.round($Lib.clamp(libSet.zoomImg, 10, 500)); } // * METHODS * // @@ -172,7 +172,7 @@ class UserInterface { return cc; }; prop.forEach((v, i) => { - this.col[v] = set(ppt[`${v}Use`] ? ppt[v] : '', i < 6 ? 0 : 1); + this.col[v] = set(libSet[`${v}Use`] ? libSet[v] : '', i < 6 ? 0 : 1); }); } @@ -188,25 +188,25 @@ class UserInterface { } calcText(refreshImg) { - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`] || 14; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`] || 14; $Lib.gr(1, 1, false, g => { - if (!this.id.local) this.row.h = Math.max(Math.round(g.CalcTextHeight('String', this.font.main)) + ppt.verticalPad, 2); + if (!this.id.local) this.row.h = Math.max(Math.round(g.CalcTextHeight('String', this.font.main)) + libSet.verticalPad, 2); if (this.style.squareNode) { this.sz.node = Math.round(SCALE(11) * $Lib.scale); // Prevent node size growing in traditional tree // Math.round($Lib.clamp(this.sz.node, 7, this.row.h - 2)); - ppt.zoomNode = Math.round(this.sz.node / this.sz.node_base * 100); + libSet.zoomNode = Math.round(this.sz.node / this.sz.node_base * 100); } else { - this.sz.node = ppt.nodeStyle === 5 ? Math.round((RES_4K ? 12 : 7) * $Lib.scale) : Math.round($Lib.clamp(this.sz.node, 7, this.row.h * 1.15)); - const mod = ppt.nodeStyle < 3 && this.sz.node > 15 ? (this.sz.node % 2) - 1 : 0; - this.icon.font = gdi.Font(this.icon.fontName, this.sz.node + mod, ppt.nodeStyle != 6 ? 0 : this.icon.fontStyle); - ppt.zoomNode = Math.round(this.sz.node / libraryFontSize * 100); + this.sz.node = libSet.nodeStyle === 5 ? Math.round((RES._4K ? 12 : 7) * $Lib.scale) : Math.round($Lib.clamp(this.sz.node, 7, this.row.h * 1.15)); + const mod = libSet.nodeStyle < 3 && this.sz.node > 15 ? (this.sz.node % 2) - 1 : 0; + this.icon.font = gdi.Font(this.icon.fontName, this.sz.node + mod, libSet.nodeStyle != 6 ? 0 : this.icon.fontStyle); + libSet.zoomNode = Math.round(this.sz.node / libraryFontSize * 100); } - pop.createImages(); + lib.pop.createImages(); this.font.mainEllipsisSpace = g.CalcTextWidth(' ...', this.font.main); this.font.groupEllipsisSpace = g.CalcTextWidth(' ...', this.font.group); this.font.lotEllipsisSpace = g.CalcTextWidth(' ...', this.font.lot); this.sz.sp = Math.max(Math.round(g.CalcTextWidth(' ', this.font.main)), 4); this.sz.sp1 = Math.max(Math.round(this.sz.sp * 1.5), 6); - if (ppt.nodeStyle && ppt.nodeStyle < 7) { + if (libSet.nodeStyle && libSet.nodeStyle < 7) { const sp_e = g.MeasureString(this.icon.expand, this.icon.font, 0, 0, 500, 500).Width; const sp_c = g.MeasureString(this.icon.collapse, this.icon.font, 0, 0, 500, 500).Width; this.icon.offset = Math.max((sp_c - sp_e) / 2, 0); @@ -219,26 +219,26 @@ class UserInterface { this.l.s2 = Math.floor(this.sz.node / 2) + this.l.wc; this.l.s3 = Math.max(7, this.sz.node / 2) - this.l.wf; - this.icon.w = ppt.facetView ? 0 : this.style.squareNode ? this.sz.node + this.sz.sp1 : this.sz.sp + this.sz.sp2; + this.icon.w = libSet.facetView ? 0 : this.style.squareNode ? this.sz.node + this.sz.sp1 : this.sz.sp + this.sz.sp2; this.sz.sel = (this.style.squareNode ? this.sz.sp1 : this.sz.sp + Math.round(this.sz.sp / 3)) / 2; - this.sz.margin = this.style.topBarShow && pop.inlineRoot ? ppt.margin /*+ Math.floor(Math.max(this.font.main.Size * 10 / 27, 5))*/ : ppt.margin; - this.sz.marginRight = ppt.countsRight || ppt.itemShowStatistics ? ppt.margin + Math.floor(Math.max(this.font.main.Size * 10 / 27, 5)) : ppt.margin; - if (ppt.facetView) this.sz.margin = this.sz.marginRight = (ppt.sbarShow ? Math.max(ppt.margin, this.sbar.sp + 7 * $Lib.scale) : ppt.margin); + this.sz.margin = this.style.topBarShow && lib.pop.inlineRoot ? libSet.margin /*+ Math.floor(Math.max(this.font.main.Size * 10 / 27, 5))*/ : libSet.margin; + this.sz.marginRight = libSet.countsRight || libSet.itemShowStatistics ? libSet.margin + Math.floor(Math.max(this.font.main.Size * 10 / 27, 5)) : libSet.margin; + if (libSet.facetView) this.sz.margin = this.sz.marginRight = (libSet.sbarShow ? Math.max(libSet.margin, this.sbar.sp + 7 * $Lib.scale) : libSet.margin); this.sz.marginSearch = this.sz.margin; - if (this.style.topBarShow && (ppt.countsRight || ppt.itemShowStatistics || ppt.rowStripes || ppt.fullLineSelection || pop.inlineRoot || ppt.nodeStyle == 3 || ppt.nodeStyle == 4)) this.sz.marginSearch -= 1; - if (this.style.topBarShow && !pop.inlineRoot && (ppt.nodeStyle == 3 || ppt.nodeStyle == 4)) this.sz.marginSearch -= 1; + if (this.style.topBarShow && (libSet.countsRight || libSet.itemShowStatistics || libSet.rowStripes || libSet.fullLineSelection || lib.pop.inlineRoot || libSet.nodeStyle == 3 || libSet.nodeStyle == 4)) this.sz.marginSearch -= 1; + if (this.style.topBarShow && !lib.pop.inlineRoot && (libSet.nodeStyle == 3 || libSet.nodeStyle == 4)) this.sz.marginSearch -= 1; this.id.tree = this.font.main.Name + this.font.main.Size + this.font.main.Style + this.icon.w + this.sz.margin + this.sz.marginSearch; - if (refreshImg) img.sizeDebounce(); + if (refreshImg) libImg.sizeDebounce(); } createImages() { const cc = StringFormat(1, 1); - const font1 = gdi.Font(fontDefault, 270, 1); - const font2 = gdi.Font(fontDefault, 120, 1); - const font3 = gdi.Font(fontDefault, 200, 1); - const font4 = gdi.Font(fontDefault, 90, 1); + const font1 = gdi.Font(grFont.fontDefault, 270, 1); + const font2 = gdi.Font(grFont.fontDefault, 120, 1); + const font3 = gdi.Font(grFont.fontDefault, 200, 1); + const font4 = gdi.Font(grFont.fontDefault, 90, 1); const tcol = this.col.text; for (let i = 0; i < 2; i++) { this.img.stub[i] = $Lib.gr(500, 500, true, g => { @@ -259,8 +259,8 @@ class UserInterface { draw(gr) { gr.SetSmoothingMode(SmoothingMode.None); // Disable smoothing for sharp edges on top and bottom bg - if (this.style.bg) gr.FillSolidRect(this.x, this.y, this.w + (pref.libraryLayout === 'split' ? 0 : RES_4K ? 35 : 17), this.h, this.col.bg); - if (pref.styleBlend && albumArt && blendedImg) gr.DrawImage(blendedImg, 0, 0, ww, wh, 0, 0, blendedImg.Width, blendedImg.Height); + if (this.style.bg) gr.FillSolidRect(this.x, this.y, this.w + (grSet.libraryLayout === 'split' ? 0 : RES._4K ? 35 : 17), this.h, this.col.bg); + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) gr.DrawImage(grCol.imgBlended, 0, 0, grm.ui.ww, grm.ui.wh, 0, 0, grCol.imgBlended.Width, grCol.imgBlended.Height); if (this.img.isBlur || this.img.bg) { this.getImgFallback(); if (this.img.cur) gr.DrawImage(this.img.cur, this.x, this.y, this.w, this.h, 0, 0, this.img.cur.Width, this.img.cur.Height); @@ -269,20 +269,20 @@ class UserInterface { drawLine(gr) { if (!this.style.topBarShow) return; - if (this.style.pen == 1) gr.DrawLine(this.x + (ppt.nodeStyle === 0 ? ui.sz.margin : panel.ln.x + ui.sz.margin), ui.y - SCALE(10) + panel.search.sp, this.x + (ppt.nodeStyle === 0 ? panel.ln.w : panel.ln.w - ui.sz.margin), ui.y - SCALE(10) + panel.search.sp, this.l.w, this.col.s_line); - if (!ppt.searchShow || !ppt.filterShow) return; - // const l_x = panel.filter.x - this.l.wc; - // const l_h = ui.row.h / 2; - // gr.FillGradRect(l_x, ui.y, this.l.w, l_h, 91, RGBA(0, 0, 0, 0), this.col.s_line); - // gr.FillGradRect(l_x, ui.y + l_h, this.l.w, l_h, 91, this.col.s_line, RGBA(0, 0, 0, 0)); + if (this.style.pen == 1) gr.DrawLine(this.x + (libSet.nodeStyle === 0 ? lib.ui.sz.margin : lib.panel.ln.x + lib.ui.sz.margin), lib.ui.y - SCALE(10) + lib.panel.search.sp, this.x + (libSet.nodeStyle === 0 ? lib.panel.ln.w : lib.panel.ln.w - lib.ui.sz.margin), lib.ui.y - SCALE(10) + lib.panel.search.sp, this.l.w, this.col.s_line); + // if (!libSet.searchShow || !libSet.filterShow) return; + // const l_x = lib.panel.filter.x - this.l.wc; + // const l_h = lib.ui.row.h / 2; + // gr.FillGradRect(l_x, lib.ui.y, this.l.w, l_h, 91, RGBA(0, 0, 0, 0), this.col.s_line); + // gr.FillGradRect(l_x, lib.ui.y + l_h, this.l.w, l_h, 91, this.col.s_line, RGBA(0, 0, 0, 0)); } drawTopBarUnderlay(gr) { if ((this.img.isBlur || this.img.bg) && this.img.cur) { - gr.FillSolidRect(this.x, this.y, this.w, img.panel.y, /* this.col.topBarUnderlay */ g_pl_colors.bg); - gr.DrawImage(this.img.cur, this.x, this.y, this.w, img.panel.y, 0, 0, this.img.cur.Width, panel.search.h); - } else gr.FillSolidRect(this.x, this.y, this.w, img.panel.y, /* this.col.topBarUnderlay */ g_pl_colors.bg); - if (pref.styleBlend && albumArt && blendedImg) gr.DrawImage(blendedImg, this.x, this.y - this.h - geo.lowerBarHeight + img.panel.y - geo.topMenuHeight, ww, wh, this.x, this.y - this.h - geo.lowerBarHeight + img.panel.y - geo.topMenuHeight, blendedImg.Width, blendedImg.Height); + gr.FillSolidRect(this.x, this.y, this.w, libImg.panel.y, /* this.col.topBarUnderlay */ pl.col.bg); + gr.DrawImage(this.img.cur, this.x, this.y, this.w, libImg.panel.y, 0, 0, this.img.cur.Width, lib.panel.search.h); + } else gr.FillSolidRect(this.x, this.y, this.w, libImg.panel.y, /* this.col.topBarUnderlay */ pl.col.bg); + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) gr.DrawImage(grCol.imgBlended, this.x, this.y - this.h - grm.ui.lowerBarHeight + libImg.panel.y - grm.ui.topMenuHeight, grm.ui.ww, grm.ui.wh, this.x, this.y - this.h - grm.ui.lowerBarHeight + libImg.panel.y - grm.ui.topMenuHeight, grCol.imgBlended.Width, grCol.imgBlended.Height); } formatImg(image) { @@ -291,10 +291,10 @@ class UserInterface { let imgh; let imgx; let imgy; - if (!this.img.isBlur && ppt.autoFill || this.img.isBlur && ppt.blurAutofill) { + if (!this.img.isBlur && libSet.autoFill || this.img.isBlur && libSet.blurAutofill) { const s1 = image.Width / this.w; const s2 = image.Height / this.h; - if (!this.img.isBlur && ppt.autoFill && Math.abs(s1 / s2 - 1) < 0.05) { + if (!this.img.isBlur && libSet.autoFill && Math.abs(s1 / s2 - 1) < 0.05) { imgx = 0; imgy = 0; imgw = image.Width; @@ -317,21 +317,21 @@ class UserInterface { switch (true) { case this.img.isBlur: g.SetInterpolationMode(0); - if (ppt.blurAutofill) image = image.Clone(imgx, imgy, imgw, imgh); + if (libSet.blurAutofill) image = image.Clone(imgx, imgy, imgw, imgh); if (this.img.blurBlend) { - if (ppt.blurTemp) { + if (libSet.blurTemp) { const iSmall = image.Resize(this.w * this.img.blurLevel / 100, this.h * this.img.blurLevel / 100, 2); const iFull = iSmall.Resize(this.w, this.h, 2); const offset = 90 - this.img.blurLevel; g.DrawImage(iFull, 0 - offset, 0 - offset, this.w + offset * 2, this.h + offset * 2, 0, 0, iFull.Width, iFull.Height, 0, this.img.blendAlpha); } else g.DrawImage(image, 0, 0, this.w, this.h, 0, 0, image.Width, image.Height, 0, this.img.blendAlpha); // no blur } else { - if (ppt.theme == 1 || ppt.theme == 3) { + if (libSet.theme == 1 || libSet.theme == 3) { g.DrawImage(image, 0, 0, this.w, this.h, 0, 0, image.Width, image.Height); if (this.img.blurLevel > 1) gi.StackBlur(this.img.blurLevel); g.FillSolidRect(0, 0, this.w, this.h, this.isImageLight(gi) ? this.col.bg_light : this.col.bg_dark); } - if (ppt.theme == 4) { + if (libSet.theme == 4) { g.FillSolidRect(0, 0, this.w, this.h, this.getRandomCol()); g.DrawImage(image, 0, 0, this.w, this.h, 0, 0, image.Width, image.Height, 0, this.getImgAlpha(image)); if (this.img.blurLevel > 1) gi.StackBlur(this.img.blurLevel); @@ -339,7 +339,7 @@ class UserInterface { } break; case !this.img.isBlur: - if (ppt.autoFill) g.DrawImage(image, 0, 0, this.w, this.h, imgx, imgy, imgw, imgh, 0, this.img.covAlpha); + if (libSet.autoFill) g.DrawImage(image, 0, 0, this.w, this.h, imgx, imgy, imgw, imgh, 0, this.img.covAlpha); else { const sc = Math.min(this.h / image.Height, this.w / image.Width); const tw = Math.round(image.Width * sc); @@ -383,17 +383,17 @@ class UserInterface { getBlurColours() { switch (true) { - case !ppt.themed: - if (ppt.theme > 5) ppt.theme = 0; // reset if coming from themed & out of bounds - this.img.isBlur = ppt.theme && ppt.theme < 5; - this.img.bg = ppt.theme == 5; - this.img.blendAlpha = $Lib.clamp($Lib.clamp(ppt.blurAlpha, 0, 100) * 105 / 30, 0, 255); - this.img.blurAlpha = $Lib.clamp(ppt.blurAlpha, 0, 100) / 30; - this.img.blurBlend = ppt.theme == 2; - this.img.blurDark = ppt.theme == 1 || ppt.theme == 4; - this.img.blurLevel = ppt.theme == 2 ? 91.05 - $Lib.clamp(ppt.blurTemp, 1.05, 90) : $Lib.clamp(ppt.blurTemp * 2, 0, 254); - this.img.blurLight = ppt.theme == 3; - this.img.covAlpha = $Lib.clamp(ppt.covAlpha * 2.55, 0, 255); + case !libSet.themed: + if (libSet.theme > 5) libSet.theme = 0; // reset if coming from themed & out of bounds + this.img.isBlur = libSet.theme && libSet.theme < 5; + this.img.bg = libSet.theme == 5; + this.img.blendAlpha = $Lib.clamp($Lib.clamp(libSet.blurAlpha, 0, 100) * 105 / 30, 0, 255); + this.img.blurAlpha = $Lib.clamp(libSet.blurAlpha, 0, 100) / 30; + this.img.blurBlend = libSet.theme == 2; + this.img.blurDark = libSet.theme == 1 || libSet.theme == 4; + this.img.blurLevel = libSet.theme == 2 ? 91.05 - $Lib.clamp(libSet.blurTemp, 1.05, 90) : $Lib.clamp(libSet.blurTemp * 2, 0, 254); + this.img.blurLight = libSet.theme == 3; + this.img.covAlpha = $Lib.clamp(libSet.covAlpha * 2.55, 0, 255); if (this.img.blurDark) { this.col.bg_light = RGBA(0, 0, 0, Math.min(160 / this.img.blurAlpha, 255)); this.col.bg_dark = RGBA(0, 0, 0, Math.min(80 / this.img.blurAlpha, 255)); @@ -403,11 +403,11 @@ class UserInterface { this.col.bg_dark = RGBA(255, 255, 255, Math.min(205 / this.img.blurAlpha, 255)); } break; - case ppt.themed: - this.img.isBlur = ppt.theme || ppt.themeBgImage; - this.img.blurBlend = ppt.theme == 6 || ppt.theme == 7; - this.img.blurDark = ppt.theme == 1 && !ppt.themeLight || ppt.theme == 2 && !ppt.themeLight || ppt.theme == 3 || ppt.theme == 4 || ppt.theme == 5 || ppt.theme == 9; - this.img.blurLight = ppt.theme == 1 && ppt.themeLight || ppt.theme == 2 && ppt.themeLight || ppt.theme == 8; + case libSet.themed: + this.img.isBlur = libSet.theme || libSet.themeBgImage; + this.img.blurBlend = libSet.theme == 6 || libSet.theme == 7; + this.img.blurDark = libSet.theme == 1 && !libSet.themeLight || libSet.theme == 2 && !libSet.themeLight || libSet.theme == 3 || libSet.theme == 4 || libSet.theme == 5 || libSet.theme == 9; + this.img.blurLight = libSet.theme == 1 && libSet.themeLight || libSet.theme == 2 && libSet.themeLight || libSet.theme == 8; break; } } @@ -422,8 +422,8 @@ class UserInterface { this.getUIColours(); this.getItemColours(); - if (ppt.themed) { - if ((ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && this.themeColour && ppt.themeColour) { + if (libSet.themed) { + if ((libSet.theme == 0 || libSet.theme == 6 || libSet.theme == 7) && this.themeColour && libSet.themeColour) { // nothing to do } else { this.themeColour = { @@ -450,7 +450,7 @@ class UserInterface { } getFbImg(handle) { - if (!handle) handle = (!ppt.recItemImage || ppt.libSource != 2) ? (fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem()) : this.expandHandle; + if (!handle) handle = (!libSet.recItemImage || libSet.libSource != 2) ? (fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem()) : this.expandHandle; if (handle) { this.cur_handle = handle; utils.GetAlbumArtAsync(0, handle, 0); @@ -466,11 +466,11 @@ class UserInterface { } getFont(init) { - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`] || 14; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`] || 14; - if (pref.customThemeFonts) this.font.main = ft.library; - else if (ppt.custFontUse && ppt.custFont.length) { - const custFont = $Lib.split(ppt.custFont, 1); + if (grSet.customThemeFonts) this.font.main = grFont.library; + else if (libSet.custFontUse && libSet.custFont.length) { + const custFont = $Lib.split(libSet.custFont, 1); this.font.main = gdi.Font(custFont[0], Math.max(Math.round($Lib.value(custFont[1], 16, 0)), 1), Math.round($Lib.value(custFont[2], 0, 0))); } else if (this.dui) this.font.main = window.GetFontDUI(2); @@ -478,20 +478,20 @@ class UserInterface { if (this.id.local) this.font.main = c_font; - if (!this.font.main || !pref.customThemeFonts && DetectWine() && /tahoma/i.test(this.font.main.Name)) { // Windows: check still needed (test MS Serif or Modern, neither can be used); Wine: tahoma is default system font, but bold and some unicode characters don't work: if Wine + tahoma detected changed to Segoe UI (if that's not installed, tahoma is still used) - this.font.main = gdi.Font(fontDefault, libraryFontSize, 0); + if (!this.font.main || !grSet.customThemeFonts && DetectWine() && /tahoma/i.test(this.font.main.Name)) { // Windows: check still needed (test MS Serif or Modern, neither can be used); Wine: tahoma is default system font, but bold and some unicode characters don't work: if Wine + tahoma detected changed to Segoe UI (if that's not installed, tahoma is still used) + this.font.main = gdi.Font(grFont.fontDefault, libraryFontSize, 0); $Lib.trace('Spider Monkey Panel is unable to use your default font. Using Segoe UI at default size & style instead', false); } - if (this.font.main.Size != libraryFontSize) ppt.zoomFont = 100; - // (pref.layout === 'artwork' ? ppt.baseFontSize_artwork : ppt.baseFontSize_default) = this.font.main.Size; + if (this.font.main.Size != libraryFontSize) libSet.zoomFont = 100; + // (pref.layout === 'artwork' ? libSet.baseFontSize_artwork : libSet.baseFontSize_default) = this.font.main.Size; - this.font.zoomSize = Math.max(Math.round(libraryFontSize * ppt.zoomFont / 100), 1); + this.font.zoomSize = Math.max(Math.round(libraryFontSize * libSet.zoomFont / 100), 1); - // this.sz.node = this.style.squareNode ? Math.round(this.sz.node_base * ppt.zoomNode / 50) : Math.round(libraryFontSize * ppt.zoomNode / 100); - this.sz.node = Math.round(libraryFontSize * ppt.zoomNode / 100); + // this.sz.node = this.style.squareNode ? Math.round(this.sz.node_base * libSet.zoomNode / 50) : Math.round(libraryFontSize * libSet.zoomNode / 100); + this.sz.node = Math.round(libraryFontSize * libSet.zoomNode / 100); this.font.main = gdi.Font(this.font.main.Name, this.font.zoomSize, this.font.main.Style); - ppt.zoomFont = Math.round(this.font.zoomSize / libraryFontSize * 100); + libSet.zoomFont = Math.round(this.font.zoomSize / libraryFontSize * 100); this.font.search = gdi.Font(this.font.main.Name, this.font.main.Size, 0); this.font.find = gdi.Font(this.font.main.Name, this.font.main.Size * 1.5, 1); @@ -500,9 +500,9 @@ class UserInterface { this.font.search = c_s_font; this.font.find = gdi.Font(this.font.main.Name, this.font.main.Size * 1.5, 1); this.sz.margin = c_margin; - ppt.treeIndent = c_pad; + libSet.treeIndent = c_pad; this.row.h = c_row_h; - if (ppt.sbarShow) { + if (libSet.sbarShow) { this.sbar.type = 0; this.sbar.w = c_scr_w; this.sbar.but_w = this.sbar.w + 1; @@ -511,29 +511,28 @@ class UserInterface { } } - this.font.label = gdi.Font(this.font.main.Name, !ppt.treeAutoExpand || ppt.libSource != 2 ? Math.round(this.font.main.Size * 11 / 14) : this.font.main.Size, this.font.main.Style); - this.font.small = gdi.Font(this.font.main.Name, !ppt.treeAutoExpand || ppt.libSource != 2 ? Math.round(this.font.main.Size * 12 / 14) : this.font.main.Size, this.font.main.Style); + this.font.label = gdi.Font(this.font.main.Name, !libSet.treeAutoExpand || libSet.libSource != 2 ? Math.round(this.font.main.Size * 11 / 14) : this.font.main.Size, this.font.main.Style); + this.font.small = gdi.Font(this.font.main.Name, !libSet.treeAutoExpand || libSet.libSource != 2 ? Math.round(this.font.main.Size * 12 / 14) : this.font.main.Size, this.font.main.Style); this.font.tracks = gdi.Font('Arial', Math.round(this.font.main.Size * 12 / 14), 2); - this.sz.sideMarker = SCALE(8); // ppt.sideMarkerWidth ? Math.max(ppt.sideMarkerWidth, 1) : 4 * this.l.w; - this.sbar.narrowWidth = ppt.narrowSbarWidth == 0 ? $Lib.clamp(Math.floor(this.font.zoomSize / 7), 2, 10) : ppt.narrowSbarWidth; + this.sz.sideMarker = SCALE(8); // libSet.sideMarkerWidth ? Math.max(libSet.sideMarkerWidth, 1) : 4 * this.l.w; + this.sbar.narrowWidth = libSet.narrowSbarWidth == 0 ? $Lib.clamp(Math.floor(this.font.zoomSize / 7), 2, 10) : libSet.narrowSbarWidth; // * Only used for pref.libraryLayoutSplitPreset4, synchronizes artist & album font sizes with Playlist - const headerFontSize = pref[`playlistHeaderFontSize_${pref.layout}`]; - const rowFontSize = pref[`playlistFontSize_${pref.layout}`]; - const libraryLayoutSplitPreset4 = ppt.albumArtLabelType === 2 && (pref.layout === 'default' && pref.libraryLayout === 'split' && displayLibrary && displayPlaylist); // displayLibrarySplit + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const libraryLayoutSplitPreset4 = libSet.albumArtLabelType === 2 && (grSet.layout === 'default' && grSet.libraryLayout === 'split' && grm.ui.displayLibrary && grm.ui.displayPlaylist); // displayLibrarySplit - if (ppt.custAlbumArtGrpFontUse && ppt.custAlbumArtGrpFont.length) { - const custFont = $Lib.split(ppt.custAlbumArtGrpFont, 1); + if (libSet.custAlbumArtGrpFontUse && libSet.custAlbumArtGrpFont.length) { + const custFont = $Lib.split(libSet.custAlbumArtGrpFont, 1); this.font.group = gdi.Font(custFont[0], this.font.main.Size, Math.round($Lib.value(custFont[1], 1, 0))); } else this.font.group = gdi.Font('Segoe UI Semibold', /* this.font.main.Name, */ libraryLayoutSplitPreset4 ? headerFontSize + 3 : this.font.main.Size, 1); - if (ppt.custAlbumArtLotFontUse && ppt.custAlbumArtLotFont.length) { - const custFont = $Lib.split(ppt.custAlbumArtLotFont, 1); + if (libSet.custAlbumArtLotFontUse && libSet.custAlbumArtLotFont.length) { + const custFont = $Lib.split(libSet.custAlbumArtLotFont, 1); this.font.lot = gdi.Font(custFont[0], this.font.main.Size, Math.round($Lib.value(custFont[1], 2, 0))); } else this.font.lot = gdi.Font('Segoe UI Semibold', libraryLayoutSplitPreset4 ? headerFontSize : this.font.main.Size, 0); - if (ppt.custAlbumArtDurFontUse && ppt.custAlbumArtDurFont.length) { - const custFont = $Lib.split(ppt.custAlbumArtDurFont, 1); + if (libSet.custAlbumArtDurFontUse && libSet.custAlbumArtDurFont.length) { + const custFont = $Lib.split(libSet.custAlbumArtDurFont, 1); this.font.statistics = gdi.Font(custFont[0], this.font.main.Size, Math.round($Lib.value(custFont[1], 2, 0))); } else this.font.statistics = gdi.Font('Segoe UI Semibold', this.font.main.Size, 0); @@ -564,7 +563,7 @@ class UserInterface { } getImgFallback() { - if (sbar.draw_timer || !this.get || ppt.themed) return; + if (lib.sbar.draw_timer || !this.get || libSet.themed) return; this.getFbImg(); this.get = false; } @@ -578,28 +577,28 @@ class UserInterface { } if (this.img.blurLight) { this.col.txt = RGB(50, 50, 50); - this.col.txt_h = ppt.themed && (ppt.theme == 1 || ppt.theme == 2) ? RGB(25, 25, 25) : RGB(71, 129, 183); + this.col.txt_h = libSet.themed && (libSet.theme == 1 || libSet.theme == 2) ? RGB(25, 25, 25) : RGB(71, 129, 183); } if (this.col.text === '') this.col.text = this.img.blurBlend ? this.setBrightness(this.col.txt, lightBg ? -10 : 10) : this.col.txt; - if (this.col.text_h === '') this.col.text_h = ppt.themed && ppt.theme == 9 ? RGB(104, 225, 255) : this.img.blurBlend ? this.setBrightness(this.col.txt_h, lightBg ? -10 : 10) : this.col.txt_h; + if (this.col.text_h === '') this.col.text_h = libSet.themed && libSet.theme == 9 ? RGB(104, 225, 255) : this.img.blurBlend ? this.setBrightness(this.col.txt_h, lightBg ? -10 : 10) : this.col.txt_h; this.col.bg3 = lightBg ? 0x10000000 : 0x10ffffff; this.col.bg4 = lightBg ? 0x1f000000 : 0x1fffffff; this.col.bg5 = lightBg ? 0x00000000 : 0x00ffffff; this.col.bg6 = this.img.blurDark ? RGB(64, 64, 64) : this.img.blurLight ? RGB(245, 245, 245) : this.col.bg == 0 ? 0xff000000 : this.col.bg - if (ppt.swapCol && (!ppt.albumArtShow || ppt.albumArtLabelType != 4)) { + if (libSet.swapCol && (!libSet.albumArtShow || libSet.albumArtLabelType != 4)) { const colH = this.col.text_h; this.col.text_h = this.col.text; this.col.text = colH; } - if (this.col.nowp === '') this.col.nowp = !this.img.blurDark ? this.col.text_h : ppt.themed && ppt.theme == 9 ? RGB(104, 225, 255) : RGB(128, 228, 27); + if (this.col.nowp === '') this.col.nowp = !this.img.blurDark ? this.col.text_h : libSet.themed && libSet.theme == 9 ? RGB(104, 225, 255) : RGB(128, 228, 27); if (this.col.bg_h === '') { - this.col.bg_h = ppt.highLightRow > 2 ? (this.img.blurDark ? 0x24000000 : 0x1E30AFED) : this.img.blurDark ? 0x19ffffff : this.img.blurLight || lightBg ? 0x19000000 : 0x19ffffff; + this.col.bg_h = libSet.highLightRow > 2 ? (this.img.blurDark ? 0x24000000 : 0x1E30AFED) : this.img.blurDark ? 0x19ffffff : this.img.blurLight || lightBg ? 0x19000000 : 0x19ffffff; this.col.bgSel_h = this.col.bg_h; - if (this.getColSat(this.col.bg) < 150 && !this.img.blurDark && !this.img.blurLight && ppt.highLightRow != 3) { + if (this.getColSat(this.col.bg) < 150 && !this.img.blurDark && !this.img.blurLight && libSet.highLightRow != 3) { this.col.bg_h = this.getBlend(this.col.bg == 0 ? 0xff000000 : this.col.bg, this.col.bgSel, 0.55); this.col.bgSel_h = this.getBlend(this.col.bg == 0 ? 0xff000000 : this.col.bg, this.col.bgSel, 0.25); } @@ -623,14 +622,14 @@ class UserInterface { this.col.imgBgSel = this.col.bgSel & 0xb0ffffff; this.col.imgOverlaySel = $Lib.RGBtoRGBA(this.col.bg, 175); - if (this.col.sideMarker === '') this.col.sideMarker = ppt.highLightNode ? this.col.text_h : this.col.text; + if (this.col.sideMarker === '') this.col.sideMarker = libSet.highLightNode ? this.col.text_h : this.col.text; this.col.count = this.setBrightness(this.col.text, this.isLightCol(this.col.text) ? -30 : 30); - if (this.col.line === '') this.col.line = ppt.nodeLines && !ppt.facetView ? RGBA(136, 136, 136, 85) : 0; + if (this.col.line === '') this.col.line = libSet.nodeLines && !libSet.facetView ? RGBA(136, 136, 136, 85) : 0; if (this.col.search === '') this.col.search = this.col.text; if (!this.dui && this.col.textSel === '') { - const colours = Object.keys(colourSelector); - this.themeColour = colours.length ? colourSelector[colours[ppt.themeColour]] : null; + const colours = Object.keys(libColourSelector); + this.themeColour = colours.length ? libColourSelector[colours[libSet.themeColour]] : null; this.col.textSel = !this.img.blurDark && !this.img.blurLight ? (this.themeColour ? this.getSelTextCol(this.col.bgSel) : window.GetColourCUI(1)) : this.col.text; } if (this.col.textSel === '') this.col.textSel = !this.img.blurDark && !this.img.blurLight ? this.getSelTextCol(this.col.bgSel) : this.col.text; @@ -648,10 +647,10 @@ class UserInterface { this.col.txt_box_h = this.setBrightness(this.col.txt_box, 33); } if (this.col.s_line === '') { - if (!ppt.colLineDark) this.col.s_line = !this.img.blurDark ? RGBA(136, 136, 136, 85) : $Lib.RGBtoRGBA(this.col.text, 36); + if (!libSet.colLineDark) this.col.s_line = !this.img.blurDark ? RGBA(136, 136, 136, 85) : $Lib.RGBtoRGBA(this.col.text, 36); else { const lightBg = this.isLightBackground(); - const nearBlack = ((ppt.theme == 1 || ppt.theme == 2) && !this.col.themeLight || (ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && !lightBg) && this.getColSat(this.col.bg) < 45; + const nearBlack = ((libSet.theme == 1 || libSet.theme == 2) && !this.col.themeLight || (libSet.theme == 0 || libSet.theme == 6 || libSet.theme == 7) && !lightBg) && this.getColSat(this.col.bg) < 45; const alpha = !lightBg ? nearBlack ? 0x20ffffff : 0x50000000 : 0x30000000; this.col.s_line = this.col.text & alpha; } @@ -673,14 +672,14 @@ class UserInterface { this.col.text_h = this.img.blurBlend ? this.setBrightness(c_textcol_h, this.isLightCol(this.col.bg == 0 ? 0xff000000 : this.col.bg) ? -10 : 10) : this.img.blurDark ? RGB(255, 255, 255) : this.img.blurLight ? RGB(50, 50, 50) : c_textcol_h; this.col.textSel = this.col.selBlend = c_textselcol; this.col.bgSel = c_backcolsel; - ppt.rowStripes = c_alternate; + libSet.rowStripes = c_alternate; this.style.fill = c_fill; this.style.pen = c_pen; this.style.pen_c = c_pen_c; this.col.search = this.col.txt_box = c_txt_box; - this.col.bg_h = ppt.highLightRow > 2 ? (this.img.blurDark ? 0x24000000 : 0x1E30AFED) : this.img.blurDark ? 0x19ffffff : this.img.blurLight || lightBg ? 0x19000000 : 0x19ffffff; + this.col.bg_h = libSet.highLightRow > 2 ? (this.img.blurDark ? 0x24000000 : 0x1E30AFED) : this.img.blurDark ? 0x19ffffff : this.img.blurLight || lightBg ? 0x19000000 : 0x19ffffff; this.col.bgSel_h = this.col.bg_h; - if (this.getColSat(this.col.bg) < 150 && !this.img.blurDark && !this.img.blurLight && !ppt.highLightRow != 3) { + if (this.getColSat(this.col.bg) < 150 && !this.img.blurDark && !this.img.blurLight && !libSet.highLightRow != 3) { this.col.bg_h = this.getBlend(this.col.bg == 0 ? 0xff000000 : this.col.bg, this.col.bgSel, 0.55); this.col.bgSel_h = this.getBlend(this.col.bg == 0 ? 0xff000000 : this.col.bg, this.col.bgSel, 0.25); } @@ -695,7 +694,7 @@ class UserInterface { this.icon.col_h = this.col.icon_h; this.setIconCol(); this.col.searchSel = window.IsTransparent || !this.col.bgSel ? 0xff0099ff : this.getContrast(this.col.search, this.col.bgSel) > 3 ? this.col.bgSel : this.getBlend(this.col.search, this.col.bg == 0 || this.img.blurDark ? 0xff000000 : this.col.bg, 0.25); - this.sbar.col = this.img.blurDark || this.img.blurLight ? 1 : ppt.sbarCol; + this.sbar.col = this.img.blurDark || this.img.blurLight ? 1 : libSet.sbarCol; this.col.txtArr = [this.col.text, this.col.text_h, this.col.textSel]; } @@ -709,7 +708,7 @@ class UserInterface { } getOpaque() { - return !(ppt.fullLineSelection && (ppt.highLightRow > 2 || ppt.sbarShow == 1) || !this.style.bg || this.img.isBlur || this.img.bg); + return !(libSet.fullLineSelection && (libSet.highLightRow > 2 || libSet.sbarShow == 1) || !this.style.bg || this.img.isBlur || this.img.bg); } getRandomCol() { @@ -730,9 +729,9 @@ class UserInterface { } getUIColours() { - const colours = Object.keys(colourSelector); - this.themeColour = ppt.themeColour && colours.length ? colourSelector[colours[ppt.themeColour]] : null; - if (ppt.themed && (ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && this.themeColour && ppt.themeColour) { + const colours = Object.keys(libColourSelector); + this.themeColour = libSet.themeColour && colours.length ? libColourSelector[colours[libSet.themeColour]] : null; + if (libSet.themed && (libSet.theme == 0 || libSet.theme == 6 || libSet.theme == 7) && this.themeColour && libSet.themeColour) { this.col.txt = this.themeColour.text; if (this.col.bg === '') this.col.bg = this.themeColour.background; if (this.col.bgSel === '') this.col.bgSel = this.img.blurDark ? RGBA(255, 255, 255, 36) : this.img.blurLight ? RGBA(50, 50, 50, 36) : this.themeColour.selection; @@ -740,13 +739,13 @@ class UserInterface { } else { switch (this.dui) { case 0: - if (this.col.bg === '') this.col.bg = g_pl_colors.bg; // Needed to prevent bg flashing when switching from tree to album art // this.img.blurLight ? RGB(245, 247, 255) : window.GetColourCUI(3); + if (this.col.bg === '') this.col.bg = pl.col.bg; // Needed to prevent bg flashing when switching from tree to album art // this.img.blurLight ? RGB(245, 247, 255) : window.GetColourCUI(3); if (this.col.bgSel === '') this.col.bgSel = this.img.blurDark ? RGBA(255, 255, 255, 36) : this.img.blurLight ? RGBA(50, 50, 50, 36) : window.GetColourCUI(4); this.col.txt = window.GetColourCUI(0); this.col.txt_h = window.GetColourCUI(2); break; case 1: - if (this.col.bg === '') this.col.bg = g_pl_colors.bg; // Needed to prevent bg flashing when switching from tree to album art // this.img.blurLight ? RGB(245, 247, 255) : window.GetColourDUI(1); + if (this.col.bg === '') this.col.bg = pl.col.bg; // Needed to prevent bg flashing when switching from tree to album art // this.img.blurLight ? RGB(245, 247, 255) : window.GetColourDUI(1); if (this.col.bgSel === '') this.col.bgSel = this.img.blurDark ? RGBA(255, 255, 255, 36) : this.img.blurLight ? RGBA(50, 50, 50, 36) : window.GetColourDUI(3); this.col.txt = window.GetColourDUI(0); this.col.txt_h = window.GetColourDUI(2); @@ -756,13 +755,13 @@ class UserInterface { } imageZoom(step) { - if (!panel.imgView) return; - ppt.zoomImg += step * 5; - ppt.zoomImg = Math.round($Lib.clamp(ppt.zoomImg, 10, 500)); - img.blockWidth = Math.round(this.row.h / 20 * 100) * ppt.zoomImg / 100; - clearInterval(img.timer.preLoad); - img.timer.preLoad = null; - img.sizeDebounce(); + if (!lib.panel.imgView) return; + libSet.zoomImg += step * 5; + libSet.zoomImg = Math.round($Lib.clamp(libSet.zoomImg, 10, 500)); + libImg.blockWidth = Math.round(this.row.h / 20 * 100) * libSet.zoomImg / 100; + clearInterval(libImg.timer.preLoad); + libImg.timer.preLoad = null; + libImg.sizeDebounce(); } isColOk(c) { @@ -792,7 +791,7 @@ class UserInterface { } isLightBackground() { - if (ppt.themed && (ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && this.themeColour && ppt.themeColour) { + if (libSet.themed && (libSet.theme == 0 || libSet.theme == 6 || libSet.theme == 7) && this.themeColour && libSet.themeColour) { // do nothing } else if (this.img.blurLight) return true; @@ -817,7 +816,7 @@ class UserInterface { } on_playback_new_track(handle) { - if (!this.img.isBlur && !this.img.bg || ppt.themed) return; + if (!this.img.isBlur && !this.img.bg || libSet.themed) return; if (this.block()) this.get = true; else { this.getFbImg(handle); @@ -833,13 +832,13 @@ class UserInterface { setIconCol() { const colBg = this.img.blurDark ? RGB(0, 0, 0) : this.img.blurLight ? RGB(255, 255, 255) : this.col.bg == 0 ? 0xff000000 : this.col.bg if (this.icon.col_c === '') { - this.col.icon_c = this.style.squareNode ? [RGB(252, 252, 252), RGB(223, 223, 223)] : (ppt.nodeStyle == 1 || ppt.nodeStyle == 3 ? (this.img.blurDark || this.img.blurBlend || this.img.blurLight ? $Lib.RGBtoRGBA(this.col.text, 72) : this.getBlend(colBg, this.col.text, 0.5)) : this.col.text); + this.col.icon_c = this.style.squareNode ? [RGB(252, 252, 252), RGB(223, 223, 223)] : (libSet.nodeStyle == 1 || libSet.nodeStyle == 3 ? (this.img.blurDark || this.img.blurBlend || this.img.blurLight ? $Lib.RGBtoRGBA(this.col.text, 72) : this.getBlend(colBg, this.col.text, 0.5)) : this.col.text); } else if (this.style.squareNode) { this.col.icon_c = this.getAlpha(this.icon.col_c) != 255 ? $Lib.RGBAtoRGB(this.icon.col_c, this.col.bg) : this.icon.col_c; this.col.icon_c = this.getGradient(this.col.icon_c, 15, -14); } if (this.icon.col_e === '') { - this.col.icon_e = this.style.squareNode ? [RGB(252, 252, 252), RGB(223, 223, 223)] : (ppt.nodeStyle == 1 || ppt.nodeStyle == 3 ? (this.img.blurDark || this.img.blurBlend ? this.col.text : this.getBlend(colBg, this.col.text, 0.1)) : this.col.text); + this.col.icon_e = this.style.squareNode ? [RGB(252, 252, 252), RGB(223, 223, 223)] : (libSet.nodeStyle == 1 || libSet.nodeStyle == 3 ? (this.img.blurDark || this.img.blurBlend ? this.col.text : this.getBlend(colBg, this.col.text, 0.1)) : this.col.text); } else if (this.style.squareNode) { this.col.icon_e = this.getAlpha(this.icon.col_e) != 255 ? $Lib.RGBAtoRGB(this.icon.col_e, this.col.bg) : this.icon.col_e; this.col.icon_e = this.getGradient(this.col.icon_e, 15, -14); @@ -847,9 +846,9 @@ class UserInterface { this.col.iconPlus = this.isLightCol(this.col.icon_e[0]) ? RGB(41, 66, 114) : RGB(225, 225, 245); this.col.iconMinus_c = this.isLightCol(this.col.icon_c[0]) ? RGB(75, 99, 167) : RGB(225, 225, 245); this.col.iconMinus_e = this.isLightCol(this.col.icon_e[0]) ? RGB(75, 99, 167) : RGB(225, 225, 245); - if (!ppt.highLightNode) return; + if (!libSet.highLightNode) return; if (this.icon.col_h === '') { - const nodeDiffHighlight = this.img.blurDark && !ppt.highLightRow && ppt.highLightNode; + const nodeDiffHighlight = this.img.blurDark && !libSet.highLightRow && libSet.highLightNode; this.col.icon_h = this.style.squareNode ? !this.img.blurDark && !this.img.blurLight ? !this.id.local ? (this.getColSat(this.col.text_h) < 650 ? this.col.text_h : this.col.text) : (this.getColSat(c_iconcol_h) < 650 ? c_iconcol_h : c_textcol) : RGB(50, 50, 50) : (nodeDiffHighlight ? this.col.nowp : this.col.text_h); this.icon.col_h = this.col.icon_h; } @@ -887,58 +886,58 @@ class UserInterface { } setNodes() { - if (!ppt.nodeStyle && ppt.winNode) ppt.nodeStyle = 7; - if (ppt.nodeStyle == 7 && !ppt.winNode) ppt.nodeStyle = 0; - ppt.nodeStyle = $Lib.clamp(ppt.nodeStyle, 0, 7); - if (ppt.nodeStyle == 6) { - this.icon.char = ppt.iconCustom; - if (!this.icon.char.charAt().length) ppt.nodeStyle = 0; + if (!libSet.nodeStyle && libSet.winNode) libSet.nodeStyle = 7; + if (libSet.nodeStyle == 7 && !libSet.winNode) libSet.nodeStyle = 0; + libSet.nodeStyle = $Lib.clamp(libSet.nodeStyle, 0, 7); + if (libSet.nodeStyle == 6) { + this.icon.char = libSet.iconCustom; + if (!this.icon.char.charAt().length) libSet.nodeStyle = 0; else { this.icon.char = this.icon.char.split('//'); if (this.icon.char[0].includes('|')) { this.icon.char = this.icon.char[0].split('|'); this.icon.expand = this.icon.char[0].trim(); this.icon.collapse = this.icon.char[1].trim(); - } else ppt.nodeStyle = 0; + } else libSet.nodeStyle = 0; } } - if (ppt.nodeStyle == 7) { + if (libSet.nodeStyle == 7) { $Lib.gr(this.sz.node, this.sz.node, false, g => { try { this.style.symb.SetPartAndStateID(2, 1); this.style.symb.SetPartAndStateID(2, 2); this.style.symb.DrawThemeBackground(g, 0, 0, this.sz.node, this.sz.node); } catch (e) { - ppt.nodeStyle = 0; + libSet.nodeStyle = 0; } }); } - if (ppt.nodeStyle) { - if (ppt.nodeStyle < 5) { + if (libSet.nodeStyle) { + if (libSet.nodeStyle < 5) { this.icon.expand = '\uF105'; this.icon.expand2 = '\uF0DA'; this.icon.collapse = '\uF107'; - } else if (ppt.nodeStyle === 5) { + } else if (libSet.nodeStyle === 5) { this.icon.expand = '\uE109'; // Segoe UI Symbol + this.icon.collapse = '\uE108'; // Segoe UI Symbol - } } - if (ppt.nodeStyle !== 7 && (!this.icon.expand.length || !this.icon.collapse.length)) ppt.nodeStyle = 0; - this.style.squareNode = !ppt.nodeStyle || ppt.nodeStyle === 7; - if (!ppt.custIconFont.length || ppt.nodeStyle !== 6) this.icon.fontName = ppt.nodeStyle !== 5 ? 'FontAwesome' : ppt.nodeStyle === 5 ? 'Segoe UI Symbol' : 'Consolas'; + if (libSet.nodeStyle !== 7 && (!this.icon.expand.length || !this.icon.collapse.length)) libSet.nodeStyle = 0; + this.style.squareNode = !libSet.nodeStyle || libSet.nodeStyle === 7; + if (!libSet.custIconFont.length || libSet.nodeStyle !== 6) this.icon.fontName = libSet.nodeStyle !== 5 ? 'FontAwesome' : libSet.nodeStyle === 5 ? 'Segoe UI Symbol' : 'Consolas'; else { - this.icon.fontName = ppt.custIconFont; + this.icon.fontName = libSet.custIconFont; this.icon.fontStyle = 0; } } setSbar() { - ppt.durationTouchFlick = $Lib.clamp($Lib.value(ppt.durationTouchFlick, 3000, 0), 0, 5000); - ppt.durationScroll = $Lib.clamp($Lib.value(ppt.durationScroll, 500, 0), 0, 5000); - ppt.flickDistance = $Lib.clamp(ppt.flickDistance, 0, 10); - ppt.touchStep = $Lib.clamp(ppt.touchStep, 1, 10); - ppt.sbarType = $Lib.value(ppt.sbarType, 0, 2); - this.sbar.type = ppt.sbarType; + libSet.durationTouchFlick = $Lib.clamp($Lib.value(libSet.durationTouchFlick, 3000, 0), 0, 5000); + libSet.durationScroll = $Lib.clamp($Lib.value(libSet.durationScroll, 500, 0), 0, 5000); + libSet.flickDistance = $Lib.clamp(libSet.flickDistance, 0, 10); + libSet.touchStep = $Lib.clamp(libSet.touchStep, 1, 10); + libSet.sbarType = $Lib.value(libSet.sbarType, 0, 2); + this.sbar.type = libSet.sbarType; if (this.sbar.type == 2) { this.theme = window.CreateThemeManager('scrollbar'); $Lib.gr(21, 21, false, g => { @@ -955,35 +954,35 @@ class UserInterface { } } catch (e) { this.sbar.type = 1; - ppt.sbarType = 1; + libSet.sbarType = 1; } }); } - this.sbar.arrowPad = ppt.sbarPad; - ppt.sbarWidth = $Lib.clamp(ppt.sbarWidth, 0, 400); - ppt.sbarBase_w = $Lib.clamp(ppt.sbarBase_w, 0, 400); - if (ppt.sbarWidth != ppt.sbarBase_w) { - ppt.sbarArrowWidth = Math.min(ppt.sbarArrowWidth, ppt.sbarWidth, 400); + this.sbar.arrowPad = libSet.sbarPad; + libSet.sbarWidth = $Lib.clamp(libSet.sbarWidth, 0, 400); + libSet.sbarBase_w = $Lib.clamp(libSet.sbarBase_w, 0, 400); + if (libSet.sbarWidth != libSet.sbarBase_w) { + libSet.sbarArrowWidth = Math.min(libSet.sbarArrowWidth, libSet.sbarWidth, 400); } else { - ppt.sbarArrowWidth = $Lib.clamp(ppt.sbarArrowWidth, 0, 400); - ppt.sbarWidth = $Lib.clamp(ppt.sbarWidth, ppt.sbarArrowWidth, 400); + libSet.sbarArrowWidth = $Lib.clamp(libSet.sbarArrowWidth, 0, 400); + libSet.sbarWidth = $Lib.clamp(libSet.sbarWidth, libSet.sbarArrowWidth, 400); } - ppt.sbarBase_w = ppt.sbarWidth; - this.sbar.w = ppt.sbarBase_w; - this.sbar.but_w = ppt.sbarArrowWidth; + libSet.sbarBase_w = libSet.sbarWidth; + this.sbar.w = libSet.sbarBase_w; + this.sbar.but_w = libSet.sbarArrowWidth; let themed_w = 21; try { themed_w = utils.GetSystemMetrics(2); } catch (e) {} - if (ppt.sbarWinMetrics) { + if (libSet.sbarWinMetrics) { this.sbar.w = themed_w; this.sbar.but_w = this.sbar.w; } - if (!ppt.sbarWinMetrics && this.sbar.type == 2) this.sbar.w = Math.max(this.sbar.w, 12); - if (!ppt.sbarShow) this.sbar.w = 0; + if (!libSet.sbarWinMetrics && this.sbar.type == 2) this.sbar.w = Math.max(this.sbar.w, 12); + if (!libSet.sbarShow) this.sbar.w = 0; this.sbar.but_h = this.sbar.w + (this.sbar.type != 2 ? 1 : 0); if (this.sbar.type != 2) { - if (ppt.sbarButType || !this.sbar.type && this.sbar.but_w < Math.round(15 * $Lib.scale)) this.sbar.but_w += 1; + if (libSet.sbarButType || !this.sbar.type && this.sbar.but_w < Math.round(15 * $Lib.scale)) this.sbar.but_w += 1; else if (this.sbar.type == 1 && this.sbar.but_w < Math.round(14 * $Lib.scale)) this.sbar.arrowPad += 1; } this.sz.pad = this.sbar.w - this.sbar.but_w < 5 || this.sbar.type == 2 ? this.l.w : 0; @@ -997,78 +996,78 @@ class UserInterface { } wheel(step, all) { - if (!vk.k('shift')) { - const textZoom = panel.m.x >= ui.x + Math.round(this.icon.w + this.sz.margin + (ppt.rootNode && !pop.inlineRoot ? ppt.treeIndent : 0)); - if (panel.m.y > panel.search.h + ui.y && textZoom || all) this.zoomText(step); - if (panel.m.y > panel.search.h + ui.y && !textZoom || all) this.zoomNode(step); - if (panel.m.y <= panel.search.h + ui.y || all) this.zoomFilter(step); - img.sizeDebounce(); + if (!lib.vk.k('shift')) { + const textZoom = lib.panel.m.x >= lib.ui.x + Math.round(this.icon.w + this.sz.margin + (libSet.rootNode && !lib.pop.inlineRoot ? libSet.treeIndent : 0)); + if (lib.panel.m.y > lib.panel.search.h + lib.ui.y && textZoom || all) this.zoomText(step); + if (lib.panel.m.y > lib.panel.search.h + lib.ui.y && !textZoom || all) this.zoomNode(step); + if (lib.panel.m.y <= lib.panel.search.h + lib.ui.y || all) this.zoomFilter(step); + libImg.sizeDebounce(); } - if (vk.k('shift') && (panel.m.y > panel.search.h + ui.y || all)) this.imageZoom(step); + if (lib.vk.k('shift') && (lib.panel.m.y > lib.panel.search.h + lib.ui.y || all)) this.imageZoom(step); window.Repaint(); } zoomDrag(x, y) { - if (sbar.touch.dn && vk.k('zoom')) { - sbar.logScroll(); - pop.deactivateTooltip(); + if (lib.sbar.touch.dn && lib.vk.k('zoom')) { + lib.sbar.logScroll(); + lib.pop.deactivateTooltip(); const y_delta = (y - this.sz.y_start); if (Math.abs(y_delta) > this.h / 50) { this.wheel(y - this.sz.y_start >= 0 ? -1 : 1, true); this.sz.y_start = y; } - sbar.setScroll(); + lib.sbar.setScroll(); } } zoomFilter(step) { - if (panel.zoomFilter < 0.7) return; - panel.zoomFilter += step * 0.1; - panel.zoomFilter = Math.max(panel.zoomFilter, 0.7); - panel.setTopBar(); - panel.calcText(); - but.refresh(true); - ppt.zoomFilter = Math.round(panel.zoomFilter * 100); + if (lib.panel.zoomFilter < 0.7) return; + lib.panel.zoomFilter += step * 0.1; + lib.panel.zoomFilter = Math.max(lib.panel.zoomFilter, 0.7); + lib.panel.setTopBar(); + lib.panel.calcText(); + lib.but.refresh(true); + libSet.zoomFilter = Math.round(lib.panel.zoomFilter * 100); } zoomNode(step) { this.sz.node += step; this.calcText(); - panel.on_size(); + lib.panel.on_size(); } zoomText(step) { - sbar.logScroll(); - pop.deactivateTooltip(); + lib.sbar.logScroll(); + lib.pop.deactivateTooltip(); this.font.zoomSize += step; this.font.zoomSize = Math.max(this.font.zoomSize, 1); const fnm = this.font.main.Name; const fst = this.font.main.Style; - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`] || 14; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`] || 14; this.font.main = gdi.Font(fnm, this.font.zoomSize, fst); this.font.search = gdi.Font(fnm, this.font.zoomSize, 0); this.font.find = gdi.Font(fnm, this.font.zoomSize * 1.5, 1); - this.font.label = gdi.Font(fnm, !ppt.treeAutoExpand || ppt.libSource != 2 ? Math.round(this.font.zoomSize * 11 / 14) : this.font.zoomSize, fst); - this.font.small = gdi.Font(fnm, !ppt.treeAutoExpand || ppt.libSource != 2 ? Math.round(this.font.zoomSize * 12 / 14) : this.font.zoomSize, fst); + this.font.label = gdi.Font(fnm, !libSet.treeAutoExpand || libSet.libSource != 2 ? Math.round(this.font.zoomSize * 11 / 14) : this.font.zoomSize, fst); + this.font.small = gdi.Font(fnm, !libSet.treeAutoExpand || libSet.libSource != 2 ? Math.round(this.font.zoomSize * 12 / 14) : this.font.zoomSize, fst); this.font.tracks = gdi.Font('Arial', Math.round(this.font.zoomSize * 12 / 14), 2); - this.sbar.narrowWidth = ppt.narrowSbarWidth == 0 ? $Lib.clamp(Math.floor(this.font.zoomSize / 7), 2, 10) : ppt.narrowSbarWidth; + this.sbar.narrowWidth = libSet.narrowSbarWidth == 0 ? $Lib.clamp(Math.floor(this.font.zoomSize / 7), 2, 10) : libSet.narrowSbarWidth; this.font.group = gdi.Font(this.font.group.Name, this.font.zoomSize, this.font.group.Style); this.font.lot = gdi.Font(this.font.lot.Name, this.font.zoomSize, this.font.lot.Style); this.font.statistics = gdi.Font(this.font.statistics.Name, this.font.zoomSize, this.font.statistics.Style); this.calcText(true); - panel.on_size(); - find.on_size(); + lib.panel.on_size(); + lib.find.on_size(); - if (this.style.topBarShow || ppt.sbarShow) but.refresh(true); - sbar.reset(); - ppt.zoomFont = Math.round(this.font.zoomSize / libraryFontSize * 100); - sbar.setScroll(); + if (this.style.topBarShow || libSet.sbarShow) lib.but.refresh(true); + lib.sbar.reset(); + libSet.zoomFont = Math.round(this.font.zoomSize / libraryFontSize * 100); + lib.sbar.setScroll(); } } -class Vkeys { +class LibVkeys { constructor() { this.selAll = 1; this.copy = 3; @@ -1116,10 +1115,13 @@ class Vkeys { } } -let colourSelector = {} -let sync = { image: () => {} } -const syncer = pref.customLibraryDir ? `${globals.customLibraryDir}cache\\library\\themed\\libraryTreeSyncTheme.js` : `${fb.ProfilePath}cache\\library\\themed\\libraryTreeSyncTheme.js`; -if (ppt.themed && $Lib.file(syncer)) include(syncer); +/** @global @type {object} */ +let libColourSelector = {} +/** @global @type {{image: Function}} */ +let libSync = { image: () => {} } +/** @global @type {string} */ +const libSyncer = grSet.customLibraryDir ? `${grCfg.customLibraryDir}cache\\library\\themed\\libraryTreeSyncTheme.js` : `${fb.ProfilePath}cache\\library\\themed\\libraryTreeSyncTheme.js`; +if (libSet.themed && $Lib.file(libSyncer)) include(libSyncer); /* eslint-disable */ (function(root,pluralize){root.pluralize=pluralize()})(this,function(){var pluralRules=[];var singularRules=[];var uncountables={};var irregularPlurals={};var irregularSingles={};function sanitizeRule(rule){if(typeof rule==='string'){return new RegExp('^'+rule+'$','i')}return rule}function restoreCase(word,token){if(word===token)return token;if(word===word.toLowerCase())return token.toLowerCase();if(word===word.toUpperCase())return token.toUpperCase();if(word[0]===word[0].toUpperCase()){return token.charAt(0).toUpperCase()+token.substr(1).toLowerCase()}return token.toLowerCase()}function interpolate(str,args){return str.replace(/\$(\d{1,2})/g,function(match,index){return args[index]||''})}function replace(word,rule){return word.replace(rule[0],function(match,index){var result=interpolate(rule[1],arguments);if(match===''){return restoreCase(word[index-1],result)}return restoreCase(match,result)})}function sanitizeWord(token,word,rules){if(!token.length||$Lib.objHasOwnProperty(uncountables, token)){return word}var len=rules.length;while(len--){var rule=rules[len];if(rule[0].test(word))return replace(word,rule)}return word}function replaceWord(replaceMap,keepMap,rules){return function(word){var token=word.toLowerCase();if($Lib.objHasOwnProperty(keepMap, token)){return restoreCase(word,token)}if($Lib.objHasOwnProperty(replaceMap, token)){return restoreCase(word,replaceMap[token])}return sanitizeWord(token,word,rules)}}function checkWord(replaceMap,keepMap,rules){return function(word){var token=word.toLowerCase();if($Lib.objHasOwnProperty(keepMap, token))return true;if($Lib.objHasOwnProperty(replaceMap, token))return false;return sanitizeWord(token,token,rules)===token}}function pluralize(word,count,inclusive){if (word.length < 2) return word;var pluralized=count===1?pluralize.singular(word):pluralize.plural(word);return(inclusive?count+' ':'')+pluralized}pluralize.plural=replaceWord(irregularSingles,irregularPlurals,pluralRules);pluralize.isPlural=checkWord(irregularSingles,irregularPlurals,pluralRules);pluralize.singular=replaceWord(irregularPlurals,irregularSingles,singularRules);pluralize.isSingular=checkWord(irregularPlurals,irregularSingles,singularRules);pluralize.addPluralRule=function(rule,replacement){pluralRules.push([sanitizeRule(rule),replacement])};pluralize.addSingularRule=function(rule,replacement){singularRules.push([sanitizeRule(rule),replacement])};pluralize.addUncountableRule=function(word){if(typeof word==='string'){uncountables[word.toLowerCase()]=true;return}pluralize.addPluralRule(word,'$0');pluralize.addSingularRule(word,'$0')};pluralize.addIrregularRule=function(single,plural){plural=plural.toLowerCase();single=single.toLowerCase();irregularSingles[single]=plural;irregularPlurals[plural]=single};[['I','we'],['me','us'],['he','they'],['she','they'],['them','them'],['myself','ourselves'],['yourself','yourselves'],['itself','themselves'],['herself','themselves'],['himself','themselves'],['themself','themselves'],['is','are'],['was','were'],['has','have'],['this','these'],['that','those'],['echo','echoes'],['dingo','dingoes'],['volcano','volcanoes'],['tornado','tornadoes'],['torpedo','torpedoes'],['genus','genera'],['viscus','viscera'],['stigma','stigmata'],['stoma','stomata'],['dogma','dogmata'],['lemma','lemmata'],['schema','schemata'],['anathema','anathemata'],['ox','oxen'],['axe','axes'],['die','dice'],['yes','yeses'],['foot','feet'],['eave','eaves'],['goose','geese'],['tooth','teeth'],['quiz','quizzes'],['human','humans'],['proof','proofs'],['carve','carves'],['valve','valves'],['looey','looies'],['thief','thieves'],['groove','grooves'],['pickaxe','pickaxes'],['passerby','passersby']].forEach(function(rule){return pluralize.addIrregularRule(rule[0],rule[1])});[[/s?$/i,'s'],[/[^\u0000-\u007F]$/i,'$0'],[/([^aeiou]ese)$/i,'$1'],[/(ax|test)is$/i,'$1es'],[/(alias|[^aou]us|t[lm]as|gas|ris)$/i,'$1es'],[/(e[mn]u)s?$/i,'$1s'],[/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i,'$1'],[/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,'$1i'],[/(alumn|alg|vertebr)(?:a|ae)$/i,'$1ae'],[/(seraph|cherub)(?:im)?$/i,'$1im'],[/(her|at|gr)o$/i,'$1oes'],[/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i,'$1a'],[/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i,'$1a'],[/sis$/i,'ses'],[/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i,'$1$2ves'],[/([^aeiouy]|qu)y$/i,'$1ies'],[/([^ch][ieo][ln])ey$/i,'$1ies'],[/(x|ch|ss|sh|zz)$/i,'$1es'],[/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i,'$1ices'],[/\b((?:tit)?m|l)(?:ice|ouse)$/i,'$1ice'],[/(pe)(?:rson|ople)$/i,'$1ople'],[/(child)(?:ren)?$/i,'$1ren'],[/eaux$/i,'$0'],[/m[ae]n$/i,'men'],['thou','you']].forEach(function(rule){return pluralize.addPluralRule(rule[0],rule[1])});[[/s$/i,''],[/(ss)$/i,'$1'],[/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i,'$1fe'],[/(ar|(?:wo|[ae])l|[eo][ao])ves$/i,'$1f'],[/ies$/i,'y'],[/\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i,'$1ie'],[/\b(mon|smil)ies$/i,'$1ey'],[/\b((?:tit)?m|l)ice$/i,'$1ouse'],[/(seraph|cherub)im$/i,'$1'],[/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i,'$1'],[/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i,'$1sis'],[/(movie|twelve|abuse|e[mn]u)s$/i,'$1'],[/(test)(?:is|es)$/i,'$1is'],[/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,'$1us'],[/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i,'$1um'],[/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,'$1on'],[/(alumn|alg|vertebr)ae$/i,'$1a'],[/(cod|mur|sil|vert|ind)ices$/i,'$1ex'],[/(matr|append)ices$/i,'$1ix'],[/(pe)(rson|ople)$/i,'$1rson'],[/(child)ren$/i,'$1'],[/(eau)x?$/i,'$1'],[/men$/i,'man']].forEach(function(rule){return pluralize.addSingularRule(rule[0],rule[1])});['a','an','and','as','at','but','by','en','for','if','in','nor','of','on','or','per','the','to','vs','via','adulthood','advice','agenda','aid','aircraft','alcohol','allmusic','ammo','analytics','anime','athletics','audio','bison','blood','bream','buffalo','butter','carp','cash','chassis','chess','clothing','cod','commerce','cooperation','corps','debris','diabetes','digestion','elk','energy','equipment','excretion','expertise','firmware','flounder','folder','fun','gallows','garbage','graffiti','hardware','headquarters','health','herpes','highjinks','homework','housework','information','jeans','justice','kudos','labour','lastfm','last.fm','listener','literature','machinery','mackerel','mail','media','mews','moose','music','mud','manga','news','only','personnel','pike','plankton','playcount','pliers','police','pollution','premises','rain','research','rice','salmon','scissors','series','sewage','shambles','shrimp','similar','software','species','staff','swine','tennis','traffic','transportation','trout','tuna','wealth','welfare','whiting','wildebeest','wildlife','wikipedia','you',/pok[eé]mon$/i,/[^aeiou]ese$/i,/deer$/i,/fish$/i,/measles$/i,/o[iu]s$/i,/pox$/i,/sheep$/i].forEach(pluralize.addUncountableRule);return pluralize}); diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-library.js b/profile/georgia-reborn/scripts/Library/scripts/lib-library.js index 9bdb1677..581ae2da 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-library.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-library.js @@ -1,6 +1,6 @@ 'use strict'; -class Library { +class LibLibrary { constructor() { this.allmusic = []; this.checkSelection = true; @@ -16,7 +16,7 @@ class Library { this.noListUpd = false; this.none = ''; this.node = []; - this.prefix = ppt.prefix.split('|'); + this.prefix = libSet.prefix.split('|'); this.root = []; this.scr = []; this.searchCache = {}; @@ -28,19 +28,19 @@ class Library { this.v2_init = fb.Version.startsWith('2') && fb.IsLibraryEnabled(); this.validSearch = true; - ppt.autoExpandLimit = $Lib.clamp(ppt.autoExpandLimit, 10, 1000); + libSet.autoExpandLimit = $Lib.clamp(libSet.autoExpandLimit, 10, 1000); this.lib_update = $Lib.debounce(() => { this.time.Reset(); - pop.cache = { + lib.pop.cache = { standard: {}, search: {}, filter: {} }; this.searchCache = {}; - this.upd_search = !!panel.search.txt; - this.rootNodes(2, ppt.process); - pop.getTreeSel(); + this.upd_search = !!lib.panel.search.txt; + this.rootNodes(2, libSet.process); + lib.pop.getTreeSel(); }, 500); this.playlist_update = $Lib.debounce((playlistIndex) => { @@ -52,26 +52,26 @@ class Library { this.search = $Lib.debounce(() => { this.upd_search = true; this.time.Reset(); - pop.cache.search = {}; + lib.pop.cache.search = {}; this.setNodes(); - panel.setHeight(true); - if (panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.list.Count ? lib.list : panel.list); - else if (!panel.search.txt.length) pop.notifySelection(); - if (ppt.searchSend != 2) return; - if (panel.search.txt) pop.load(panel.list, false, false, false, true, false); - else plman.ClearPlaylist(plman.FindOrCreatePlaylist(ppt.libPlaylist.replace(/%view_name%/i, panel.viewName), false)); + lib.panel.setHeight(true); + if (lib.panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.lib.list.Count ? lib.lib.list : lib.panel.list); + else if (!lib.panel.search.txt.length) lib.pop.notifySelection(); + if (libSet.searchSend != 2) return; + if (lib.panel.search.txt) lib.pop.load(lib.panel.list, false, false, false, true, false); + else plman.ClearPlaylist(plman.FindOrCreatePlaylist(libSet.libPlaylist.replace(/%view_name%/i, lib.panel.viewName), false)); }, 333); this.search500 = $Lib.debounce(() => { this.upd_search = true; this.time.Reset(); - pop.cache.search = {}; + lib.pop.cache.search = {}; this.setNodes(); - panel.setHeight(true); - if (panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.list.Count ? lib.list : panel.list); - else if (!panel.search.txt.length) pop.notifySelection(); - if (ppt.searchSend != 2) return; - pop.load(panel.list, false, false, false, true, false); + lib.panel.setHeight(true); + if (lib.panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.lib.list.Count ? lib.lib.list : lib.panel.list); + else if (!lib.panel.search.txt.length) lib.pop.notifySelection(); + if (libSet.searchSend != 2) return; + lib.pop.load(lib.panel.list, false, false, false, true, false); }, 500); this.checkView(); @@ -87,47 +87,47 @@ class Library { this.full_list_need_sort = true; switch (true) { case handleList.Count < 100: { - const lis = ppt.filterBy && !this.filterQuery.includes('$searchtext') ? $Lib.query(handleList, this.filterQuery) : handleList; - panel.sort(lis); - this.binaryInsert(panel.folderView, lis, this.list, this.libNode); + const lis = libSet.filterBy && !this.filterQuery.includes('$searchtext') ? $Lib.query(handleList, this.filterQuery) : handleList; + lib.panel.sort(lis); + this.binaryInsert(lib.panel.folderView, lis, this.list, this.libNode); if (this.list.Count) this.empty = ''; - if (panel.search.txt) { + if (lib.panel.search.txt) { let newSearchItems = new FbMetadbHandleList(); this.validSearch = true; try { - newSearchItems = fb.GetQueryItems(handleList, !this.filterQuery.includes('$searchtext') ? panel.search.txt : this.filterQuery.replace(/\$searchtext/g, panel.search.txt)); + newSearchItems = fb.GetQueryItems(handleList, !this.filterQuery.includes('$searchtext') ? lib.panel.search.txt : this.filterQuery.replace(/\$searchtext/g, lib.panel.search.txt)); } catch (e) { this.validSearch = false; } - this.binaryInsert(panel.folderView, newSearchItems, panel.list, this.searchNode); - if (!panel.list.Count) { - pop.clearTree(); - sbar.setRows(0); + this.binaryInsert(lib.panel.folderView, newSearchItems, lib.panel.list, this.searchNode); + if (!lib.panel.list.Count) { + lib.pop.clearTree(); + lib.sbar.setRows(0); this.none = this.validSearch ? 'Nothing found' : 'Invalid search expression'; - panel.treePaint(); + lib.panel.treePaint(); this.noListUpd = true; } - } else panel.list = this.list; + } else lib.panel.list = this.list; break; } default: - if (ppt.filterBy && !this.filterQuery.includes('$searchtext')) { + if (libSet.filterBy && !this.filterQuery.includes('$searchtext')) { const newFilterItems = $Lib.query(handleList, this.filterQuery); this.list.InsertRange(this.list.Count, newFilterItems); - panel.sort(this.list); + lib.panel.sort(this.list); } else { - if (this.full_list_need_sort) panel.sort(this.full_list); + if (this.full_list_need_sort) lib.panel.sort(this.full_list); this.list = this.full_list.Clone(); this.full_list_need_sort = false; } - panel.sort(handleList); + lib.panel.sort(handleList); switch (true) { - case !panel.folderView: { - const tfo = FbTitleFormat(panel.view); + case !lib.panel.folderView: { + const tfo = FbTitleFormat(lib.panel.view); items = tfo.EvalWithMetadbs(handleList); handleList.Convert().forEach((h, j) => { i = this.list.Find(h); - if (i != -1) this.format(items[j], panel.splitter, i, this.libNode); + if (i != -1) this.format(items[j], lib.panel.splitter, i, this.libNode); }); break; } @@ -140,43 +140,43 @@ class Library { break; } if (this.list.Count) this.empty = ''; - if (panel.search.txt) { + if (lib.panel.search.txt) { let newSearchItems = new FbMetadbHandleList(); this.validSearch = true; try { - newSearchItems = fb.GetQueryItems(handleList, !this.filterQuery.includes('$searchtext') ? panel.search.txt : this.filterQuery.replace(/\$searchtext/g, panel.search.txt)); + newSearchItems = fb.GetQueryItems(handleList, !this.filterQuery.includes('$searchtext') ? lib.panel.search.txt : this.filterQuery.replace(/\$searchtext/g, lib.panel.search.txt)); } catch (e) { this.validSearch = false; } - panel.list.InsertRange(panel.list.Count, newSearchItems); - panel.sort(panel.list); - panel.sort(newSearchItems); + lib.panel.list.InsertRange(lib.panel.list.Count, newSearchItems); + lib.panel.sort(lib.panel.list); + lib.panel.sort(newSearchItems); switch (true) { - case !panel.folderView: { - const tfo = FbTitleFormat(panel.view); + case !lib.panel.folderView: { + const tfo = FbTitleFormat(lib.panel.view); items = tfo.EvalWithMetadbs(newSearchItems); newSearchItems.Convert().forEach((h, j) => { - i = panel.list.Find(h); - if (i != -1) this.format(items[j], panel.splitter, i, this.searchNode); + i = lib.panel.list.Find(h); + if (i != -1) this.format(items[j], lib.panel.splitter, i, this.searchNode); }); break; } default: items = newSearchItems.GetLibraryRelativePaths(); newSearchItems.Convert().forEach((h, j) => { - i = panel.list.Find(h); + i = lib.panel.list.Find(h); if (i != -1) this.format(items[j], '\\', i, this.searchNode); }); break; } - if (!panel.list.Count) { - pop.clearTree(); - sbar.setRows(0); + if (!lib.panel.list.Count) { + lib.pop.clearTree(); + lib.sbar.setRows(0); this.none = this.validSearch ? 'Nothing found' : 'Invalid search expression'; - panel.treePaint(); + lib.panel.treePaint(); this.noListUpd = true; } - } else panel.list = this.list; + } else lib.panel.list = this.list; break; } } @@ -186,19 +186,19 @@ class Library { let items; switch (true) { case handleList.Count < 100: - this.binaryInsert(panel.folderView, handleList, this.list, this.libNode); + this.binaryInsert(lib.panel.folderView, handleList, this.list, this.libNode); break; default: this.list.InsertRange(this.list.Count, handleList); - panel.sort(this.list); - panel.sort(handleList); + lib.panel.sort(this.list); + lib.panel.sort(handleList); switch (true) { - case !panel.folderView: { - const tfo = FbTitleFormat(panel.view); + case !lib.panel.folderView: { + const tfo = FbTitleFormat(lib.panel.view); items = tfo.EvalWithMetadbs(handleList); handleList.Convert().forEach((h, j) => { i = this.list.Find(h); - if (i != -1) this.format(items[j], panel.splitter, i, this.libNode); + if (i != -1) this.format(items[j], lib.panel.splitter, i, this.libNode); }); if (!this.list.Count) this.none = 'Nothing found'; break; @@ -220,25 +220,25 @@ class Library { let items; switch (true) { case handleList.Count < 100: - this.binaryInsert(panel.folderView, handleList, panel.list, this.searchNode); + this.binaryInsert(lib.panel.folderView, handleList, lib.panel.list, this.searchNode); break; default: - panel.list.InsertRange(panel.list.Count, handleList); - panel.sort(panel.list); + lib.panel.list.InsertRange(lib.panel.list.Count, handleList); + lib.panel.sort(lib.panel.list); switch (true) { - case !panel.folderView: { - const tfo = FbTitleFormat(panel.view); + case !lib.panel.folderView: { + const tfo = FbTitleFormat(lib.panel.view); items = tfo.EvalWithMetadbs(handleList); handleList.Convert().forEach((h, j) => { - i = panel.list.Find(h); - if (i != -1) this.format(items[j], panel.splitter, i, this.searchNode); + i = lib.panel.list.Find(h); + if (i != -1) this.format(items[j], lib.panel.splitter, i, this.searchNode); }); break; } default: items = handleList.GetLibraryRelativePaths(); handleList.Convert().forEach((h, j) => { - i = panel.list.Find(h); + i = lib.panel.list.Find(h); if (i != -1) this.format(items[j], '\\', i, this.searchNode); }); break; @@ -252,7 +252,7 @@ class Library { let index = Math.floor((min + max) / 2); while (max > min) { const tmp = new FbMetadbHandleList([item, li[index]]); - panel.sort(tmp); + lib.panel.sort(tmp); if (item.Compare(tmp[0])) max = index; else min = index + 1; index = Math.floor((min + max) / 2); @@ -265,11 +265,11 @@ class Library { let items; switch (true) { case !folder: { - const tfo = FbTitleFormat(panel.view); + const tfo = FbTitleFormat(lib.panel.view); items = tfo.EvalWithMetadbs(insert); insert.Convert().forEach((h, j) => { i = this.bInsert(h, li); - this.format(items[j], panel.splitter, i, n); + this.format(items[j], lib.panel.splitter, i, n); li.Insert(i, h); }); break; @@ -286,123 +286,123 @@ class Library { } checkAutoExpand() { - if (panel.imgView) return; - if (!ppt.treeAutoExpand || panel.list.Count >= ppt.autoExpandLimit || !pop.tree.length) return false; - let m = pop.tree.length; - const rootNode = ppt.rootNode; - const n = !!(rootNode && pop.tree.length > 1); - pop.expandedTracks = 0; - pop.expandLmt = ppt.autoExpandLimit; + if (lib.panel.imgView) return; + if (!libSet.treeAutoExpand || lib.panel.list.Count >= libSet.autoExpandLimit || !lib.pop.tree.length) return false; + let m = lib.pop.tree.length; + const rootNode = libSet.rootNode; + const n = !!(rootNode && lib.pop.tree.length > 1); + lib.pop.expandedTracks = 0; + lib.pop.expandLmt = libSet.autoExpandLimit; while (m--) { - pop.expandNodes(pop.tree[m], !(!rootNode || m)); + lib.pop.expandNodes(lib.pop.tree[m], !(!rootNode || m)); if (n && m == 1) break; } - sbar.setRows(pop.tree.length); - panel.treePaint(); + lib.sbar.setRows(lib.pop.tree.length); + lib.panel.treePaint(); return true; } checkFilter() { - pop.cache.filter = {}; - pop.cache.search = {}; + lib.pop.cache.filter = {}; + lib.pop.cache.search = {}; this.searchCache = {}; - if (panel.filter.mode[ppt.filterBy].type.match(/\$nowplaying{(.+?)}/)) { + if (lib.panel.filter.mode[libSet.filterBy].type.match(/\$nowplaying{(.+?)}/)) { this.getFilterQuery(); if (this.filterQuery != this.filterQueryID) { - if (!ppt.rememberTree && !ppt.reset) this.logTree(); - else if (ppt.rememberTree) this.logFilter(); - if (panel.search.txt) lib.upd_search = true; + if (!libSet.rememberTree && !libSet.reset) this.logTree(); + else if (libSet.rememberTree) this.logFilter(); + if (lib.panel.search.txt) lib.lib.upd_search = true; this.getLibrary(); - this.rootNodes(!ppt.reset ? 1 : 0, true); - if (!pop.notifySelection()) { - const list = !panel.search.txt.length || !lib.list.Count ? lib.list : panel.list; - window.NotifyOthers(window.Name, ppt.filterBy ? list : new FbMetadbHandleList()); + this.rootNodes(!libSet.reset ? 1 : 0, true); + if (!lib.pop.notifySelection()) { + const list = !lib.panel.search.txt.length || !lib.lib.list.Count ? lib.lib.list : lib.panel.list; + window.NotifyOthers(window.Name, libSet.filterBy ? list : new FbMetadbHandleList()); } - if (ppt.searchSend == 2 && panel.search.txt.length) pop.load(panel.list, false, false, false, true, false); - pop.checkAutoHeight(); + if (libSet.searchSend == 2 && lib.panel.search.txt.length) lib.pop.load(lib.panel.list, false, false, false, true, false); + lib.pop.checkAutoHeight(); } } - if (panel.filter.mode[ppt.filterBy].type.match(/\$selected{(.+?)}/)) { + if (lib.panel.filter.mode[libSet.filterBy].type.match(/\$selected{(.+?)}/)) { this.getFilterQuery(); if (this.filterQuery != this.filterQueryID) { - if (!ppt.rememberTree && !ppt.reset) this.logTree(); - else if (ppt.rememberTree) this.logFilter(); - if (panel.search.txt) lib.upd_search = true; + if (!libSet.rememberTree && !libSet.reset) this.logTree(); + else if (libSet.rememberTree) this.logFilter(); + if (lib.panel.search.txt) lib.lib.upd_search = true; this.getLibrary(); - this.rootNodes(!ppt.reset ? 1 : 0, true); - if (!pop.notifySelection()) { - const list = !panel.search.txt.length || !lib.list.Count ? lib.list : panel.list; - window.NotifyOthers(window.Name, ppt.filterBy ? list : new FbMetadbHandleList()); + this.rootNodes(!libSet.reset ? 1 : 0, true); + if (!lib.pop.notifySelection()) { + const list = !lib.panel.search.txt.length || !lib.lib.list.Count ? lib.lib.list : lib.panel.list; + window.NotifyOthers(window.Name, libSet.filterBy ? list : new FbMetadbHandleList()); } - if (ppt.searchSend == 2 && panel.search.txt.length) pop.load(panel.list, false, false, false, true, false); - pop.checkAutoHeight(); + if (libSet.searchSend == 2 && lib.panel.search.txt.length) lib.pop.load(lib.panel.list, false, false, false, true, false); + lib.pop.checkAutoHeight(); } } } checkLines(arr, arrExpanded) { - if (ppt.albumArtGrpLevel) return; // user set - const defaultView = !panel.folderView ? panel.defaultViews.indexOf(panel.grp[ppt.viewBy].type.trim()) : 6; + if (libSet.albumArtGrpLevel) return; // user set + const defaultView = !lib.panel.folderView ? lib.panel.defaultViews.indexOf(lib.panel.grp[libSet.viewBy].type.trim()) : 6; if (defaultView != -1) { - panel.lines = [ - [2, 2, 2, 2, 2, 2, 2, 2][ppt.artId], - [2, 2, 2, 2, 2, 2, 2, 2][ppt.artId], 1, 1, 1, 2, 2, 2, 2, 2 + lib.panel.lines = [ + [2, 2, 2, 2, 2, 2, 2, 2][libSet.artId], + [2, 2, 2, 2, 2, 2, 2, 2][libSet.artId], 1, 1, 1, 2, 2, 2, 2, 2 ][defaultView]; return; } const lengths = arr.map(v => v.length); const avg = $Lib.average(lengths); - if (avg < (!arrExpanded ? 3 : 2)) panel.lines = 1; + if (avg < (!arrExpanded ? 3 : 2)) lib.panel.lines = 1; } checkStatistics(handleList) { - pop.tree.forEach(v => { + lib.pop.tree.forEach(v => { delete v.statistics; delete v._statistics; }); - panel.sort(this.full_list); + lib.panel.sort(this.full_list); handleList.Convert().forEach(h => { const i = this.full_list.Find(h); if (i != -1) { ['standard', 'search', 'filter'].forEach(w => { - const keys = Object.keys(pop.cache[w]); + const keys = Object.keys(lib.pop.cache[w]); let j = keys.length - while (j--) if (pop.cache[w][keys[j]] && pop.cache[w][keys[j]].items.includes(i)) delete pop.cache[w][keys[j]]; + while (j--) if (lib.pop.cache[w][keys[j]] && lib.pop.cache[w][keys[j]].items.includes(i)) delete lib.pop.cache[w][keys[j]]; }); } }); - panel.treePaint(); + lib.panel.treePaint(); } checkTree() { if (!this.upd) return; this.lib_update.cancel(); this.time.Reset(); - pop.cache = { + lib.pop.cache = { standard: {}, search: {}, filter: {} }; this.searchCache = {}; - this.upd_search = !!panel.search.txt; - this.rootNodes(this.upd == 2 ? 2 : 1, ppt.process); + this.upd_search = !!lib.panel.search.txt; + this.rootNodes(this.upd == 2 ? 2 : 1, libSet.process); if (this.checkSelection) { - setSelection(ppt.followPlaylistFocus && !ppt.libSource ? fb.GetFocusItem() : panel.setSelection() ? fb.GetSelection() : null); + lib.call.setSelection(libSet.followPlaylistFocus && !libSet.libSource ? fb.GetFocusItem() : lib.panel.setSelection() ? fb.GetSelection() : null); this.checkSelection = false; } this.upd = 0; } checkView() { - const startIX = ppt.rememberView ? panel.grp.length : 0; + const startIX = libSet.rememberView ? lib.panel.grp.length : 0; for (let i = startIX; i < 100; i++) { - ppt.set(`Panel Library - Tree.View ${$Lib.padNumber(i, 2) + (!panel.imgView ? '' : ' Image')}`, null); // clear non-existent - ppt.set(`Panel Library - Tree.View ${$Lib.padNumber(i, 2) + (!panel.imgView ? ' Search' : ' Image Search')}`, null); // clear non-existent + libSet.set(`Panel Library - Tree.View ${$Lib.padNumber(i, 2) + (!lib.panel.imgView ? '' : ' Image')}`, null); // clear non-existent + libSet.set(`Panel Library - Tree.View ${$Lib.padNumber(i, 2) + (!lib.panel.imgView ? ' Search' : ' Image Search')}`, null); // clear non-existent } - if (ppt.rememberTree) { - this.exp = ppt.get(this.rememberViewProp(), JSON.stringify({})); + if (libSet.rememberTree) { + this.exp = libSet.get(this.rememberViewProp(), JSON.stringify({})); this.exp = $Lib.jsonParse(this.exp, {}); - } else ppt.set(!panel.imgView ? `Panel Library - Tree${panel.search.txt ? ' Search' : ''}` : `Panel Library - Tree Image${panel.search.txt ? ' Search' : ''}`, null); + } else libSet.set(!lib.panel.imgView ? `Panel Library - Tree${lib.panel.search.txt ? ' Search' : ''}` : `Panel Library - Tree Image${lib.panel.search.txt ? ' Search' : ''}`, null); } expandArr(arr) { @@ -440,7 +440,7 @@ class Library { format(item, splitter, i, node) { item = item.split(splitter); - if (panel.imgView && panel.lines == 2) { + if (lib.panel.imgView && lib.panel.lines == 2) { item[0] = `${item[0]}^@^${item[1] || '?'}`; item.splice(1, 1); } @@ -448,7 +448,7 @@ class Library { } getFilterQuery() { - this.filterQuery = panel.filter.mode[ppt.filterBy].type; + this.filterQuery = lib.panel.filter.mode[libSet.filterBy].type; while (this.filterQuery.includes('$nowplaying{')) { const q = this.filterQuery.match(/\$nowplaying{(.+?)}/); this.filterQuery = this.filterQuery.replace(q[0], this.eval(q[1], 'nowplaying') || '~#No Value For Item#~'); @@ -464,29 +464,29 @@ class Library { this.time.Reset(); this.none = ''; let fixedPlaylistIndex = -1; - if (ppt.fixedPlaylist) { - fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName); + if (libSet.fixedPlaylist) { + fixedPlaylistIndex = plman.FindPlaylist(libSet.fixedPlaylistName); if (fixedPlaylistIndex == -1) { - ppt.fixedPlaylist = false; - ppt.libSource = 0; + libSet.fixedPlaylist = false; + libSet.libSource = 0; } } if (!items) { - this.list = [plman.GetPlaylistItems($Lib.pl_active), !ppt.fixedPlaylist ? fb.GetLibraryItems() : plman.GetPlaylistItems(fixedPlaylistIndex), plman.GetPlaylistItems(plman.FindPlaylist(ppt.lastPanelSelectionPlaylist))][ppt.libSource]; - if (ppt.recItemImage && ppt.libSource == 2) ui.expandHandle = this.list.Count ? this.list[0] : null; - if (ppt.libSource != 2) this.full_list = this.list.Clone(); + this.list = [plman.GetPlaylistItems($Lib.pl_active), !libSet.fixedPlaylist ? fb.GetLibraryItems() : plman.GetPlaylistItems(fixedPlaylistIndex), plman.GetPlaylistItems(plman.FindPlaylist(libSet.lastPanelSelectionPlaylist))][libSet.libSource]; + if (libSet.recItemImage && libSet.libSource == 2) lib.ui.expandHandle = this.list.Count ? this.list[0] : null; + if (libSet.libSource != 2) this.full_list = this.list.Clone(); } - if (ppt.libSource && (!this.list.Count || !fb.IsLibraryEnabled() && ppt.libSource == 1)) { - pop.clearTree(); - sbar.setRows(0); - this.empty = ppt.libSource == 1 ? (!ppt.fixedPlaylist ? (!this.list.Count && this.v2_init ? 'Loading...\n\n' : 'Nothing to show\nClick here to configure the media library') : 'Nothing found\n\n') : 'Nothing received'; - panel.treePaint(); + if (libSet.libSource && (!this.list.Count || !fb.IsLibraryEnabled() && libSet.libSource == 1)) { + lib.pop.clearTree(); + lib.sbar.setRows(0); + this.empty = libSet.libSource == 1 ? (!libSet.fixedPlaylist ? (!this.list.Count && this.v2_init ? 'Loading...\n\n' : 'Nothing to show\nClick here to configure the media library') : 'Nothing found\n\n') : 'Nothing received'; + lib.panel.treePaint(); return; } - pop.libItems = true; - panel.forcePaint(); - if (ppt.filterBy) { + lib.pop.libItems = true; + lib.panel.forcePaint(); + if (libSet.filterBy) { this.getFilterQuery(); this.filterQueryID = this.filterQuery; if (!this.filterQuery.includes('$searchtext')) this.list = $Lib.query(this.list, this.filterQuery); @@ -495,13 +495,13 @@ class Library { this.filterQueryID = 'N/A'; } if (!this.list.Count) { - pop.clearTree(); - sbar.setRows(0); + lib.pop.clearTree(); + lib.sbar.setRows(0); this.none = 'Nothing found'; - panel.treePaint(); + lib.panel.treePaint(); return; } - this.rootNames('', 0, ppt.libSource == 2 ? false : items); + this.rootNames('', 0, libSet.libSource == 2 ? false : items); } getSearchList(n) { @@ -518,35 +518,35 @@ class Library { } initialise(handleList) { - lib.initialised = true; + lib.lib.initialised = true; this.load(handleList); this.getLibrary(true); - this.rootNodes(ppt.rememberTree, ppt.process); + this.rootNodes(libSet.rememberTree, libSet.process); } isMainChanged(handleList) { let i; let items; - const tree_type = !panel.folderView ? 0 : 1; + const tree_type = !lib.panel.folderView ? 0 : 1; switch (tree_type) { // check for changes to items; any change updates all case 0: { - const tfo = FbTitleFormat(panel.view); + const tfo = FbTitleFormat(lib.panel.view); items = tfo.EvalWithMetadbs(handleList); const ret = handleList.Convert().some((h, j) => { i = this.list.Find(h); if (i != -1) { let libItem = []; - if (!panel.imgView || panel.lines != 2) libItem = this.libNode[i]; + if (!lib.panel.imgView || lib.panel.lines != 2) libItem = this.libNode[i]; else { libItem = this.libNode[i].slice(); libItem[0] = libItem[0].split('^@^'); libItem = libItem.flat(); } - return !$Lib.equal(libItem, items[j].split(panel.splitter)); + return !$Lib.equal(libItem, items[j].split(lib.panel.splitter)); } }); if (ret) return true; - if (ppt.itemShowStatistics < 2) return false; + if (libSet.itemShowStatistics < 2) return false; this.checkStatistics(handleList); break; } @@ -557,7 +557,7 @@ class Library { i = this.list.Find(h); if (i != -1) { let libItem = []; - if (!panel.imgView || panel.lines != 2) libItem = this.libNode[i]; + if (!lib.panel.imgView || lib.panel.lines != 2) libItem = this.libNode[i]; else { libItem = this.libNode[i].slice(); libItem[0] = libItem[0].split('^@^'); @@ -567,7 +567,7 @@ class Library { } }); if (ret) return true; - if (ppt.itemShowStatistics < 2) return false; + if (libSet.itemShowStatistics < 2) return false; this.checkStatistics(handleList); break; } @@ -576,46 +576,46 @@ class Library { load(handleList) { let fixedPlaylistIndex = -1; - if (ppt.fixedPlaylist) { - fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName); + if (libSet.fixedPlaylist) { + fixedPlaylistIndex = plman.FindPlaylist(libSet.fixedPlaylistName); if (fixedPlaylistIndex == -1) { - ppt.fixedPlaylist = false; - ppt.libSource = 0; + libSet.fixedPlaylist = false; + libSet.libSource = 0; } } - this.list = [plman.GetPlaylistItems($Lib.pl_active), !ppt.fixedPlaylist ? (handleList || fb.GetLibraryItems()) : plman.GetPlaylistItems(fixedPlaylistIndex), plman.GetPlaylistItems(plman.FindPlaylist(ppt.lastPanelSelectionPlaylist))][ppt.libSource]; - if (ppt.recItemImage && ppt.libSource == 2) ui.expandHandle = this.list.Count ? this.list[0] : null; + this.list = [plman.GetPlaylistItems($Lib.pl_active), !libSet.fixedPlaylist ? (handleList || fb.GetLibraryItems()) : plman.GetPlaylistItems(fixedPlaylistIndex), plman.GetPlaylistItems(plman.FindPlaylist(libSet.lastPanelSelectionPlaylist))][libSet.libSource]; + if (libSet.recItemImage && libSet.libSource == 2) lib.ui.expandHandle = this.list.Count ? this.list[0] : null; this.full_list = this.list.Clone(); if (this.list.Count) this.v2_init = false; - if (ppt.libSource && (!this.list.Count || !fb.IsLibraryEnabled() && ppt.libSource == 1)) { - this.empty = ppt.libSource == 1 ? (!ppt.fixedPlaylist ? (!this.list.Count && this.v2_init ? 'Loading...\n\n' : 'Nothing to show\nClick here to configure the media library') : 'Nothing found\n\n') : 'Nothing received'; - panel.treePaint(); + if (libSet.libSource && (!this.list.Count || !fb.IsLibraryEnabled() && libSet.libSource == 1)) { + this.empty = libSet.libSource == 1 ? (!libSet.fixedPlaylist ? (!this.list.Count && this.v2_init ? 'Loading...\n\n' : 'Nothing to show\nClick here to configure the media library') : 'Nothing found\n\n') : 'Nothing received'; + lib.panel.treePaint(); } } logFilter() { - ppt.process = true; - const key = !ppt.rememberView ? 'def' : panel.viewName; + libSet.process = true; + const key = !libSet.rememberView ? 'def' : lib.panel.viewName; if (!$Lib.objHasOwnProperty(this.exp, key)) this.exp[key] = {}; - this.exp[key].filter = panel.filter.menu[ppt.filterBy]; - ppt.set(this.rememberViewProp(), JSON.stringify(this.exp)); + this.exp[key].filter = lib.panel.filter.menu[libSet.filterBy]; + libSet.set(this.rememberViewProp(), JSON.stringify(this.exp)); } logTree() { - if (!pop.tree.length) return; + if (!lib.pop.tree.length) return; let i = 0; let ix = -1; let level = 0; this.expand = []; - ppt.process = true; + libSet.process = true; this.sel = []; - pop.tree.forEach(v => { - level = !ppt.rootNode ? v.level : v.level - 1; + lib.pop.tree.forEach(v => { + level = !libSet.rootNode ? v.level : v.level - 1; if (v.child.length) { this.expand.push({ level, - a: level < 1 ? v.root || v.srt[0] : pop.tree[v.par].root || pop.tree[v.par].srt[0], + a: level < 1 ? v.root || v.srt[0] : lib.pop.tree[v.par].root || lib.pop.tree[v.par].srt[0], b: level < 1 ? '' : v.srt[0] }); } @@ -624,38 +624,38 @@ class Library { this.sel.push({ level, a: v.root || v.srt[0], - b: level != 0 ? pop.tree[v.par].root || pop.tree[v.par].srt[0] : '', - c: level > 1 ? pop.tree[pop.tree[v.par].par].root || pop.tree[pop.tree[v.par].par].srt[0] : '' + b: level != 0 ? lib.pop.tree[v.par].root || lib.pop.tree[v.par].srt[0] : '', + c: level > 1 ? lib.pop.tree[lib.pop.tree[v.par].par].root || lib.pop.tree[lib.pop.tree[v.par].par].srt[0] : '' }); } }); - ix = pop.get_ix((!panel.imgView ? 0 : img.panel.x + 1) + ui.x, (!panel.imgView || img.style.vertical ? panel.tree.y : panel.tree.x) + sbar.row.h / 2 + ui.y, true, false); + ix = lib.pop.get_ix((!lib.panel.imgView ? 0 : libImg.panel.x + 1) + lib.ui.x, (!lib.panel.imgView || libImg.style.vertical ? lib.panel.tree.y : lib.panel.tree.x) + lib.sbar.row.h / 2 + lib.ui.y, true, false); level = 0; - const l = Math.min(Math.floor(ix + panel.rows), pop.tree.length); + const l = Math.min(Math.floor(ix + lib.panel.rows), lib.pop.tree.length); if (ix != -1) { this.scr = []; for (i = ix; i < l; i++) { - level = pop.tree[i].level; + level = lib.pop.tree[i].level; this.scr.push({ level, - a: pop.tree[i].root || pop.tree[i].srt[0], - b: level != 0 ? pop.tree[pop.tree[i].par].root || pop.tree[pop.tree[i].par].srt[0] : '', - c: level > 1 ? pop.tree[pop.tree[pop.tree[i].par].par].root || pop.tree[pop.tree[pop.tree[i].par].par].srt[0] : '' + a: lib.pop.tree[i].root || lib.pop.tree[i].srt[0], + b: level != 0 ? lib.pop.tree[lib.pop.tree[i].par].root || lib.pop.tree[lib.pop.tree[i].par].srt[0] : '', + c: level > 1 ? lib.pop.tree[lib.pop.tree[lib.pop.tree[i].par].par].root || lib.pop.tree[lib.pop.tree[lib.pop.tree[i].par].par].srt[0] : '' }); } } this.sortByLevel(this.expand); - if (ppt.rememberTree) { - const key = !ppt.rememberView ? 'def' : panel.viewName; + if (libSet.rememberTree) { + const key = !libSet.rememberView ? 'def' : lib.panel.viewName; const cur_sel = this.exp[key] ? this.exp[key].sel : []; this.exp[key] = { exp: this.expand, - filter: panel.filter.menu[ppt.filterBy], + filter: lib.panel.filter.menu[libSet.filterBy], scr: this.scr, sel: this.sel.length ? this.sel : cur_sel, - s_txt: panel.search.txt + s_txt: lib.panel.search.txt }; - ppt.set(this.rememberViewProp(), JSON.stringify(this.exp)); + libSet.set(this.rememberViewProp(), JSON.stringify(this.exp)); } } @@ -703,47 +703,47 @@ class Library { } readTreeState(bypass, treeArtToggle) { - if (ppt.rememberTree) { - const key = !ppt.rememberView ? 'def' : panel.viewName; + if (libSet.rememberTree) { + const key = !libSet.rememberView ? 'def' : lib.panel.viewName; if (this.exp[key]) { this.expand = this.exp[key].exp || []; if (!treeArtToggle) { let tmpFilter = this.exp[key].filter || 'N/A'; - tmpFilter = panel.filter.menu.indexOf(tmpFilter); - ppt.filterBy = tmpFilter != -1 ? tmpFilter : 0; + tmpFilter = lib.panel.filter.menu.indexOf(tmpFilter); + libSet.filterBy = tmpFilter != -1 ? tmpFilter : 0; } this.scr = this.exp[key].scr || []; this.sel = this.exp[key].sel || []; - if (!treeArtToggle) panel.search.txt = this.exp[key].s_txt || ''; - panel.calcText(); - if (!bypass) but.setSearchBtnsHide(); + if (!treeArtToggle) lib.panel.search.txt = this.exp[key].s_txt || ''; + lib.panel.calcText(); + if (!bypass) lib.but.setSearchBtnsHide(); window.Repaint(); } else { this.exp = {}; - ppt.rememberView ? ppt.set(this.rememberViewProp(), null) : ppt.set(!panel.imgView ? `Panel Library - Tree${panel.search.txt ? ' Search' : ''}` : `Panel Library - Tree Image${panel.search.txt ? ' Search' : ''}`, JSON.stringify(this.exp)); + libSet.rememberView ? libSet.set(this.rememberViewProp(), null) : libSet.set(!lib.panel.imgView ? `Panel Library - Tree${lib.panel.search.txt ? ' Search' : ''}` : `Panel Library - Tree Image${lib.panel.search.txt ? ' Search' : ''}`, JSON.stringify(this.exp)); } - } else ppt.process = false; + } else libSet.process = false; } rememberViewProp() { let isValidProp; let prop; let property; - switch (ppt.rememberView) { + switch (libSet.rememberView) { case true: - property = `Panel Library - Tree.View ${$Lib.padNumber(ppt.viewBy, 2) + (!panel.imgView ? '' : ' Image')} Search`; - if (panel.search.txt) return property; - prop = ppt.get(property); + property = `Panel Library - Tree.View ${$Lib.padNumber(libSet.viewBy, 2) + (!lib.panel.imgView ? '' : ' Image')} Search`; + if (lib.panel.search.txt) return property; + prop = libSet.get(property); isValidProp = prop && prop.includes('exp'); if (isValidProp) return property; - return `Panel Library - Tree.View ${$Lib.padNumber(ppt.viewBy, 2) + (!panel.imgView ? '' : ' Image')}`; + return `Panel Library - Tree.View ${$Lib.padNumber(libSet.viewBy, 2) + (!lib.panel.imgView ? '' : ' Image')}`; case false: - property = !panel.imgView ? 'Panel Library - Tree Search' : 'Panel Library - Tree Image Search'; - if (panel.search.txt) return property; - prop = ppt.get(property); + property = !lib.panel.imgView ? 'Panel Library - Tree Search' : 'Panel Library - Tree Image Search'; + if (lib.panel.search.txt) return property; + prop = libSet.get(property); isValidProp = prop && prop.includes('exp'); if (isValidProp) return property; - return !panel.imgView ? 'Panel Library - Tree' : 'Panel Library - Tree Image'; + return !lib.panel.imgView ? 'Panel Library - Tree' : 'Panel Library - Tree Image'; } } @@ -756,35 +756,35 @@ class Library { this.libNode.splice(i, 1); } } - if (ppt.filterBy) { + if (libSet.filterBy) { j = handleList.Count; - if (this.full_list_need_sort) panel.sort(this.full_list); + if (this.full_list_need_sort) lib.panel.sort(this.full_list); this.full_list_need_sort = false; while (j--) { i = this.full_list.Find(handleList[j]); if (i != -1) this.full_list.RemoveById(i); } } else this.full_list = this.list.Clone(); - if (panel.search.txt) { + if (lib.panel.search.txt) { j = handleList.Count; while (j--) { - i = panel.list.Find(handleList[j]); + i = lib.panel.list.Find(handleList[j]); if (i != -1) { - panel.list.RemoveById(i); + lib.panel.list.RemoveById(i); this.searchNode.splice(i, 1); } } - if (!panel.list.Count) { - pop.clearTree(); - sbar.setRows(0); + if (!lib.panel.list.Count) { + lib.pop.clearTree(); + lib.sbar.setRows(0); this.none = this.validSearch ? 'Nothing found' : 'Invalid search expression'; - panel.treePaint(); + lib.panel.treePaint(); this.noListUpd = true; } - } else panel.list = this.list; + } else lib.panel.list = this.list; - if (ppt.libSource && !this.full_list.Count) { - this.empty = ppt.libSource == 1 ? (!ppt.fixedPlaylist ? 'Nothing to show\nClick here to configure the media library' : 'Nothing found\n\n') : 'Nothing received'; + if (libSet.libSource && !this.full_list.Count) { + this.empty = libSet.libSource == 1 ? (!libSet.fixedPlaylist ? 'Nothing to show\nClick here to configure the media library' : 'Nothing found\n\n') : 'Nothing received'; } } @@ -802,22 +802,22 @@ class Library { removedSearch(handleList) { let j = handleList.Count; while (j--) { - const i = panel.list.Find(handleList[j]); + const i = lib.panel.list.Find(handleList[j]); if (i != -1) { - panel.list.RemoveById(i); + lib.panel.list.RemoveById(i); this.searchNode.splice(i, 1); } } } rootNames(li, search, treeArtToggle) { - const tree_type = !panel.folderView ? 0 : 1; + const tree_type = !lib.panel.folderView ? 0 : 1; let arr = []; - if (!treeArtToggle || !panel.samePattern) { + if (!treeArtToggle || !lib.panel.samePattern) { switch (search) { case 0: - if (ppt.libSource || panel.multiProcess) panel.sort(this.list); - li = panel.list = this.list; + if (libSet.libSource || lib.panel.multiProcess) lib.panel.sort(this.list); + li = lib.panel.list = this.list; this.libNode = []; arr = this.libNode; break; @@ -828,27 +828,27 @@ class Library { } } - if (!treeArtToggle || !panel.samePattern) { + if (!treeArtToggle || !lib.panel.samePattern) { switch (tree_type) { case 0: { - const tfo = FbTitleFormat(panel.view); - tfo.EvalWithMetadbs(li).forEach((v, i) => arr[i] = v.split(panel.splitter)); + const tfo = FbTitleFormat(lib.panel.view); + tfo.EvalWithMetadbs(li).forEach((v, i) => arr[i] = v.split(lib.panel.splitter)); break; } case 1: li.GetLibraryRelativePaths().forEach((v, i) => arr[i] = v.length ? v.split('\\') : ['File(s) Not In Library']); break; } - if (panel.imgView && panel.lines == 2) this.checkLines(arr); - if (panel.imgView && panel.lines == 2) this.expandArr(arr); + if (lib.panel.imgView && lib.panel.lines == 2) this.checkLines(arr); + if (lib.panel.imgView && lib.panel.lines == 2) this.expandArr(arr); } else { arr = !search ? this.libNode : this.searchNode; const arrExpanded = arr.length && arr[0][0].includes('^@^'); - if (panel.imgView && panel.lines == 2) this.checkLines(arr, arrExpanded); - if (panel.imgView) { - if (panel.lines == 2) { + if (lib.panel.imgView && lib.panel.lines == 2) this.checkLines(arr, arrExpanded); + if (lib.panel.imgView) { + if (lib.panel.lines == 2) { if (!arrExpanded) this.expandArr(arr); - } else if (panel.lines == 1) { + } else if (lib.panel.lines == 1) { if (arrExpanded) this.flattenArr(arr); } } else if (arrExpanded) this.flattenArr(arr); @@ -860,29 +860,29 @@ class Library { this.root = []; let i = 0; let n = ''; - if (panel.search.txt && (this.upd_search || lib_update === true)) { + if (lib.panel.search.txt && (this.upd_search || lib_update === true)) { this.validSearch = true; this.none = ''; try { - panel.list = fb.GetQueryItems(this.getSearchList(panel.search.txt) || this.list, !this.filterQuery.includes('$searchtext') ? panel.search.txt : this.filterQuery.replace(/\$searchtext/g, panel.search.txt)); - this.searchCache[panel.search.txt] = panel.list; + lib.panel.list = fb.GetQueryItems(this.getSearchList(lib.panel.search.txt) || this.list, !this.filterQuery.includes('$searchtext') ? lib.panel.search.txt : this.filterQuery.replace(/\$searchtext/g, lib.panel.search.txt)); + this.searchCache[lib.panel.search.txt] = lib.panel.list; } catch (e) { this.list = this.list.Clone(); - panel.list.RemoveAll(); + lib.panel.list.RemoveAll(); this.validSearch = false; } - if (!panel.list.Count) { - pop.clearTree(); - sbar.setRows(0); + if (!lib.panel.list.Count) { + lib.pop.clearTree(); + lib.sbar.setRows(0); this.none = this.validSearch ? 'Nothing found' : 'Invalid search expression'; - panel.treePaint(); + lib.panel.treePaint(); return; } - this.rootNames(panel.list, 1); + this.rootNames(lib.panel.list, 1); this.node = this.searchNode; this.upd_search = false; - } else if (!panel.search.txt) { - panel.list = this.list; + } else if (!lib.panel.search.txt) { + lib.panel.list = this.list; this.node = this.libNode; this.searchNode = []; this.searchCache = {}; @@ -891,16 +891,16 @@ class Library { let n_o = '#get_node#'; let nU = ''; let start = 0; - const total = panel.list.Count; - pop.getNowplaying(); - if (ppt.rootNode) { + const total = lib.panel.list.Count; + lib.pop.getNowplaying(); + if (libSet.rootNode) { this.root[0] = { root: 'Root Node', - nm: panel.rootName, + nm: lib.panel.rootName, sel: false, child: [], item: this.set(start, total - 1), - srt: this.sort(panel.rootName) + srt: this.sort(lib.panel.rootName) }; } else { @@ -911,7 +911,7 @@ class Library { n_o = nU; if (i > 0) this.root[i - 1].item = this.set(start, end); start = l; - if (panel.multiPrefix) n = this.prefixes(n); + if (lib.panel.multiPrefix) n = this.prefixes(n); this.root[i] = { nm: n, sel: false, @@ -924,28 +924,28 @@ class Library { }); if (i > 0) this.root[i - 1].item = this.set(start, end); } - if (!lib_update) sbar.reset(); + if (!lib_update) lib.sbar.reset(); /* Draw tree -> */ - if (!ppt.rootNode || panel.search.txt) pop.buildTree(this.root, 0); - if (ppt.rootNode) pop.branch(this.root[0], true); - if (panel.pn_h_auto && !panel.imgView && (panel.init || lib_update) && ppt.pn_h == ppt.pn_h_min && pop.tree[0]) pop.clearChild(pop.tree[0]); - panel.init = false; // $Lib.trace('initialised in: ' + this.time.Time / 1000 + ' seconds'); + if (!libSet.rootNode || lib.panel.search.txt) lib.pop.buildTree(this.root, 0); + if (libSet.rootNode) lib.pop.branch(this.root[0], true); + if (lib.panel.pn_h_auto && !lib.panel.imgView && (lib.panel.init || lib_update) && libSet.pn_h == libSet.pn_h_min && lib.pop.tree[0]) lib.pop.clearChild(lib.pop.tree[0]); + lib.panel.init = false; // $Lib.trace('initialised in: ' + this.time.Time / 1000 + ' seconds'); if (lib_update !== 2) this.checkAutoExpand(); if (lib_update && process) { - if (!panel.imgView) { + if (!lib.panel.imgView) { this.expand.forEach(v => { if (v.level == 0) { - pop.tree.some(w => { + lib.pop.tree.some(w => { if (this.match(w, v.a)) { - pop.branch(w); + lib.pop.branch(w); return true; } }); } else if (v.level > 0) { - pop.tree.some(w => { - if (this.match(w, v.b) && this.match(pop.tree[w.par], v.a)) { - pop.branch(w); + lib.pop.tree.some(w => { + if (this.match(w, v.b) && this.match(lib.pop.tree[w.par], v.a)) { + lib.pop.branch(w); return true; } }); @@ -954,16 +954,16 @@ class Library { } this.sel.forEach(v => { if (v.level == 0) { - pop.tree.some(w => { + lib.pop.tree.some(w => { if (this.match(w, v.a)) return w.sel = true; }); } else if (v.level == 1) { - pop.tree.some(w => { - if (this.match(w, v.a) && this.match(pop.tree[w.par], v.b)) return w.sel = true; + lib.pop.tree.some(w => { + if (this.match(w, v.a) && this.match(lib.pop.tree[w.par], v.b)) return w.sel = true; }); } else if (v.level > 1) { - pop.tree.some(w => { - if (this.match(w, v.a) && this.match(pop.tree[w.par], v.b) && this.match(pop.tree[pop.tree[w.par].par], v.c)) return w.sel = true; + lib.pop.tree.some(w => { + if (this.match(w, v.a) && this.match(lib.pop.tree[w.par], v.b) && this.match(lib.pop.tree[lib.pop.tree[w.par].par], v.c)) return w.sel = true; }); } }); @@ -971,37 +971,37 @@ class Library { this.scr.some((v, h) => { if (scr_pos) return true; if (v.level == 0) { - pop.tree.some((w, j) => { + lib.pop.tree.some((w, j) => { if (this.match(w, v.a)) { - sbar.scrollMemory(h, j); + lib.sbar.scrollMemory(h, j); return scr_pos = true; } }); } else if (v.level == 1) { - pop.tree.some((w, j) => { - if (this.match(w, v.a) && this.match(pop.tree[w.par], v.b)) { - sbar.scrollMemory(h, j); + lib.pop.tree.some((w, j) => { + if (this.match(w, v.a) && this.match(lib.pop.tree[w.par], v.b)) { + lib.sbar.scrollMemory(h, j); return scr_pos = true; } }); } else if (v.level > 1) { - pop.tree.some((w, j) => { - if (this.match(w, v.a) && this.match(pop.tree[w.par], v.b) && this.match(pop.tree[pop.tree[w.par].par], v.c)) { - sbar.scrollMemory(h, j); + lib.pop.tree.some((w, j) => { + if (this.match(w, v.a) && this.match(lib.pop.tree[w.par], v.b) && this.match(lib.pop.tree[lib.pop.tree[w.par].par], v.c)) { + lib.sbar.scrollMemory(h, j); return scr_pos = true; } }); } }); if (!scr_pos) { - sbar.reset(); - panel.treePaint(); + lib.sbar.reset(); + lib.panel.treePaint(); } - } else this.treeState(false, ppt.rememberTree); - if (panel.imgView && total) img.load(); + } else this.treeState(false, libSet.rememberTree); + if (lib.panel.imgView && total) libImg.load(); if (lib_update && !process) { - sbar.reset(); - panel.treePaint(); + lib.sbar.reset(); + lib.panel.treePaint(); } } @@ -1015,30 +1015,30 @@ class Library { setMemory(i) { const o = ['rememberTree', 'rememberView'][i]; - ppt[o] = ppt[o] ? 0 : 1; + libSet[o] = libSet[o] ? 0 : 1; this.checkView(); this.logTree(); } setNodes() { - if (panel.search.txt == '' && ppt.rememberPreSearch) { - ppt.set(this.rememberViewProp(), JSON.stringify({})); + if (lib.panel.search.txt == '' && libSet.rememberPreSearch) { + libSet.set(this.rememberViewProp(), JSON.stringify({})); this.checkView(); this.logFilter(); this.readTreeState(true); - this.rootNodes(ppt.rememberTree, ppt.process); - this.treeState(false, ppt.rememberTree); + this.rootNodes(libSet.rememberTree, libSet.process); + this.treeState(false, libSet.rememberTree); } else { - this.treeState(false, ppt.rememberTree); + this.treeState(false, libSet.rememberTree); this.rootNodes(); } } sort(name) { - if (panel.multiProcess) name = name.replace(/#!#/g, ''); - if (panel.noDisplay) name = name.replace(/#@#/g, ''); - if (panel.colMarker) name = name.replace(/@!#.*?@!#/g, ''); - if (panel.imgView) name = name.replace(/\^@\^/g, ' '); + if (lib.panel.multiProcess) name = name.replace(/#!#/g, ''); + if (lib.panel.noDisplay) name = name.replace(/#@#/g, ''); + if (lib.panel.colMarker) name = name.replace(/@!#.*?@!#/g, ''); + if (lib.panel.imgView) name = name.replace(/\^@\^/g, ' '); return [name, name, name, false]; } @@ -1049,11 +1049,11 @@ class Library { treeState(reset, state, handleList, handleType) { if (!state) return; - panel.searchPaint(); - panel.treePaint(); - ppt.process = false; + lib.panel.searchPaint(); + lib.panel.treePaint(); + libSet.process = false; if (!reset) this.logTree(); - if (ppt.rememberTree && state === true) return; + if (libSet.rememberTree && state === true) return; if (handleType == 3) { this.getLibrary(true); this.rootNodes(true, true); @@ -1074,7 +1074,7 @@ class Library { handleList.MakeDifference(origList); if (handleList.Count) this.added(handleList); if (this.noListUpd) break; - if (ui.w < 1 || !window.IsVisible) this.upd = 2; + if (lib.ui.w < 1 || !window.IsVisible) this.upd = 2; else this.lib_update(); break; } @@ -1084,11 +1084,11 @@ class Library { if (isMainChanged) { this.removed(handleList); this.added(handleList); - if (ui.w < 1 || !window.IsVisible) this.upd = 2; + if (lib.ui.w < 1 || !window.IsVisible) this.upd = 2; else this.lib_update(isMainChanged); break; } - if (ppt.filterBy && !this.filterQuery.includes('$searchtext')) { // filter: check if not done + if (libSet.filterBy && !this.filterQuery.includes('$searchtext')) { // filter: check if not done const newFilterItems = $Lib.query(handleList, this.filterQuery); const origFilter = this.list.Clone(); // addns @@ -1105,25 +1105,25 @@ class Library { removeFilterItems.MakeDifference(handlesInFilter); // handles to remove if (removeFilterItems.Count) this.removedFilter(removeFilterItems); if (newFilterItems.Count || removeFilterItems.Count) { - if (!panel.search.txt) panel.list = this.list; - if (ui.w < 1 || !window.IsVisible) this.upd = 2; + if (!lib.panel.search.txt) lib.panel.list = this.list; + if (lib.ui.w < 1 || !window.IsVisible) this.upd = 2; else this.lib_update(); } } - if (panel.search.txt) { // search: check if not done + if (lib.panel.search.txt) { // search: check if not done let handlesInSearch = new FbMetadbHandleList(); let newSearchItems = new FbMetadbHandleList(); - const origSearch = panel.list.Clone(); + const origSearch = lib.panel.list.Clone(); // addns this.validSearch = true; try { - newSearchItems = fb.GetQueryItems(handleList, !this.filterQuery.includes('$searchtext') ? panel.search.txt : this.filterQuery.replace(/\$searchtext/g, panel.search.txt)); + newSearchItems = fb.GetQueryItems(handleList, !this.filterQuery.includes('$searchtext') ? lib.panel.search.txt : this.filterQuery.replace(/\$searchtext/g, lib.panel.search.txt)); } catch (e) { this.validSearch = false; } origSearch.Sort(); newSearchItems.Sort(); - if (ppt.filterBy) { + if (libSet.filterBy) { const newFilt = this.list.Clone(); newFilt.Sort(); newSearchItems.MakeIntersection(newFilt); @@ -1136,7 +1136,7 @@ class Library { removeSearchItems.MakeIntersection(origSearch); // handles in origSearch (present in any filter) this.validSearch = true; try { - handlesInSearch = fb.GetQueryItems(removeSearchItems, !this.filterQuery.includes('$searchtext') ? panel.search.txt : this.filterQuery.replace(/\$searchtext/g, panel.search.txt)); + handlesInSearch = fb.GetQueryItems(removeSearchItems, !this.filterQuery.includes('$searchtext') ? lib.panel.search.txt : this.filterQuery.replace(/\$searchtext/g, lib.panel.search.txt)); } catch (e) { this.validSearch = false; } @@ -1144,14 +1144,14 @@ class Library { removeSearchItems.MakeDifference(handlesInSearch); // handles to remove if (removeSearchItems.Count) this.removedSearch(removeSearchItems); if (newSearchItems.Count || removeSearchItems.Count) { - if (!panel.list.Count) { - pop.clearTree(); - sbar.setRows(0); + if (!lib.panel.list.Count) { + lib.pop.clearTree(); + lib.sbar.setRows(0); this.none = this.validSearch ? 'Nothing found' : 'Invalid search expression'; - panel.treePaint(); + lib.panel.treePaint(); break; } - if (ui.w < 1 || !window.IsVisible) this.upd = 2; + if (lib.ui.w < 1 || !window.IsVisible) this.upd = 2; else this.lib_update(); } } @@ -1160,7 +1160,7 @@ class Library { case 2: this.removed(handleList); if (this.noListUpd) break; - if (ui.w < 1 || !window.IsVisible) this.upd = 2; + if (lib.ui.w < 1 || !window.IsVisible) this.upd = 2; else this.lib_update(); break; } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-menu.js b/profile/georgia-reborn/scripts/Library/scripts/lib-menu.js index 0ca87cda..9e79d8dc 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-menu.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-menu.js @@ -1,9 +1,11 @@ 'use strict'; -const MF_GRAYED_LIB = 0x00000001; -const MF_STRING_LIB = 0x00000000; +/** @global @type {number} */ +const LIB_MF_GRAYED = 0x00000001; +/** @global @type {number} */ +const LIB_MF_STRING = 0x00000000; -class MenuManager { +class LibMenuManager { constructor(name, clearArr, baseMenu) { this.baseMenu = baseMenu || 'baseMenu'; this.clearArr = clearArr; @@ -79,7 +81,7 @@ addSeparator({ menuName = this.baseMenu, separator = true }) { this.menuItems.pu } load(x, y) { - if (!this.menuItems.length) men[this.name](); + if (!this.menuItems.length) lib.men[this.name](); let i = 0; let ln = this.menuNames.length; while (i < ln) { @@ -96,9 +98,9 @@ addSeparator({ menuName = this.baseMenu, separator = true }) { this.menuItems.pu } let Context; - if (men.show_context) { + if (lib.men.show_context) { Context = fb.CreateContextMenuManager(); - Context.InitContext(men.items); + Context.InitContext(lib.men.items); this.menu[this.baseMenu].AppendMenuSeparator(); Context.BuildMenu(this.menu[this.baseMenu], 5000); } @@ -106,17 +108,17 @@ addSeparator({ menuName = this.baseMenu, separator = true }) { this.menuItems.pu const idx = this.menu[this.baseMenu].TrackPopupMenu(x, y); this.run(idx); - if (men.show_context) { + if (lib.men.show_context) { if (idx >= 5000 && idx <= 5800) Context.ExecuteByID(idx - 5000); - men.show_context = false; + lib.men.show_context = false; } this.clear(); } - newItem({ str = null, func = null, menuName = this.baseMenu, flags = MF_STRING_LIB, checkItem = false, checkRadio = false, separator = false, hide = false }) { this.menuItems.push({ str, func, menuName, flags, checkItem, checkRadio, separator, hide }); } + newItem({ str = null, func = null, menuName = this.baseMenu, flags = LIB_MF_STRING, checkItem = false, checkRadio = false, separator = false, hide = false }) { this.menuItems.push({ str, func, menuName, flags, checkItem, checkRadio, separator, hide }); } - newMenu({ menuName = this.baseMenu, str = '', appendTo = this.baseMenu, flags = MF_STRING_LIB, separator = false, hide = false }) { + newMenu({ menuName = this.baseMenu, str = '', appendTo = this.baseMenu, flags = LIB_MF_STRING, separator = false, hide = false }) { this.menuNames.push(menuName); if (menuName != this.baseMenu) this.menuItems.push({ menuName, appendMenu: true, str, appendTo, flags, separator, hide }); } @@ -127,13 +129,38 @@ addSeparator({ menuName = this.baseMenu, separator = true }) { this.menuItems.pu } } -const clearArr = true; -const menu = new MenuManager('mainMenu', clearArr); -const fMenu = new MenuManager('filterMenu', clearArr); -const sMenu = new MenuManager('searchHistoryMenu', clearArr); -const searchMenu = new MenuManager('searchMenu'); - -class MenuItems { +/** @global @type {boolean} */ +const libClearArr = true; + +/** + * The instance of `LibMenuManager` class for library main menu operations. + * @typedef {LibMenuManager} + * @global + */ +const libMenu = new LibMenuManager('mainMenu', libClearArr); + +/** + * The instance of `LibMenuManager` class for library filter menu operations. + * @typedef {LibMenuManager} + * @global + */ +const libFMenu = new LibMenuManager('filterMenu', libClearArr); + +/** + * The instance of `LibMenuManager` class for library search histry menu operations. + * @typedef {LibMenuManager} + * @global + */ +const libSMenu = new LibMenuManager('searchHistoryMenu', libClearArr); + +/** + * The instance of `LibMenuManager` class for library search menu operations. + * @typedef {LibMenuManager} + * @global + */ +const libSearchMenu = new LibMenuManager('searchMenu'); + +class LibMenuItems { constructor() { this.expandable = false; this.ix = -1; @@ -142,7 +169,7 @@ class MenuItems { this.pl = []; this.r_up = false; this.show_context = false; - this.treeExpandLimit = $Lib.file('C:\\check_local\\1450343922.txt') ? 6000 : $Lib.clamp(ppt.treeExpandLimit, 10, 6000); + this.treeExpandLimit = $Lib.file('C:\\check_local\\1450343922.txt') ? 6000 : $Lib.clamp(libSet.treeExpandLimit, 10, 6000); this.playlists_changed(true); this.settingsBtnDn = false; this.validItem = false; @@ -151,23 +178,23 @@ class MenuItems { // * METHODS * // mainMenu() { - menu.newMenu({ hide: !this.settingsBtnDn && ppt.settingsShow && this.validItem }); + libMenu.newMenu({ hide: !this.settingsBtnDn && libSet.settingsShow && this.validItem }); // * Top menu options Library submenu - menu.newItem({ + libMenu.newItem({ str: 'Library options menu', func: () => { - topMenuOptions(state.mouse_x, state.mouse_y, true, false, false, true); + grm.topMenu.topMenuOptions(grm.ui.state.mouse_x, grm.ui.state.mouse_y, true, false, false, true); }, separator: () => true }); - if (pref.layout === 'default' && pref.theme.startsWith('custom')) { - menu.newItem({ + if (grSet.layout === 'default' && grSet.theme.startsWith('custom')) { + libMenu.newItem({ str: 'Edit custom theme', func: () => { - displayCustomThemeMenu = true; - initCustomThemeMenu(false, false, 'lib_bg'); + grm.ui.displayCustomThemeMenu = true; + grm.cthMenu.initCustomThemeMenu(false, false, 'lib_bg'); window.Repaint(); }, separator: () => true @@ -175,36 +202,36 @@ class MenuItems { } // * Library layout switcher - menu.newItem({ - str: pref.libraryLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', + libMenu.newItem({ + str: grSet.libraryLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', func: () => { - pref.libraryLayout = pref.libraryLayout === 'normal' ? 'full' : 'normal'; - displayPlaylist = pref.libraryLayout === 'split'; - g_properties.auto_collapse = false; - playlist.expand_header(); - if (pref.panelWidthAuto) { - initPanelWidthAuto(); + grSet.libraryLayout = grSet.libraryLayout === 'normal' ? 'full' : 'normal'; + grm.ui.displayPlaylist = grSet.libraryLayout === 'split'; + plSet.auto_collapse = false; + pl.playlist.header_expand(); + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); } - initLibraryLayout(); + grm.ui.initLibraryLayout(); }, - hide: () => pref.layout === 'default' && pref.libraryDesign === 'flowMode' || pref.layout !== 'default' + hide: () => grSet.layout === 'default' && grSet.libraryDesign === 'flowMode' || grSet.layout !== 'default' }); - menu.newItem({ - str: pref.libraryLayout === 'split' ? 'Change layout to full' : 'Change layout to split', + libMenu.newItem({ + str: grSet.libraryLayout === 'split' ? 'Change layout to full' : 'Change layout to split', func: () => { - pref.libraryLayout = pref.libraryLayout === 'split' ? 'full' : 'split'; - displayPlaylist = pref.libraryLayout === 'split'; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); + grSet.libraryLayout = grSet.libraryLayout === 'split' ? 'full' : 'split'; + grm.ui.displayPlaylist = grSet.libraryLayout === 'split'; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); } - initLibraryLayout(); + grm.ui.initLibraryLayout(); }, separator: () => true, - hide: () => pref.layout === 'default' && pref.libraryDesign === 'flowMode' || pref.layout !== 'default' + hide: () => grSet.layout === 'default' && grSet.libraryDesign === 'flowMode' || grSet.layout !== 'default' }); if (this.validItem) { - ['Add to current playlist\tShift+enter', 'Send to new playlist\tCtrl+enter', 'Show now playing'].forEach((v, i) => menu.newItem({ + ['Add to current playlist\tShift+enter', 'Send to new playlist\tCtrl+enter', 'Show now playing'].forEach((v, i) => libMenu.newItem({ str: v, func: () => this.setPlaylist(i), flags: this.getPaylistFlag(i), @@ -212,146 +239,146 @@ class MenuItems { })); } - if (this.validItem && ppt.albumArtOptionsShow) { - menu.newItem({ - str: !panel.imgView ? 'Show album art' : (!ppt.facetView ? 'Show tree' : 'Show text'), + if (this.validItem && libSet.albumArtOptionsShow) { + libMenu.newItem({ + str: !lib.panel.imgView ? 'Show album art' : (!libSet.facetView ? 'Show tree' : 'Show text'), func: () => this.setPlaylist(3), - flags: !panel.pn_h_auto || ppt.pn_h != ppt.pn_h_min ? MF_STRING_LIB : MF_GRAYED_LIB, - separator: !panel.imgView //|| this.show_context && !ui.style.topBarShow + flags: !lib.panel.pn_h_auto || libSet.pn_h != libSet.pn_h_min ? LIB_MF_STRING : LIB_MF_GRAYED, + separator: !lib.panel.imgView //|| this.show_context && !lib.ui.style.topBarShow }); } - if (this.validItem && panel.imgView) { - menu.newItem({ - str: ppt.artId != 4 ? 'Show artists' : 'Show albums', - func: () => { ppt.artId = ppt.artId != 4 ? 4 : 0; this.setPlaylist(4); }, - separator: this.show_context && !ui.style.topBarShow + if (this.validItem && lib.panel.imgView) { + libMenu.newItem({ + str: libSet.artId != 4 ? 'Show artists' : 'Show albums', + func: () => { libSet.artId = libSet.artId != 4 ? 4 : 0; this.setPlaylist(4); }, + separator: this.show_context && !lib.ui.style.topBarShow }); } for (let i = 0; i < 2; i++) { - menu.newItem({ + libMenu.newItem({ str: [this.items.Count ? 'Refresh selected images...' : 'Refresh images: none selected', 'Refresh all images...'][i], func: () => this.setMode(i), - flags: () => panel.imgView && !i && this.items.Count || !panel.imgView || i ? MF_STRING_LIB : MF_GRAYED_LIB, - hide: () => !this.validItem || !ppt.albumArtShow + flags: () => lib.panel.imgView && !i && this.items.Count || !lib.panel.imgView || i ? LIB_MF_STRING : LIB_MF_GRAYED, + hide: () => !this.validItem || !libSet.albumArtShow }); } - if (this.validItem && !panel.imgView) { - ['Collapse all\tNum -', 'Expand\tNum *'].forEach((v, i) => menu.newItem({ + if (this.validItem && !lib.panel.imgView) { + ['Collapse all\tNum -', 'Expand\tNum *'].forEach((v, i) => libMenu.newItem({ str: v, func: () => this.setTreeState(i), - flags: !i || i == 1 && this.expandable ? MF_STRING_LIB : MF_GRAYED_LIB, - separator: i == 1 && this.show_context && (!ppt.settingsShow && !ppt.searchShow && !ppt.filterShow || this.shift) + flags: !i || i == 1 && this.expandable ? LIB_MF_STRING : LIB_MF_GRAYED, + separator: i == 1 && this.show_context && (!libSet.settingsShow && !libSet.searchShow && !libSet.filterShow || this.shift) })); } - menu.newMenu({ menuName: 'Settings', hide: !this.show_context || ui.style.topBarShow && !this.shift }); + libMenu.newMenu({ menuName: 'Settings', hide: !this.show_context || lib.ui.style.topBarShow && !this.shift }); const mainMenu = () => this.show_context ? 'Settings' : 'baseMenu'; - menu.newMenu({ menuName: 'Views', separator: false }); - panel.menu.forEach((v, i) => menu.newItem({ + libMenu.newMenu({ menuName: 'Views', separator: false }); + lib.panel.menu.forEach((v, i) => libMenu.newItem({ menuName: 'Views', str: v, func: () => this.setView(i), - checkRadio: i == ppt.viewBy, - separator: i > panel.menu.length - 3 + checkRadio: i == libSet.viewBy, + separator: i > lib.panel.menu.length - 3 })); const d = {}; this.getSortData(d); - menu.newMenu({ menuName: d.menuName, flags: d.sortType ? MF_STRING_LIB : MF_GRAYED_LIB, separator: this.show_context }); + libMenu.newMenu({ menuName: d.menuName, flags: d.sortType ? LIB_MF_STRING : LIB_MF_GRAYED, separator: this.show_context }); if (d.sortType) { - menu.newItem({ + libMenu.newItem({ menuName: d.menuName, str: ['', 'By year', 'Albums by year'][d.sortType], - flags: MF_GRAYED_LIB, + flags: LIB_MF_GRAYED, separator: true }); const menuSort = [[], ['Default', 'Ascending', 'Descending'], ['Default', 'Ascending (hide year)', 'Ascending (show year)', 'Descending (hide year)', 'Descending (show year)', 'Action: year after album', 'Action: year before album']][d.sortType]; - menuSort.forEach((v, i) => menu.newItem({ + menuSort.forEach((v, i) => libMenu.newItem({ menuName: d.menuName, str: v, func: () => this.sortByDate(i, d), - flags: i > 4 && (d.sortIX == 1 || d.sortIX == 3) ? MF_GRAYED_LIB : MF_STRING_LIB, - checkRadio: d.sortIX == -1 && !i || i == d.sortIX || d.sortType == 2 && i == 5 && !ppt.yearBeforeAlbum || i == 6 && ppt.yearBeforeAlbum, + flags: i > 4 && (d.sortIX == 1 || d.sortIX == 3) ? LIB_MF_GRAYED : LIB_MF_STRING, + checkRadio: d.sortIX == -1 && !i || i == d.sortIX || d.sortType == 2 && i == 5 && !libSet.yearBeforeAlbum || i == 6 && libSet.yearBeforeAlbum, separator: i == 0 || d.sortType == 2 && (i == 2 || i == 4) })); } - menu.newItem({ + libMenu.newItem({ str: 'Write theme to tags', func: () => WriteThemeTags() }); - const meta_handler = new MetaHandler(); - menu.newItem({ + const meta_handler = new PlaylistMetaHandler(); + libMenu.newItem({ str: 'Write album statistics to tags', func: () => meta_handler.write_album_stats_to_tags() }); - menu.newItem({ + libMenu.newItem({ menuName: 'Views', str: 'Configure views...', - func: () => panel.open('views') + func: () => lib.panel.open('views') }); - menu.newMenu({ menuName: 'Statistics', appendTo: mainMenu(), separator: true }); - [pop.countsRight && !panel.imgView ? ['None', '# Tracks', '# Items'][pop.nodeCounts] : 'None', 'Bitrate', 'Duration', 'Total size', 'Rating', 'Popularity', 'Date', 'Playback queue', 'Playcount', 'First played', 'Last played', 'Added', 'Configure statistics...'].forEach((v, i) => menu.newItem({ + libMenu.newMenu({ menuName: 'Statistics', appendTo: mainMenu(), separator: true }); + [lib.pop.countsRight && !lib.panel.imgView ? ['None', '# Tracks', '# Items'][lib.pop.nodeCounts] : 'None', 'Bitrate', 'Duration', 'Total size', 'Rating', 'Popularity', 'Date', 'Playback queue', 'Playcount', 'First played', 'Last played', 'Added', 'Configure statistics...'].forEach((v, i) => libMenu.newItem({ menuName: 'Statistics', str: v, func: () => this.setStatistics(i), - checkRadio: i == ppt.itemShowStatistics, + checkRadio: i == libSet.itemShowStatistics, separator: !i || i == 7 || i == 11 })); - menu.newMenu({ menuName: 'Album art', appendTo: mainMenu(), hide: !panel.imgView }); - ['Front', 'Back', 'Disc', 'Icon', 'Artist', 'Group: auto', 'Group: top level', 'Group: two levels', 'Change group name...', 'Configure album art...'].forEach((v, i) => menu.newItem({ + libMenu.newMenu({ menuName: 'Album art', appendTo: mainMenu(), hide: !lib.panel.imgView }); + ['Front', 'Back', 'Disc', 'Icon', 'Artist', 'Group: auto', 'Group: top level', 'Group: two levels', 'Change group name...', 'Configure album art...'].forEach((v, i) => libMenu.newItem({ menuName: 'Album art', str: v, func: () => this.setAlbumart(i), - flags: i == 8 && (panel.folderView || ppt.rootNode != 3) ? MF_GRAYED_LIB : MF_STRING_LIB, - checkRadio: i == ppt.artId || i - 5 == ppt.albumArtGrpLevel, + flags: i == 8 && (lib.panel.folderView || libSet.rootNode != 3) ? LIB_MF_GRAYED : LIB_MF_STRING, + checkRadio: i == libSet.artId || i - 5 == libSet.albumArtGrpLevel, separator: i == 4 || i == 7 || i == 8 })); - menu.newMenu({ menuName: 'Quick setup', appendTo: mainMenu() }); + libMenu.newMenu({ menuName: 'Quick setup', appendTo: mainMenu() }); - ['Georgia-Reborn'].forEach((v, i) => menu.newItem({ + ['Georgia-Reborn'].forEach((v, i) => libMenu.newItem({ menuName: 'Quick setup', str: v, - func: () => panel.set('quickSetup', 12), - flags: () => i != 10 || this.items.Count ? MF_STRING_LIB : MF_GRAYED_LIB, + func: () => lib.panel.set('quickSetup', 12), + flags: () => i != 10 || this.items.Count ? LIB_MF_STRING : LIB_MF_GRAYED, separator: i == 0 })); - ['Traditional', 'Modern', 'Ultra-Modern', 'Clean', 'Facet'].forEach((v, i) => menu.newItem({ + ['Traditional', 'Modern', 'Ultra-Modern', 'Clean', 'Facet'].forEach((v, i) => libMenu.newItem({ menuName: 'Quick setup', str: v, - func: () => panel.set('quickSetup', i), + func: () => lib.panel.set('quickSetup', i), separator: i == 3 || i == 4 })); - if (ppt.albumArtOptionsShow) { - ['Covers [labels right]', 'Covers [labels bottom]', 'Covers [labels blend]', 'Artist photos [labels right]', pref.libraryThumbnailSize === 'auto' ? 'Album art size + (disable thumbnail auto-size)' : 'Album art size +', pref.libraryThumbnailSize === 'auto' ? 'Album art size - (disable thumbnail auto-size)' : 'Album art size -', 'Flow mode', 'Always load preset with current \'view\' pattern'].forEach((v, i) => menu.newItem({ + if (libSet.albumArtOptionsShow) { + ['Covers [labels right]', 'Covers [labels bottom]', 'Covers [labels blend]', 'Artist photos [labels right]', grSet.libraryThumbnailSize === 'auto' ? 'Album art size + (disable thumbnail auto-size)' : 'Album art size +', grSet.libraryThumbnailSize === 'auto' ? 'Album art size - (disable thumbnail auto-size)' : 'Album art size -', 'Flow mode', 'Always load preset with current \'view\' pattern'].forEach((v, i) => libMenu.newItem({ menuName: 'Quick setup', str: v, - func: () => panel.set('quickSetup', i + 5), - flags: i == 4 && (ppt.thumbNailSize == 7 || !panel.imgView || ppt.albumArtFlowMode || pref.libraryThumbnailSize === 'auto') || i == 5 && (ppt.thumbNailSize == 0 || !panel.imgView || ppt.albumArtFlowMode || pref.libraryThumbnailSize === 'auto') ? MF_GRAYED_LIB : MF_STRING_LIB, - checkItem: i == 7 && ppt.presetLoadCurView, + func: () => lib.panel.set('quickSetup', i + 5), + flags: i == 4 && (libSet.thumbNailSize == 7 || !lib.panel.imgView || libSet.albumArtFlowMode || grSet.libraryThumbnailSize === 'auto') || i == 5 && (libSet.thumbNailSize == 0 || !lib.panel.imgView || libSet.albumArtFlowMode || grSet.libraryThumbnailSize === 'auto') ? LIB_MF_GRAYED : LIB_MF_STRING, + checkItem: i == 7 && libSet.presetLoadCurView, separator: i == 2 || i == 3 || i == 5 || i == 6 })); } // ! Source panel and playlist feature is not supported in a single panel/one library i.e in Georgia/Georgia-ReBORN - menu.newMenu({ menuName: 'Source', appendTo: mainMenu(), separator: true }); - ['Library'/*, 'Panel', 'Playlist' */].forEach((v, i) => menu.newItem({ + libMenu.newMenu({ menuName: 'Source', appendTo: mainMenu(), separator: true }); + ['Library'/*, 'Panel', 'Playlist' */].forEach((v, i) => libMenu.newItem({ menuName: 'Source', str: v, func: () => this.setSource(i), - checkRadio: i == (ppt.libSource - 1 < 0 || ppt.fixedPlaylist ? 2 : ppt.libSource - 1), + checkRadio: i == (libSet.libSource - 1 < 0 || libSet.fixedPlaylist ? 2 : libSet.libSource - 1), separator: i == 2 })); @@ -359,86 +386,86 @@ class MenuItems { // menuName: 'Source', // str: 'Select source panel', // func: () => this.setSourcePanel(), - // flags: ppt.libSource != 2 ? MF_GRAYED_LIB : MF_STRING_LIB, + // flags: libSet.libSource != 2 ? MF_GRAYED_LIB : MF_STRING_LIB, // separator: true // }); - menu.newMenu({ menuName: 'Playlist', appendTo: 'Source' }); - menu.newItem({ + libMenu.newMenu({ menuName: 'Playlist', appendTo: 'Source' }); + libMenu.newItem({ menuName: 'Playlist', str: 'Active playlist', func: () => { this.setActivePlaylist(); this.setSource(2) }, - checkRadio: ppt.libSource == 0, + checkRadio: libSet.libSource == 0, separator: true }); const pl_no = Math.ceil(this.pl.length / 30); - const pl_ix = ppt.fixedPlaylist ? plman.FindPlaylist(ppt.fixedPlaylistName) : -1; + const pl_ix = libSet.fixedPlaylist ? plman.FindPlaylist(libSet.fixedPlaylistName) : -1; for (let j = 0; j < pl_no; j++) { const n = `# ${j * 30 + 1} - ${Math.min(this.pl.length, 30 + j * 30)}${30 + j * 30 > pl_ix && ((j * 30) - 1) < pl_ix ? ' >>>' : ''}`; - menu.newMenu({ menuName: n, appendTo: 'Playlist' }); + libMenu.newMenu({ menuName: n, appendTo: 'Playlist' }); for (let i = j * 30; i < Math.min(this.pl.length, 30 + j * 30); i++) { - menu.newItem({ + libMenu.newItem({ menuName: n, str: this.pl[i].menuName, func: () => { this.setFixedPlaylist(i); this.setSource(2) }, - checkRadio: i == pl_ix && ppt.libSource != 0 + checkRadio: i == pl_ix && libSet.libSource != 0 }); } } - menu.newMenu({ menuName: 'Refresh', appendTo: mainMenu(), separator: true }); - for (let i = 0; i < 5; i++) { menu.newItem({ + libMenu.newMenu({ menuName: 'Refresh', appendTo: mainMenu(), separator: true }); + for (let i = 0; i < 5; i++) { libMenu.newItem({ menuName: 'Refresh', str: ['Refresh selected images...', 'Refresh all images...', 'Reset zoom...', 'Refresh library...', 'Reload...'][i], func: () => this.setMode(i), - flags: panel.imgView && !i && this.items.Count || !panel.imgView || i ? MF_STRING_LIB : MF_GRAYED_LIB, - separator: i == 1 && panel.imgView, - hide: i < 2 && !panel.imgView || i == 3 && ppt.libAutoSync + flags: lib.panel.imgView && !i && this.items.Count || !lib.panel.imgView || i ? LIB_MF_STRING : LIB_MF_GRAYED, + separator: i == 1 && lib.panel.imgView, + hide: i < 2 && !lib.panel.imgView || i == 3 && libSet.libAutoSync }); } - for (let i = 0; i < 2; i++) { menu.newItem({ + for (let i = 0; i < 2; i++) { libMenu.newItem({ menuName: mainMenu(), - str: [popUpBox.ok ? 'Options...' : 'Options: see console', 'Configure...'][i], - func: () => !i ? panel.open() : window.EditScript(), + str: [lib.popUpBox.ok ? 'Options...' : 'Options: see console', 'Configure...'][i], + func: () => !i ? lib.panel.open() : window.EditScript(), separator: !i && this.shift, - hide: !this.settingsBtnDn && ppt.settingsShow && this.validItem && !this.shift || i && !this.shift + hide: !this.settingsBtnDn && libSet.settingsShow && this.validItem && !this.shift || i && !this.shift }); } } filterMenu() { - fMenu.newMenu({}); - for (let i = 0; i < panel.filter.menu.length + 1; i++) { fMenu.newItem({ - str: i != panel.filter.menu.length ? (!i ? 'No filter' : panel.filter.menu[i]) : 'Auto-manage scroll', - func: () => panel.set('Filter', i), - checkItem: i == panel.filter.menu.length && !ppt.reset, - checkRadio: i == ppt.filterBy && i < panel.filter.menu.length, - separator: !i || i == panel.filter.menu.length - 1 || i == panel.filter.menu.length + libFMenu.newMenu({}); + for (let i = 0; i < lib.panel.filter.menu.length + 1; i++) { libFMenu.newItem({ + str: i != lib.panel.filter.menu.length ? (!i ? 'No filter' : lib.panel.filter.menu[i]) : 'Auto-manage scroll', + func: () => lib.panel.set('Filter', i), + checkItem: i == lib.panel.filter.menu.length && !libSet.reset, + checkRadio: i == libSet.filterBy && i < lib.panel.filter.menu.length, + separator: !i || i == lib.panel.filter.menu.length - 1 || i == lib.panel.filter.menu.length }); } - fMenu.newItem({ + libFMenu.newItem({ str: 'Configure filters...', - func: () => panel.open('filters') + func: () => lib.panel.open('filters') }); } searchHistoryMenu() { - sMenu.newMenu({}); - for (let i = 0; i < search.menu.length + 2; i++) { - sMenu.newItem({ - str: !i ? 'Query syntax help' : i < search.menu.length + 1 ? search.menu[i - 1].search : 'Clear history', + libSMenu.newMenu({}); + for (let i = 0; i < lib.search.menu.length + 2; i++) { + libSMenu.newItem({ + str: !i ? 'Query syntax help' : i < lib.search.menu.length + 1 ? lib.search.menu[i - 1].search : 'Clear history', func: () => this.setSearchHistory(i), - flags: i != 1 || search.menu.length ? MF_STRING_LIB : MF_GRAYED_LIB, - separator: !i || search.menu.length && i == search.menu.length + flags: i != 1 || lib.search.menu.length ? LIB_MF_STRING : LIB_MF_GRAYED, + separator: !i || lib.search.menu.length && i == lib.search.menu.length }); } } searchMenu() { - searchMenu.newMenu({}); - ['Copy', 'Cut', 'Paste'].forEach((v, i) => searchMenu.newItem({ + libSearchMenu.newMenu({}); + ['Copy', 'Cut', 'Paste'].forEach((v, i) => libSearchMenu.newItem({ str: v, func: () => this.setEdit(i), - flags: () => search.start == search.end && i < 2 || i == 2 && !search.paste ? MF_GRAYED_LIB : MF_STRING_LIB, + flags: () => lib.search.start == lib.search.end && i < 2 || i == 2 && !lib.search.paste ? LIB_MF_GRAYED : LIB_MF_STRING, separator: i == 1 })); } @@ -448,18 +475,18 @@ class MenuItems { const plnIsValid = pln != -1 && pln < plman.PlaylistCount; const plLockAdd = plnIsValid ? plman.GetPlaylistLockedActions(pln).includes('AddItems') : false; const plLockRemoveOrAdd = plnIsValid ? plman.GetPlaylistLockedActions(pln).includes('RemoveItems') || plman.GetPlaylistLockedActions(pln).includes('ReplaceItems') || plLockAdd : false; - return !i && !plLockRemoveOrAdd || i == 1 && !plLockAdd || i == 2 || i == 3 && pop.nowp != -1 ? MF_STRING_LIB : MF_GRAYED_LIB + return !i && !plLockRemoveOrAdd || i == 1 && !plLockAdd || i == 2 || i == 3 && lib.pop.nowp != -1 ? LIB_MF_STRING : LIB_MF_GRAYED } getSortData(d) { - d.name = panel.propNames[ppt.viewBy]; - d.sortAlbumsByYearAfter = ['', `[$nodisplay{$sub(${tf.date},0#)}]%album%`, `[$nodisplay{$sub(${tf.date},0)}]%album%[ '['$sub(${tf.date},0)']']`, `[$nodisplay{$sub(4001,${tf.date})}]%album%`, `[$nodisplay{$sub(4002,${tf.date})}]%album%[ '['$sub(${tf.date},0)']']`]; - d.sortAlbumsByYearBefore = ['', `[$nodisplay{$sub(${tf.date},0)}]%album%`, `$sub(${tf.date},0) - %album%`, `[$nodisplay{$sub(4003,${tf.date})}]%album%`, `[$nodisplay{$sub(4004,${tf.date})}]$sub(${tf.date},0) - %album%`]; - d.sortAlbumByYear = ppt.yearBeforeAlbum ? d.sortAlbumsByYearBefore : d.sortAlbumsByYearAfter; + d.name = lib.panel.propNames[libSet.viewBy]; + d.sortAlbumsByYearAfter = ['', `[$nodisplay{$sub(${grTF.date},0#)}]%album%`, `[$nodisplay{$sub(${grTF.date},0)}]%album%[ '['$sub(${grTF.date},0)']']`, `[$nodisplay{$sub(4001,${grTF.date})}]%album%`, `[$nodisplay{$sub(4002,${grTF.date})}]%album%[ '['$sub(${grTF.date},0)']']`]; + d.sortAlbumsByYearBefore = ['', `[$nodisplay{$sub(${grTF.date},0)}]%album%`, `$sub(${grTF.date},0) - %album%`, `[$nodisplay{$sub(4003,${grTF.date})}]%album%`, `[$nodisplay{$sub(4004,${grTF.date})}]$sub(${grTF.date},0) - %album%`]; + d.sortAlbumByYear = libSet.yearBeforeAlbum ? d.sortAlbumsByYearBefore : d.sortAlbumsByYearAfter; d.sortIX = -1; d.sortType = 0; d.sortYear = ['', '$if2($nodisplay{$sub(%date%,0)},$nodisplay{-4000})', '$nodisplay{$sub(4000,%date%)}']; - d.value = ppt.get(d.name) || ''; + d.value = libSet.get(d.name) || ''; d.valueLength = d.value.length; let l = d.sortYear.length; while (l-- && l) { @@ -496,28 +523,28 @@ class MenuItems { } loadView(clearCache, view, sel) { - ui.getColours(); - initLibraryColors(); - themeColorAdjustments(); - sbar.setCol(); - but.createImages(); - if (clearCache) img.clearCache(); + lib.ui.getColours(); + grm.theme.initLibraryColors(); + grm.theme.themeColorAdjustments(); + lib.sbar.setCol(); + lib.but.createImages(); + if (clearCache) libImg.clearCache(); if (sel !== undefined) { - const handle = sel >= panel.list.Count ? null : panel.list[sel]; - panel.set('view', view, true); + const handle = sel >= lib.panel.list.Count ? null : lib.panel.list[sel]; + lib.panel.set('view', view, true); if (handle) { - const item = panel.list.Find(handle); + const item = lib.panel.list.Find(handle); let idx = -1; - pop.tree.forEach((v, i) => { - if (pop.inRange(item, v.item)) idx = i; + lib.pop.tree.forEach((v, i) => { + if (lib.pop.inRange(item, v.item)) idx = i; }); if (idx != -1) { - if (!panel.imgView) pop.focusShow(idx); - else pop.showItem(idx, 'focus'); + if (!lib.panel.imgView) lib.pop.focusShow(idx); + else lib.pop.showItem(idx, 'focus'); } } - } else panel.set('view', view, true); - but.refresh(true); + } else lib.panel.set('view', view, true); + lib.but.refresh(true); } playlists_changed() { @@ -533,63 +560,63 @@ class MenuItems { this.r_up = true; this.expandable = false; this.items = new FbMetadbHandleList(); - this.ix = pop.get_ix(x, y, true, false); + this.ix = lib.pop.get_ix(x, y, true, false); this.nm = ''; this.settingsBtnDn = settingsBtnDn; - this.shift = vk.k('shift'); + this.shift = lib.vk.k('shift'); this.show_context = false; - let item = pop.tree[this.ix]; + let item = lib.pop.tree[this.ix]; let row = -1; - const level = pop.tree.length > this.ix && this.ix != -1 ? !pop.inlineRoot ? item.level : Math.max(item.level - 1, 0) : -1; + const level = lib.pop.tree.length > this.ix && this.ix != -1 ? !lib.pop.inlineRoot ? item.level : Math.max(item.level - 1, 0) : -1; - this.validItem = this.settingsBtnDn ? false : !panel.imgView ? y < panel.tree.y + pop.rows * sbar.row.h + ui.y && pop.tree.length > this.ix && this.ix != -1 && (x < Math.round(ppt.treeIndent * level) + ui.icon.w + ppt.margin + ui.x && (!item.track || item.root) || pop.check_ix(item, x, y, true)) : pop.tree.length > this.ix && this.ix != -1; + this.validItem = this.settingsBtnDn ? false : !lib.panel.imgView ? y < lib.panel.tree.y + lib.pop.rows * lib.sbar.row.h + lib.ui.y && lib.pop.tree.length > this.ix && this.ix != -1 && (x < Math.round(libSet.treeIndent * level) + lib.ui.icon.w + libSet.margin + lib.ui.x && (!item.track || item.root) || lib.pop.check_ix(item, x, y, true)) : lib.pop.tree.length > this.ix && this.ix != -1; - if (!this.validItem && !this.settingsBtnDn && ppt.settingsShow && y > ui.y + panel.search.sp) { - this.ix = pop.row.i != -1 ? pop.row.i : !panel.imgView ? pop.tree.length - 1 : -1; - if (this.ix < pop.tree.length && this.ix != -1) { - item = pop.tree[this.ix]; + if (!this.validItem && !this.settingsBtnDn && libSet.settingsShow && y > lib.ui.y + lib.panel.search.sp) { + this.ix = lib.pop.row.i != -1 ? lib.pop.row.i : !lib.panel.imgView ? lib.pop.tree.length - 1 : -1; + if (this.ix < lib.pop.tree.length && this.ix != -1) { + item = lib.pop.tree[this.ix]; this.validItem = true; } } if (this.validItem) { if (!item.sel) { - pop.clearSelected(); + lib.pop.clearSelected(); item.sel = true; } - pop.getTreeSel(); - this.expandable = !(pop.trackCount(pop.tree[this.ix].item) > this.treeExpandLimit || pop.tree[this.ix].track || panel.imgView); - if (this.expandable && pop.tree.length) { + lib.pop.getTreeSel(); + this.expandable = !(lib.pop.trackCount(lib.pop.tree[this.ix].item) > this.treeExpandLimit || lib.pop.tree[this.ix].track || lib.panel.imgView); + if (this.expandable && lib.pop.tree.length) { let count = 0; - pop.tree.forEach((v, m, arr) => { + lib.pop.tree.forEach((v, m, arr) => { if (m == this.ix || v.sel) { if (row == -1 || m < row) { row = m; this.nm = (v.level ? arr[v.par].srt[0] : '') + v.srt[0]; this.nm = this.nm.toUpperCase(); } - count += pop.trackCount(v.item); + count += lib.pop.trackCount(v.item); this.expandable = count <= this.treeExpandLimit; } }); } - this.items = pop.getHandleList(); + this.items = lib.pop.getHandleList(); this.show_context = true; - } else this.items = pop.getHandleList('newItems'); + } else this.items = lib.pop.getHandleList('newItems'); - menu.load(x, y); + libMenu.load(x, y); this.r_up = false; } setActivePlaylist() { - ppt.libSource = 0; - ppt.fixedPlaylist = false; - ppt.fixedPlaylistName = 'ActivePlaylist'; - if (panel.imgView) img.clearCache(); - lib.searchCache = {}; - if (ppt.showSource) panel.setRootName(); - lib.treeState(false, 2); + libSet.libSource = 0; + libSet.fixedPlaylist = false; + libSet.fixedPlaylistName = 'ActivePlaylist'; + if (lib.panel.imgView) libImg.clearCache(); + lib.lib.searchCache = {}; + if (libSet.showSource) lib.panel.setRootName(); + lib.lib.treeState(false, 2); } setAlbumart(i) { @@ -600,26 +627,26 @@ class MenuItems { case 2: case 3: case 4: - ppt.artId = i; + libSet.artId = i; break; case 5: case 6: case 7: - ppt.albumArtGrpLevel = i - 5; + libSet.albumArtGrpLevel = i - 5; break; case 8: { - const key = `${panel.grp[ppt.viewBy].type.trim()}${panel.lines}`; + const key = `${lib.panel.grp[libSet.viewBy].type.trim()}${lib.panel.lines}`; const ok_callback = (status, input) => { if (status != 'cancel') { - const albumArtGrpNames = $Lib.jsonParse(ppt.albumArtGrpNames, {}); + const albumArtGrpNames = $Lib.jsonParse(libSet.albumArtGrpNames, {}); albumArtGrpNames[key] = input; - ppt.albumArtGrpNames = JSON.stringify(albumArtGrpNames); + libSet.albumArtGrpNames = JSON.stringify(albumArtGrpNames); } } const caption = 'Change group name'; - const def = img.groupField; + const def = libImg.groupField; const prompt = 'Enter SINGULAR name, i.e. not plural\n\nName is pinned to VIEW PATTERN and GROUP LEVEL'; - const fallback = popUpBox.isHtmlDialogSupported() ? popUpBox.input(caption, prompt, ok_callback, '', def) : true; + const fallback = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.input(caption, prompt, ok_callback, '', def) : true; if (fallback) { let ns = ''; let status = 'ok'; @@ -633,49 +660,49 @@ class MenuItems { break; } case 9: - panel.open('albumArt'); + lib.panel.open('albumArt'); break; } - this.loadView(clearCache, ppt.albumArtViewBy); + this.loadView(clearCache, libSet.albumArtViewBy); } setEdit(i) { switch (i) { case 0: - search.on_char(vk.copy); + lib.search.on_char(lib.vk.copy); break; case 1: - search.on_char(vk.cut); + lib.search.on_char(lib.vk.cut); break; case 2: - search.on_char(vk.paste, true); + lib.search.on_char(lib.vk.paste, true); break; } } setFixedPlaylist(i) { - ppt.fixedPlaylistName = this.pl[i].name; - ppt.fixedPlaylist = true; - ppt.libSource = 1; - if (panel.imgView) img.clearCache(); - if (ppt.showSource) panel.setRootName(); - lib.searchCache = {}; - lib.treeState(false, 2); + libSet.fixedPlaylistName = this.pl[i].name; + libSet.fixedPlaylist = true; + libSet.libSource = 1; + if (lib.panel.imgView) libImg.clearCache(); + if (libSet.showSource) lib.panel.setRootName(); + lib.lib.searchCache = {}; + lib.lib.treeState(false, 2); } setMode(i) { switch (i) { case 0: - img.refresh(this.items); + libImg.refresh(this.items); break; case 1: - img.refresh('all'); + libImg.refresh('all'); break; case 2: - panel.zoomReset(); + lib.panel.zoomReset(); break; case 3: - lib.treeState(false, 2); + lib.lib.treeState(false, 2); break; case 4: window.Reload(); @@ -686,63 +713,65 @@ class MenuItems { setPlaylist(i) { switch (i) { // case 0: // The infamous 'Send to current playlist' func, deleting your entire playlist... >_< - // pop.load(pop.sel_items, true, false, pop.autoPlay.send, false, false); - // panel.treePaint(); - // lib.treeState(false, ppt.rememberTree); + // lib.pop.load(lib.pop.sel_items, true, false, lib.pop.autoPlay.send, false, false); + // lib.panel.treePaint(); + // lib.lib.treeState(false, libSet.rememberTree); // break; case 0: - pop.load(pop.sel_items, true, true, false, false, false); - lib.treeState(false, ppt.rememberTree); - if (pref.libraryPlaylistSwitch) { - btns.library.enabled = false; - btns.library.changeState(ButtonState.Default); - displayLibrary = false; - displayPlaylist = true; - if (!pref.playlistAutoScrollNowPlaying) playlist.on_size(ww, wh); + lib.pop.load(lib.pop.sel_items, true, true, false, false, false); + lib.lib.treeState(false, libSet.rememberTree); + if (grSet.addTracksPlaylistSwitch) { + grm.ui.btn.library.enabled = false; + grm.ui.btn.library.changeState(ButtonState.Default); + grm.ui.displayLibrary = false; + grm.ui.displayPlaylist = true; + if (!grSet.playlistAutoScrollNowPlaying) pl.call.on_size(grm.ui.ww, grm.ui.wh); + setTimeout(() => { pl.playlist.scrollbar.scroll_to_end(); }, 500); window.Repaint(); } break; case 1: - pop.sendToNewPlaylist(); - panel.treePaint(); - lib.treeState(false, ppt.rememberTree); - if (pref.libraryPlaylistSwitch) { - btns.library.enabled = false; - btns.library.changeState(ButtonState.Default); - displayLibrary = false; - displayPlaylist = true; + lib.pop.sendToNewPlaylist(); + lib.panel.treePaint(); + lib.lib.treeState(false, libSet.rememberTree); + if (grSet.addTracksPlaylistSwitch) { + grm.ui.btn.library.enabled = false; + grm.ui.btn.library.changeState(ButtonState.Default); + grm.ui.displayLibrary = false; + grm.ui.displayPlaylist = true; + setTimeout(() => { pl.playlist.scrollbar.scroll_to_end(); }, 100); window.Repaint(); } break; case 2: - pop.nowPlayingShow(); + lib.pop.nowPlayingShow(); break; case 3: { - lib.logTree(); - pop.clearTree(); - ppt.toggle('albumArtShow'); - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow; - this.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]); + lib.lib.logTree(); + lib.pop.clearTree(); + libSet.toggle('albumArtShow'); + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow; + this.loadView(false, !lib.panel.imgView ? (libSet.artTreeSameView ? libSet.viewBy : libSet.treeViewBy) : (libSet.artTreeSameView ? libSet.viewBy : libSet.albumArtViewBy), lib.pop.sel_items[0]); // Need continuous repaint when using style "Blend" and switching from normal to full width let blendedImgNeedsRepaint = true; - if (pref.styleBlend && blendedImgNeedsRepaint) { + if (grSet.styleBlend && blendedImgNeedsRepaint) { RepaintWindowRectAreas(); } - setLibrarySize(); - initLibraryColors(); - themeColorAdjustments(); - if (pref.libraryDesign === 'traditional') pop.createImages(); + grm.ui.setLibrarySize(); + grm.theme.initLibraryColors(); + grm.theme.themeColorAdjustments(); + if (grSet.libraryDesign === 'traditional') lib.pop.createImages(); blendedImgNeedsRepaint = false; window.Repaint(); break; } case 4: - lib.logTree(); - pop.clearTree(); - this.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]); + lib.lib.logTree(); + lib.pop.clearTree(); + this.loadView(false, !lib.panel.imgView ? (libSet.artTreeSameView ? libSet.viewBy : libSet.treeViewBy) : (libSet.artTreeSameView ? libSet.viewBy : libSet.albumArtViewBy), lib.pop.sel_items[0]); break; } } @@ -755,16 +784,16 @@ class MenuItems { $Lib.browser(`"${fn}`); break; } - case i < search.menu.length + 1: - panel.search.txt = search.menu[i - 1].search; - search.menu[i - 1].accessed = Date.now(); - search.focus(); - but.setSearchBtnsHide(); - lib.search(); + case i < lib.search.menu.length + 1: + lib.panel.search.txt = lib.search.menu[i - 1].search; + lib.search.menu[i - 1].accessed = Date.now(); + lib.search.focus(); + lib.but.setSearchBtnsHide(); + lib.lib.search(); break; - case i == search.menu.length + 1: - search.menu = []; - ppt.searchHistory = JSON.stringify([]); + case i == lib.search.menu.length + 1: + lib.search.menu = []; + libSet.searchHistory = JSON.stringify([]); break; } } @@ -772,42 +801,42 @@ class MenuItems { setSource(i) { switch (i) { case 0: - ppt.libSource = 1; - ppt.fixedPlaylist = false; + libSet.libSource = 1; + libSet.fixedPlaylist = false; break; case 1: - ppt.libSource = 2; - ppt.fixedPlaylist = false; - // if (ppt.panelSourceMsg && popUpBox.isHtmlDialogSupported()) popUpBox.message(); // Deactivated popup, let's not confuse the user since panel source is deactivated + libSet.libSource = 2; + libSet.fixedPlaylist = false; + // if (libSet.panelSourceMsg && lib.popUpBox.isHtmlDialogSupported()) lib.popUpBox.message(); // Deactivated popup, let's not confuse the user since panel source is deactivated break; case 2: { - const fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName); - if (fixedPlaylistIndex != -1) ppt.fixedPlaylist = true; - ppt.libSource = ppt.fixedPlaylist ? 1 : 0; - // if (ppt.panelSourceMsg && popUpBox.isHtmlDialogSupported()) popUpBox.message(); // Deactivated popup, let's not confuse the user since panel source is deactivated + const fixedPlaylistIndex = plman.FindPlaylist(libSet.fixedPlaylistName); + if (fixedPlaylistIndex != -1) libSet.fixedPlaylist = true; + libSet.libSource = libSet.fixedPlaylist ? 1 : 0; + // if (libSet.panelSourceMsg && lib.popUpBox.isHtmlDialogSupported()) lib.popUpBox.message(); // Deactivated popup, let's not confuse the user since panel source is deactivated break; } } - if (panel.imgView) img.clearCache(); - lib.searchCache = {}; - if (ppt.showSource) panel.setRootName(); - lib.treeState(false, 2); + if (lib.panel.imgView) libImg.clearCache(); + lib.lib.searchCache = {}; + if (libSet.showSource) lib.panel.setRootName(); + lib.lib.treeState(false, 2); - pref.librarySource = ppt.libSource; - pref.libraryFixedPlaylist = ppt.fixedPlaylist; - pref.libraryFixedPlaylistName = ppt.fixedPlaylistName; + grSet.librarySource = libSet.libSource; + grSet.libraryFixedPlaylist = libSet.fixedPlaylist; + grSet.libraryFixedPlaylistName = libSet.fixedPlaylistName; } setSourcePanel() { const ok_callback = (status, input) => { if (status != 'cancel') { - ppt.panelSelectionPlaylist = input; + libSet.panelSelectionPlaylist = input; } } const caption = 'Panel source name'; - const def = ppt.panelSelectionPlaylist; + const def = libSet.panelSelectionPlaylist; const prompt = 'Enter source panel name\n\n• To get the name, go to the library tree panel to be used as source\n• Press shift + windows key and choose configure\n• Paste the panel name or id at the top into here\n• Name is also used for a cache playlist that remembers last open state\n• Edit source panel name if required\n• For more than one source panel, use pipe separator, e.g. Genre|Artist' - const fallback = popUpBox.isHtmlDialogSupported() ? popUpBox.input(caption, prompt, ok_callback, '', def) : true; + const fallback = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.input(caption, prompt, ok_callback, '', def) : true; if (fallback) { let ns = ''; let status = 'ok'; @@ -822,67 +851,67 @@ class MenuItems { setStatistics(i) { if (i < 12) { - const curStatisticsShown = ppt.itemShowStatistics > 0; - ppt.itemShowStatistics = i; - ppt.itemShowStatisticsLast = ppt.itemShowStatistics; - pop.tree.forEach(v => { + const curStatisticsShown = libSet.itemShowStatistics > 0; + libSet.itemShowStatistics = i; + libSet.itemShowStatisticsLast = libSet.itemShowStatistics; + lib.pop.tree.forEach(v => { v.id = ''; v.count = ''; // has to reset parentheses if stats change off/on delete v.statistics; delete v._statistics; }); - pop.cache = { + lib.pop.cache = { standard: {}, search: {}, filter: {} } - pop.statisticsShow = ppt.itemShowStatistics; - pop.label = !ppt.labelStatistics || !pop.statisticsShow ? '' : pop.statistics[pop.statisticsShow]; - const statisticsShown = ppt.itemShowStatistics > 0; - if (panel.imgView && curStatisticsShown != statisticsShown) { - img.labels = { statistics: ppt.itemShowStatistics ? 1 : 0 } - img.clearCache(); - panel.set('view', ppt.viewBy); + lib.pop.statisticsShow = libSet.itemShowStatistics; + lib.pop.label = !libSet.labelStatistics || !lib.pop.statisticsShow ? '' : lib.pop.statistics[lib.pop.statisticsShow]; + const statisticsShown = libSet.itemShowStatistics > 0; + if (lib.panel.imgView && curStatisticsShown != statisticsShown) { + libImg.labels = { statistics: libSet.itemShowStatistics ? 1 : 0 } + libImg.clearCache(); + lib.panel.set('view', libSet.viewBy); } - panel.treePaint(); - } else panel.open('display'); + lib.panel.treePaint(); + } else lib.panel.open('display'); } setTreeState(i) { switch (i) { case 0: - pop.collapseAll(); + lib.pop.collapseAll(); break; case 1: - pop.expand(this.ix, this.nm); - panel.setHeight(true); + lib.pop.expand(this.ix, this.nm); + lib.panel.setHeight(true); break; } - pop.checkAutoHeight(); + lib.pop.checkAutoHeight(); } setView(i) { - if (i < panel.menu.length) { - if (ppt.artTreeSameView) { - ppt.treeViewBy = i; - ppt.albumArtViewBy = i; + if (i < lib.panel.menu.length) { + if (libSet.artTreeSameView) { + libSet.treeViewBy = i; + libSet.albumArtViewBy = i; } else { - if (!panel.imgView) ppt.treeViewBy = i; - else ppt.albumArtViewBy = i; - if (ppt.treeViewBy != ppt.albumArtViewBy) { - ppt.set(panel.imgView ? 'Panel Library - Tree' : 'Panel Library - Tree Image', null); - ppt.set(panel.imgView ? 'Panel Library - Tree Search' : 'Panel Library - Tree Image Search', null); + if (!lib.panel.imgView) libSet.treeViewBy = i; + else libSet.albumArtViewBy = i; + if (libSet.treeViewBy != libSet.albumArtViewBy) { + libSet.set(lib.panel.imgView ? 'Panel Library - Tree' : 'Panel Library - Tree Image', null); + libSet.set(lib.panel.imgView ? 'Panel Library - Tree Search' : 'Panel Library - Tree Image Search', null); } } - panel.set('view', i); + lib.panel.set('view', i); } } sortByDate(i, d) { let sortByIX = -1; if (i > 4) { - ppt.toggle('yearBeforeAlbum'); - d.sortAlbumByYear = ppt.yearBeforeAlbum ? d.sortAlbumsByYearBefore : d.sortAlbumsByYearAfter; + libSet.toggle('yearBeforeAlbum'); + d.sortAlbumByYear = libSet.yearBeforeAlbum ? d.sortAlbumsByYearBefore : d.sortAlbumsByYearAfter; sortByIX = d.sortIX; } else sortByIX = i; if (d.sortType == 1) { @@ -899,38 +928,38 @@ class MenuItems { } if (d.sortType == 1 || sortByIX != -1) { const expanded = []; - const ix = pop.get_ix(!panel.imgView ? 0 : img.panel.x + 1, (!panel.imgView || img.style.vertical ? panel.tree.y : panel.tree.x) + sbar.row.h / 2, true, false); - const curName = ix != -1 ? pop.tree[ix].name : ''; - const scrollPos = sbar.scroll; + const ix = lib.pop.get_ix(!lib.panel.imgView ? 0 : libImg.panel.x + 1, (!lib.panel.imgView || libImg.style.vertical ? lib.panel.tree.y : lib.panel.tree.x) + lib.sbar.row.h / 2, true, false); + const curName = ix != -1 ? lib.pop.tree[ix].name : ''; + const scrollPos = lib.sbar.scroll; const selected = []; - pop.tree.forEach((v, i) => { - const level = !ppt.rootNode ? v.level : v.level - 1; // 1 level memory: more is less reliable + lib.pop.tree.forEach((v, i) => { + const level = !libSet.rootNode ? v.level : v.level - 1; // 1 level memory: more is less reliable if (!level) { if (v.child.length) expanded.push(i); if (v.sel) selected.push(i); } }); - ppt.set(d.name, d.value); - pop.clearTree(); - panel.getViews(); - this.setView(ppt.viewBy); - if (ix < pop.tree.length) { - const name = ix != -1 ? pop.tree[ix].name : ''; + libSet.set(d.name, d.value); + lib.pop.clearTree(); + lib.panel.getViews(); + this.setView(libSet.viewBy); + if (ix < lib.pop.tree.length) { + const name = ix != -1 ? lib.pop.tree[ix].name : ''; if (name && name == curName) { expanded.forEach(v => { - if (v < pop.tree.length) { - const item = pop.tree[v]; - pop.branch(item, !!item.root, true); + if (v < lib.pop.tree.length) { + const item = lib.pop.tree[v]; + lib.pop.branch(item, !!item.root, true); } }); selected.forEach(v => { - if (v < pop.tree.length) { - pop.tree[v].sel = true; + if (v < lib.pop.tree.length) { + lib.pop.tree[v].sel = true; } }); } } - sbar.checkScroll(scrollPos, 'full', true); + lib.sbar.checkScroll(scrollPos, 'full', true); } } } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-panel.js b/profile/georgia-reborn/scripts/Library/scripts/lib-panel.js index cf104de5..93b01966 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-panel.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-panel.js @@ -1,6 +1,6 @@ 'use strict'; -class Panel { +class LibPanel { constructor() { const DT_CENTER = 0x00000001; const DT_RIGHT = 0x00000002; @@ -21,15 +21,15 @@ class Panel { this.folder_view = 10; this.folderView = false; this.grp = []; - this.imgView = ppt.albumArtShow; + this.imgView = libSet.albumArtShow; this.init = true; this.l = DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; this.lc = DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS; - this.lines = ppt.albumArtGrpLevel ? ppt.albumArtGrpLevel : [2, 2, 2, 1, 1][ppt.artId]; + this.lines = libSet.albumArtGrpLevel ? libSet.albumArtGrpLevel : [2, 2, 2, 1, 1][libSet.artId]; this.list = new FbMetadbHandleList(); this.menu = []; - this.paint_y = Math.floor(ui.style.topBarShow || !ppt.sbarShow ? ui.row.h * 1.2 : 0); - this.pn_h_auto = ppt.pn_h_auto && ppt.rootNode; + this.paint_y = Math.floor(lib.ui.style.topBarShow || !libSet.sbarShow ? lib.ui.row.h * 1.2 : 0); + this.pn_h_auto = libSet.pn_h_auto && libSet.rootNode; this.propNames = []; this.newView = true; this.pos = -1; @@ -44,8 +44,8 @@ class Panel { this.sourceName = ''; this.statistics = false; this.viewName = ''; - this.zoomFilter = Math.max(ppt.zoomFilter / 100, 0.7); - ppt.zoomFilter = this.zoomFilter * 100; + this.zoomFilter = Math.max(libSet.zoomFilter / 100, 0.7); + libSet.zoomFilter = this.zoomFilter * 100; this.filter = { menu: [], @@ -93,7 +93,7 @@ class Panel { y: 0 }; - ppt.get('Panel Library - Library Tree Dialog Box', JSON.stringify({ + libSet.get('Panel Library - Library Tree Dialog Box', JSON.stringify({ w: 85, h: 60, def_w: 85, @@ -103,64 +103,64 @@ class Panel { })); if (this.pn_h_auto) { - window.MaxHeight = window.MinHeight = ppt.pn_h; + window.MaxHeight = window.MinHeight = libSet.pn_h; } this.setTopBar(); this.getViews(); this.getFilters(); - ppt.initialLoadFilters = false; - ppt.initialLoadViews = false; - this.getFields(ppt.viewBy, ppt.filterBy); + libSet.initialLoadFilters = false; + libSet.initialLoadViews = false; + this.getFields(libSet.viewBy, libSet.filterBy); } // * METHODS * // calcText() { - ui.style.topBarShow = ppt.filterShow || ppt.searchShow || ppt.settingsShow; - if (!ui.style.topBarShow) return; + lib.ui.style.topBarShow = libSet.filterShow || libSet.searchShow || libSet.settingsShow; + if (!lib.ui.style.topBarShow) return; $Lib.gr(1, 1, false, g => { - this.filter.w = ppt.filterShow ? g.CalcTextWidth(this.filter.mode[ppt.filterBy].name, this.filter.font) + (ppt.searchShow ? Math.max(ppt.margin * 2 + (!ppt.settingsBtnStyle ? 2 : 0), 12) : 0) : 0; - this.settings.w = ppt.settingsShow ? Math.round(g.MeasureString(this.settings.icon, this.settings.font, 0, 0, 500, 500).Width) : 0; + this.filter.w = libSet.filterShow ? g.CalcTextWidth(this.filter.mode[libSet.filterBy].name, this.filter.font) + (libSet.searchShow ? Math.max(libSet.margin * 2 + (!libSet.settingsBtnStyle ? 2 : 0), 12) : 0) : 0; + this.settings.w = libSet.settingsShow ? Math.round(g.MeasureString(this.settings.icon, this.settings.font, 0, 0, 500, 500).Width) : 0; }); switch (true) { - case ppt.settingsShow && ppt.searchShow: - this.filter.x = ui.x + ui.w - ui.sz.marginSearch - this.filter.w - this.settings.w + this.settings.offset; + case libSet.settingsShow && libSet.searchShow: + this.filter.x = lib.ui.x + lib.ui.w - lib.ui.sz.marginSearch - this.filter.w - this.settings.w + this.settings.offset; break; - case !ppt.searchShow: - this.filter.x = ui.x + ui.sz.marginSearch; + case !libSet.searchShow: + this.filter.x = lib.ui.x + lib.ui.sz.marginSearch; break; - case !ppt.settingsShow: - this.filter.x = ui.x + ui.w - ui.sz.marginSearch - this.filter.w; + case !libSet.settingsShow: + this.filter.x = lib.ui.x + lib.ui.w - lib.ui.sz.marginSearch - this.filter.w; break; - case !ppt.filterShow: - this.filter.x = ui.x + ui.w - ui.sz.marginSearch * 2 - this.settings.w + this.settings.offset; + case !libSet.filterShow: + this.filter.x = lib.ui.x + lib.ui.w - lib.ui.sz.marginSearch * 2 - this.settings.w + this.settings.offset; break; } - this.search.x = Math.round(ui.sz.marginSearch + ui.row.h); - this.search.w = ppt.searchShow && (ppt.filterShow || ppt.settingsShow) ? this.filter.x - this.search.x - 11 : ui.w - ui.sz.marginSearch - Math.round(ui.row.h * 0.75) - this.search.x + 1; + this.search.x = Math.round(lib.ui.sz.marginSearch + lib.ui.row.h); + this.search.w = libSet.searchShow && (libSet.filterShow || libSet.settingsShow) ? this.filter.x - this.search.x - 11 : lib.ui.w - lib.ui.sz.marginSearch - Math.round(lib.ui.row.h * 0.75) - this.search.x + 1; } clear(type) { if (type == 'views' || type == 'both') { for (let i = 0; i < 100; i++) { - ppt.set(`Panel Library - View ${$Lib.padNumber(i, 2)}: Name // Pattern`, null); + libSet.set(`Panel Library - View ${$Lib.padNumber(i, 2)}: Name // Pattern`, null); } } if (type == 'filters' || type == 'both') { - for (let i = 0; i < 100; i++) ppt.set(`Panel Library - Filter ${$Lib.padNumber(i, 2)}: Name // Query`, null); + for (let i = 0; i < 100; i++) libSet.set(`Panel Library - Filter ${$Lib.padNumber(i, 2)}: Name // Query`, null); } } forcePaint() { - window.RepaintRect(ui.x, ui.y, ui.w, ui.h, true); + window.RepaintRect(lib.ui.x, lib.ui.y, lib.ui.w, lib.ui.h, true); } getFields(view, filter, grpsOnly) { - this.newView = ppt.viewBy != view; - ppt.filterBy = filter; - ppt.viewBy = view; - const prefix = ppt.prefix.split('|'); + this.newView = libSet.viewBy != view; + libSet.filterBy = filter; + libSet.viewBy = view; + const prefix = libSet.prefix.split('|'); let grps = []; let ix1 = -1; let ix2 = -1; @@ -219,25 +219,25 @@ class Panel { this.grp = this.grp.filter(removeEmpty); this.filter.mode = this.filter.mode.filter(removeEmpty); this.folder_view = this.grp.length - 1; - ppt.filterBy = Math.min(ppt.filterBy, this.filter.mode.length - 1); - ppt.viewBy = Math.min(ppt.viewBy, this.grp.length - 1); - this.folderView = ppt.viewBy == this.folder_view; + libSet.filterBy = Math.min(libSet.filterBy, this.filter.mode.length - 1); + libSet.viewBy = Math.min(libSet.viewBy, this.grp.length - 1); + this.folderView = libSet.viewBy == this.folder_view; if (grpsOnly) return; - this.colMarker = this.grp[ppt.viewBy].type.includes('$colour{'); + this.colMarker = this.grp[libSet.viewBy].type.includes('$colour{'); let valid = false; - if (ui.img.blurDark && ppt.text_hUse) { - const c = ppt.text_h.replace(/[^0-9.,-]/g, '').split(/[,-]/); + if (lib.ui.img.blurDark && libSet.text_hUse) { + const c = libSet.text_h.replace(/[^0-9.,-]/g, '').split(/[,-]/); if (c.length == 3 || c.length == 4) valid = true; } - this.textDiffHighlight = ui.img.blurDark && !ppt.highLightRow && !(ppt.text_hUse && valid) && ppt.highLightText && !this.colMarker; + this.textDiffHighlight = lib.ui.img.blurDark && !libSet.highLightRow && !(libSet.text_hUse && valid) && libSet.highLightText && !this.colMarker; if (this.folderView) { this.samePattern = !this.newView && !this.init; } else { - this.sortBy = this.view = this.grp[ppt.viewBy].type; + this.sortBy = this.view = this.grp[libSet.viewBy].type; this.samePattern = !this.colMarker && this.curPattern == this.view; } this.curPattern = this.view; - this.lines = ppt.albumArtGrpLevel ? ppt.albumArtGrpLevel : [2, 2, 2, 1, 1][ppt.artId]; + this.lines = libSet.albumArtGrpLevel ? libSet.albumArtGrpLevel : [2, 2, 2, 1, 1][libSet.artId]; if (!this.folderView) { this.statistics = /play(_|)count|auto(_|)rating/.test(this.view); @@ -314,12 +314,12 @@ class Panel { colView.forEach((v, i, arr) => { if (i % 2 === 1) { const colSplit = v.split(','); - arr[i] = `@!#${ui.setMarkerCol(colSplit[0]) || (!ppt.albumArtShow || ppt.albumArtLabelType != 4 ? ui.col.text : RGB(240, 240, 240))}\`${ui.setMarkerCol(colSplit[1]) || (ppt.highLightText ? ui.col.text_h : (!ppt.albumArtShow || ppt.albumArtLabelType != 4 ? ui.col.text : RGB(240, 240, 240)))}\`${ui.setMarkerCol(colSplit[2]) || (!ppt.albumArtShow || ppt.albumArtLabelType != 4 ? ui.col.textSel : ui.col.text)}@!#`; + arr[i] = `@!#${lib.ui.setMarkerCol(colSplit[0]) || (!libSet.albumArtShow || libSet.albumArtLabelType != 4 ? lib.ui.col.text : RGB(240, 240, 240))}\`${lib.ui.setMarkerCol(colSplit[1]) || (libSet.highLightText ? lib.ui.col.text_h : (!libSet.albumArtShow || libSet.albumArtLabelType != 4 ? lib.ui.col.text : RGB(240, 240, 240)))}\`${lib.ui.setMarkerCol(colSplit[2]) || (!libSet.albumArtShow || libSet.albumArtLabelType != 4 ? lib.ui.col.textSel : lib.ui.col.text)}@!#`; } }); this.view = colView.join(''); } - if (ui.col.counts) this.colMarker = true; + if (lib.ui.col.counts) this.colMarker = true; if (this.colMarker) this.sortBy = this.sortBy.replace(/\$colour{.*?}/g, ''); while (this.sortBy.includes('$nodisplay{')) { ix1 = this.sortBy.indexOf('$nodisplay{'); @@ -329,8 +329,8 @@ class Panel { } this.sortBy = this.sortBy.replace(RegExp(this.splitter, 'g'), ' '); } - this.pn_h_auto = ppt.pn_h_auto && ppt.rootNode; - if (this.pn_h_auto) window.MaxHeight = window.MinHeight = ppt.pn_h; + this.pn_h_auto = libSet.pn_h_auto && libSet.rootNode; + if (this.pn_h_auto) window.MaxHeight = window.MinHeight = libSet.pn_h; else { window.MaxHeight = 2147483647; window.MinHeight = 0; @@ -342,7 +342,7 @@ class Panel { getFilterIndex(arr, name, type) { const findFilterIndex = arr.findIndex(v => v.name === name && v.type === type); - if (findFilterIndex != -1) ppt.filterBy = findFilterIndex; + if (findFilterIndex != -1) libSet.filterBy = findFilterIndex; return findFilterIndex; } @@ -375,12 +375,12 @@ class Panel { let pptNo = 0; for (let i = 0; i < pt.length; i++) { const v = pt[i]; - const prop = ppt.initialLoadFilters ? ppt.get(v[0], v[1]) : ppt.get(v[0]); + const prop = libSet.initialLoadFilters ? libSet.get(v[0], v[1]) : libSet.get(v[0]); if (!i) { const defValid = prop && prop.endsWith('// Button Name'); dialogFilters.push(defValid ? prop : 'Panel Library - Filter // Button Name'); this.filter_ppt.push(defValid ? prop : 'Panel Library - Filter // Button Name'); - if (!defValid) ppt.set(v[0], v[1]); + if (!defValid) libSet.set(v[0], v[1]); pptNo++; } else { @@ -396,7 +396,7 @@ class Panel { let nm = ''; for (let i = pptNo + 1; i < 100; i++) { - nm = ppt.get(`Panel Library - Filter ${$Lib.padNumber(i, 2)}: Name // Query`); + nm = libSet.get(`Panel Library - Filter ${$Lib.padNumber(i, 2)}: Name // Query`); if (nm) { if (nm.includes('//') || nm.includes('/hide/')) dialogFilters.push(nm); if (nm.includes('//')) this.filter_ppt.push(nm); @@ -407,7 +407,7 @@ class Panel { this.propNames = []; for (let i = 1; i < 100; i++) { const propName = `Panel Library - View ${$Lib.padNumber(i, 2)}: Name // Pattern`; - nm = ppt.get(propName); + nm = libSet.get(propName); if (nm && nm.includes('//')) { this.propNames.push(propName); } @@ -439,7 +439,7 @@ class Panel { getViewIndex(arr, name, type) { const findViewIndex = arr.findIndex(v => v.name.trim() === name && v.type.trimStart() === type); - if (findViewIndex != -1) ppt.viewBy = findViewIndex; + if (findViewIndex != -1) libSet.viewBy = findViewIndex; return findViewIndex; } @@ -472,7 +472,7 @@ class Panel { const names1 = ['Artist', 'Album Artist', 'Album', 'Album', 'Genre', 'Year']; const names2 = ['Album', 'Album', 'Track', 'Track', 'Album', 'Album']; - const albumArtGrpNames = $Lib.jsonParse(ppt.albumArtGrpNames, {}); + const albumArtGrpNames = $Lib.jsonParse(libSet.albumArtGrpNames, {}); this.defaultViews.forEach((v, i) => { if (!albumArtGrpNames[`${v}1`]) albumArtGrpNames[`${v}1`] = names1[i]; if (!albumArtGrpNames[`${v}2`]) albumArtGrpNames[`${v}2`] = names2[i]; @@ -483,12 +483,12 @@ class Panel { let pptNo = 0; for (let i = 0; i < pt.length; i++) { const v = pt[i]; - const prop = ppt.initialLoadViews ? ppt.get(v[0], v[1]) : ppt.get(v[0]); + const prop = libSet.initialLoadViews ? libSet.get(v[0], v[1]) : libSet.get(v[0]); if (!i) { const defValid = prop && prop.endsWith('// Pattern Not Configurable'); dialogViews.push(defValid ? prop : 'Panel Library - View by Folder Structure // Pattern Not Configurable'); this.view_ppt.push(defValid ? prop : 'Panel Library - View by Folder Structure // Pattern Not Configurable'); - if (!defValid) ppt.set(v[0], v[1]); + if (!defValid) libSet.set(v[0], v[1]); pptNo++; } else if (prop) { @@ -501,7 +501,7 @@ class Panel { pt = undefined; let nm = 0; for (let i = pptNo + 1; i < 100; i++) { - nm = ppt.get(`Panel Library - View ${$Lib.padNumber(i, 2)}: Name // Pattern`); + nm = libSet.get(`Panel Library - View ${$Lib.padNumber(i, 2)}: Name // Pattern`); if (nm) { if (nm.includes('//') || nm.includes('/hide/')) dialogViews.push(nm); if (nm.includes('//')) this.view_ppt.push(nm); @@ -542,71 +542,71 @@ class Panel { if (!keysPresent.includes(`${v}2`)) delete albumArtGrpNames[`${v}2`]; }); } - ppt.albumArtGrpNames = JSON.stringify(albumArtGrpNames); + libSet.albumArtGrpNames = JSON.stringify(albumArtGrpNames); } load() { - ppt.nodeLines = true; - ppt.nodeCounts = 1; - ppt.sbarButType = 0; - ppt.searchShow = true; - ppt.filterShow = true; - ppt.settingsShow = true; - if (libraryCanReload) window.Reload(); + libSet.nodeLines = true; + libSet.nodeCounts = 1; + libSet.sbarButType = 0; + libSet.searchShow = true; + libSet.filterShow = true; + libSet.settingsShow = true; + if (grm.ui.libraryCanReload) window.Reload(); } on_size(fontChanged) { - const ln_sp = ui.style.topBarShow && !ui.id.local ? Math.floor(ui.row.h * 0.1) : 0; - const sbarStyle = !ppt.sbarFullHeight ? 2 : 0; + const ln_sp = lib.ui.style.topBarShow && !lib.ui.id.local ? Math.floor(lib.ui.row.h * 0.1) : 0; + const sbarStyle = !libSet.sbarFullHeight ? 2 : 0; this.calcText(); - this.ln.x = ppt.countsRight || ppt.itemShowStatistics || ppt.rowStripes || ppt.fullLineSelection || pop.inlineRoot ? 0 : ui.sz.marginSearch; - this.ln.w = ui.w - this.ln.x - 1; - this.search.h = ui.style.topBarShow ? ui.row.h + (!ui.id.local ? ln_sp * 2 + ui.sz.margin + SCALE(7) : 0) : ppt.marginTopBottom; + this.ln.x = libSet.countsRight || libSet.itemShowStatistics || libSet.rowStripes || libSet.fullLineSelection || lib.pop.inlineRoot ? 0 : lib.ui.sz.marginSearch; + this.ln.w = lib.ui.w - this.ln.x - 1; + this.search.h = lib.ui.style.topBarShow ? lib.ui.row.h + (!lib.ui.id.local ? ln_sp * 2 + lib.ui.sz.margin + SCALE(7) : 0) : libSet.marginTopBottom; this.search.sp = this.search.h - ln_sp; - let sp = ui.h - this.search.h - (ui.style.topBarShow ? ui.sz.margin / 2 : ppt.marginTopBottom); - this.rows = sp / ui.row.h; + let sp = lib.ui.h - this.search.h - (lib.ui.style.topBarShow ? lib.ui.sz.margin / 2 : libSet.marginTopBottom); + this.rows = sp / lib.ui.row.h; this.rows = Math.floor(this.rows); - sp = ui.row.h * this.rows; - this.node_y = Math.round((ui.row.h - ui.sz.node) / 1.75); - this.filter.y = ui.y + sp + this.search.h - ui.row.h * 0.9; + sp = lib.ui.row.h * this.rows; + this.node_y = Math.round((lib.ui.row.h - lib.ui.sz.node) / 1.75); + this.filter.y = lib.ui.y + sp + this.search.h - lib.ui.row.h * 0.9; if (this.init || fontChanged || !this.tree.y) this.tree.y = this.search.h; - this.paint_y = Math.floor(ui.style.topBarShow || !ppt.sbarShow ? this.search.h : 0); + this.paint_y = Math.floor(lib.ui.style.topBarShow || !libSet.sbarShow ? this.search.h : 0); - const sbar_top = !ui.sbar.type ? 5 : ui.style.topBarShow ? 3 : 0; - const sbar_bot = !ui.sbar.type ? 5 : 0; - this.sbar_o = [ui.sbar.arrowPad, Math.max(Math.floor(ui.sbar.but_w * 0.2), 2) + ui.sbar.arrowPad * 2, 0][ui.sbar.type]; - const vertical = !ppt.albumArtFlowMode || ui.h - this.search.h > ui.w - ui.sbar.w; + const sbar_top = !lib.ui.sbar.type ? 5 : lib.ui.style.topBarShow ? 3 : 0; + const sbar_bot = !lib.ui.sbar.type ? 5 : 0; + this.sbar_o = [lib.ui.sbar.arrowPad, Math.max(Math.floor(lib.ui.sbar.but_w * 0.2), 2) + lib.ui.sbar.arrowPad * 2, 0][lib.ui.sbar.type]; + const vertical = !libSet.albumArtFlowMode || lib.ui.h - this.search.h > lib.ui.w - lib.ui.sbar.w; switch (true) { case !this.imgView || vertical: { - this.sbar_x = ui.x + ui.w - ui.sbar.sp - (RES_4K ? 48 : 18); - const top_corr = [this.sbar_o - (ui.sbar.but_h - ui.sbar.but_w) / 2, this.sbar_o, 0][ui.sbar.type]; - const bot_corr = [(ui.sbar.but_h - ui.sbar.but_w) / 2 - this.sbar_o, -this.sbar_o, 0][ui.sbar.type]; - let sbar_y = ui.y + (ui.sbar.type < sbarStyle || ui.style.topBarShow ? this.search.sp + 1 : 0) + sbar_top + top_corr; - let sbar_h = ui.sbar.type < sbarStyle ? sp + 1 - sbar_top - sbar_bot + bot_corr * 2 - ui.sz.margin : ui.y + ui.h - sbar_y - sbar_bot + bot_corr - ui.sz.margin; - if (ui.sbar.type == 2) { + this.sbar_x = lib.ui.x + lib.ui.w - lib.ui.sbar.sp - (RES._4K ? 48 : 18); + const top_corr = [this.sbar_o - (lib.ui.sbar.but_h - lib.ui.sbar.but_w) / 2, this.sbar_o, 0][lib.ui.sbar.type]; + const bot_corr = [(lib.ui.sbar.but_h - lib.ui.sbar.but_w) / 2 - this.sbar_o, -this.sbar_o, 0][lib.ui.sbar.type]; + let sbar_y = lib.ui.y + (lib.ui.sbar.type < sbarStyle || lib.ui.style.topBarShow ? this.search.sp + 1 : 0) + sbar_top + top_corr; + let sbar_h = lib.ui.sbar.type < sbarStyle ? sp + 1 - sbar_top - sbar_bot + bot_corr * 2 - lib.ui.sz.margin : lib.ui.y + lib.ui.h - sbar_y - sbar_bot + bot_corr - lib.ui.sz.margin; + if (lib.ui.sbar.type == 2) { sbar_y += 1; sbar_h -= 2; } - sbar.metrics(this.sbar_x, sbar_y, ui.sbar.w, sbar_h, this.rows, ui.row.h, !this.imgView ? true : vertical); - if (this.imgView) img.metrics(); + lib.sbar.metrics(this.sbar_x, sbar_y, lib.ui.sbar.w, sbar_h, this.rows, lib.ui.row.h, !this.imgView ? true : vertical); + if (this.imgView) libImg.metrics(); break; } case !vertical: { - this.sbar_y = ui.y + ui.h - ui.sbar.sp; - let sbar_x = ui.x; - let sbar_w = ui.w; - if (ui.sbar.type == 2) { + this.sbar_y = lib.ui.y + lib.ui.h - lib.ui.sbar.sp; + let sbar_x = lib.ui.x; + let sbar_w = lib.ui.w; + if (lib.ui.sbar.type == 2) { sbar_x += 1; sbar_w -= 2; } - sbar.metrics(sbar_x, this.sbar_y, sbar_w, ui.sbar.w, this.rows, ui.row.h, !this.imgView); - if (this.imgView) img.metrics(); + lib.sbar.metrics(sbar_x, this.sbar_y, sbar_w, lib.ui.sbar.w, this.rows, lib.ui.row.h, !this.imgView); + if (this.imgView) libImg.metrics(); break; } } if (this.imgView) { - if (this.init) img.sizeDebounce(); - else if (sbar.scroll > sbar.max_scroll) sbar.checkScroll(sbar.max_scroll); + if (this.init) libImg.sizeDebounce(); + else if (lib.sbar.scroll > lib.sbar.max_scroll) lib.sbar.checkScroll(lib.sbar.max_scroll); } } @@ -621,7 +621,7 @@ class Panel { } cfg[0].forEach((v, i) => { const nm = v.type ? v.name + (v.menu ? ' // ' : ' /hide/ ') + v.type : null; - ppt.set(v.type != 'Pattern Not Configurable' ? `Panel Library - View ${$Lib.padNumber(i + 2, 2)}: Name // Pattern` : 'Panel Library - View 01: Name // Pattern', nm); + libSet.set(v.type != 'Pattern Not Configurable' ? `Panel Library - View ${$Lib.padNumber(i + 2, 2)}: Name // Pattern` : 'Panel Library - View 01: Name // Pattern', nm); }); i = cfg[1].length; while (i--) { @@ -629,46 +629,46 @@ class Panel { } cfg[1].forEach((v, i) => { const nm = v.type ? v.name + (v.menu ? ' // ' : ' /hide/ ') + v.type : null; - ppt.set(v.type != 'Button Name' ? `Panel Library - Filter ${$Lib.padNumber(i + 2, 2)}: Name // Query` : 'Panel Library - Filter 01: Name // Query', nm); + libSet.set(v.type != 'Button Name' ? `Panel Library - Filter ${$Lib.padNumber(i + 2, 2)}: Name // Query` : 'Panel Library - Filter 01: Name // Query', nm); }); - const view_name = this.grp[ppt.viewBy].name; - const view_type = this.grp[ppt.viewBy].type.trimStart(); - const filter_name = this.filter.mode[ppt.filterBy].name; - const filter_type = this.filter.mode[ppt.filterBy].type; + const view_name = this.grp[libSet.viewBy].name; + const view_type = this.grp[libSet.viewBy].type.trimStart(); + const filter_name = this.filter.mode[libSet.filterBy].name; + const filter_type = this.filter.mode[libSet.filterBy].type; this.getViews(); this.getFilters(); - this.getFields(ppt.viewBy, ppt.filterBy, true); + this.getFields(libSet.viewBy, libSet.filterBy, true); if (this.getViewIndex(this.grp, view_name, view_type) == -1 || this.getFilterIndex(this.filter.mode, filter_name, filter_type) == -1) { - lib.logTree(); + lib.lib.logTree(); window.Reload(); - } else this.getFields(ppt.viewBy, ppt.filterBy); + } else this.getFields(libSet.viewBy, libSet.filterBy); } if (new_ppt) this.updateProp($Lib.jsonParse(new_ppt, {}), 'value'); - if (new_cfgWindow) ppt.set('Panel Library - Library Tree Dialog Box', new_cfgWindow); + if (new_cfgWindow) libSet.set('Panel Library - Library Tree Dialog Box', new_cfgWindow); if (type == 'reset') { - this.updateProp(ppt, 'default_value'); + this.updateProp(libSet, 'default_value'); } }; this.getViews(); - let cfgWindow = ppt.get('Panel Library - Library Tree Dialog Box'); + let cfgWindow = libSet.get('Panel Library - Library Tree Dialog Box'); cfgWindow = $Lib.jsonParse(cfgWindow); if (page !== undefined) cfgWindow.page = page; cfgWindow.version = `v${window.ScriptInfo.Version}`; cfgWindow = JSON.stringify(cfgWindow); - ppt.set('Panel Library - Library Tree Dialog Box', cfgWindow); - if (popUpBox.isHtmlDialogSupported()) popUpBox.config(JSON.stringify([this.dialogGrps, this.dialogFiltGrps, this.defViewPatterns, this.defFilterPatterns]), JSON.stringify(ppt), cfgWindow, ok_callback); + libSet.set('Panel Library - Library Tree Dialog Box', cfgWindow); + if (lib.popUpBox.isHtmlDialogSupported()) lib.popUpBox.config(JSON.stringify([this.dialogGrps, this.dialogFiltGrps, this.defViewPatterns, this.defFilterPatterns]), JSON.stringify(libSet), cfgWindow, ok_callback); else { - popUpBox.ok = false; + lib.popUpBox.ok = false; $Lib.trace('options dialog isn\'t available with current operating system. All settings in options are available in panel properties. Common settings are on the menu.'); } } searchPaint() { - window.RepaintRect(ui.x, ui.y, ui.w, this.search.h); + window.RepaintRect(lib.ui.x, lib.ui.y, lib.ui.w, this.search.h); } set(n, i, treeArtToggle) { @@ -679,490 +679,490 @@ class Panel { case 0: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'traditional'; - pref.libraryLayout = 'normal'; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 2; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.countsRight = false; - ppt.itemShowStatistics = 0; - ppt.nodeStyle = 0; - ppt.inlineRoot = false; - ppt.autoCollapse = false; - ppt.treeAutoExpandSingle = false; - ppt.facetView = false; - ui.sbar.type = 1; // ui.sbar.type = 0; - ppt.sbarType = 1; // ppt.sbarType = 0; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar // ppt.sbarShow = 2; - ppt.fullLineSelection = false; - ppt.highLightText = true; - ppt.rowStripes = false; - ppt.highLightRow = 3; - ppt.highLightNode = true; - ppt.verticalPad = 3; - ppt.rootNode = 0; // ppt.rootNode = 1; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = false; - ppt.albumArtLabelType = 1; - ppt.artId = 0; - ppt.albumArtFlowMode = false; // In default does not exist + grSet.libraryDesign = 'traditional'; + grSet.libraryLayout = 'normal'; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 2; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.countsRight = false; + libSet.itemShowStatistics = 0; + libSet.nodeStyle = 0; + libSet.inlineRoot = false; + libSet.autoCollapse = false; + libSet.treeAutoExpandSingle = false; + libSet.facetView = false; + lib.ui.sbar.type = 1; // lib.ui.sbar.type = 0; + libSet.sbarType = 1; // libSet.sbarType = 0; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar // libSet.sbarShow = 2; + libSet.fullLineSelection = false; + libSet.highLightText = true; + libSet.rowStripes = false; + libSet.highLightRow = 3; + libSet.highLightNode = true; + libSet.verticalPad = 3; + libSet.rootNode = 0; // libSet.rootNode = 1; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = false; + libSet.albumArtLabelType = 1; + libSet.artId = 0; + libSet.albumArtFlowMode = false; // In default does not exist this.load(); // } // } // const caption = 'Quick Setup: Traditional Style'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 1: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'modern'; - pref.libraryLayout = 'normal'; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.countsRight = true; - ppt.itemShowStatistics = 0; - ppt.nodeStyle = 1; - ppt.inlineRoot = true; - ppt.autoCollapse = false; - ppt.treeAutoExpandSingle = false; - ppt.facetView = false; - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 2; - ppt.highLightNode = true; - ppt.verticalPad = 5; - ppt.rootNode = 3; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = false; - ppt.albumArtLabelType = 1; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 2; // In default the same - ppt.artId = 0; - ppt.albumArtFlowMode = false; // In default does not exist + grSet.libraryDesign = 'modern'; + grSet.libraryLayout = 'normal'; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.countsRight = true; + libSet.itemShowStatistics = 0; + libSet.nodeStyle = 1; + libSet.inlineRoot = true; + libSet.autoCollapse = false; + libSet.treeAutoExpandSingle = false; + libSet.facetView = false; + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 2; + libSet.highLightNode = true; + libSet.verticalPad = 5; + libSet.rootNode = 3; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = false; + libSet.albumArtLabelType = 1; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 2; // In default the same + libSet.artId = 0; + libSet.albumArtFlowMode = false; // In default does not exist this.load(); // } // } // const caption = 'Quick Setup: Modern Style'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 2: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'ultraModern'; - pref.libraryLayout = 'normal'; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.countsRight = true; - ppt.itemShowStatistics = 1; - ppt.nodeStyle = 3; - ppt.inlineRoot = true; - ppt.autoCollapse = true; - ppt.treeAutoExpandSingle = true; - ppt.facetView = false; - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 1; - ppt.highLightNode = true; // ppt.highLightNode = false; - ppt.verticalPad = 5; - ppt.rootNode = 3; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = false; - if (!ppt.presetLoadCurView) ppt.viewBy = 1; - ppt.albumArtFlowMode = false; - ppt.albumArtLabelType = 1; - ppt.albumArtFlipLabels = false; - ppt.imgStyleFront = 1; - ppt.itemOverlayType = 1; - ppt.thumbNailSize = 2; - ppt.albumArtGrpLevel = 0; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 2; // In default the same - ppt.artId = 0; + grSet.libraryDesign = 'ultraModern'; + grSet.libraryLayout = 'normal'; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.countsRight = true; + libSet.itemShowStatistics = 1; + libSet.nodeStyle = 3; + libSet.inlineRoot = true; + libSet.autoCollapse = true; + libSet.treeAutoExpandSingle = true; + libSet.facetView = false; + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 1; + libSet.highLightNode = true; // libSet.highLightNode = false; + libSet.verticalPad = 5; + libSet.rootNode = 3; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = false; + if (!libSet.presetLoadCurView) libSet.viewBy = 1; + libSet.albumArtFlowMode = false; + libSet.albumArtLabelType = 1; + libSet.albumArtFlipLabels = false; + libSet.imgStyleFront = 1; + libSet.itemOverlayType = 1; + libSet.thumbNailSize = 2; + libSet.albumArtGrpLevel = 0; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 2; // In default the same + libSet.artId = 0; this.load(); // } // } // const caption = 'Quick Setup: Ultra Modern Style'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 3: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'clean'; - ppt.countsRight = true; - ppt.itemShowStatistics = 0; - ppt.nodeStyle = 5; - ppt.inlineRoot = true; - ppt.autoCollapse = false; - ppt.treeAutoExpandSingle = false; - ppt.facetView = false; - ui.sbar.type = 0; - ppt.sbarType = 0; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = true; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 0; - ppt.highLightNode = true; - ppt.verticalPad = 5; - ppt.rootNode = 3; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = false; - pref.libraryLayout = ppt.albumArtShow ? 'full' : 'normal'; - ppt.albumArtLabelType = 1; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 2; // In default the same - ppt.artId = 0; + grSet.libraryDesign = 'clean'; + libSet.countsRight = true; + libSet.itemShowStatistics = 0; + libSet.nodeStyle = 5; + libSet.inlineRoot = true; + libSet.autoCollapse = false; + libSet.treeAutoExpandSingle = false; + libSet.facetView = false; + lib.ui.sbar.type = 0; + libSet.sbarType = 0; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = true; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 0; + libSet.highLightNode = true; + libSet.verticalPad = 5; + libSet.rootNode = 3; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = false; + grSet.libraryLayout = libSet.albumArtShow ? 'full' : 'normal'; + libSet.albumArtLabelType = 1; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 2; // In default the same + libSet.artId = 0; this.load(); // } // } // const caption = 'Quick Setup: Clean'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 4: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'facet'; - pref.libraryLayout = 'normal'; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.countsRight = true; - ppt.itemShowStatistics = 0; - ppt.nodeStyle = 1; - ppt.inlineRoot = true; - ppt.autoCollapse = false; - ppt.treeAutoExpandSingle = false; - ppt.facetView = true; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = false; - ppt.albumArtLabelType = 1; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 2; // In default the same - ppt.artId = 0; - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 2; - ppt.highLightNode = true; - ppt.verticalPad = 5; - ppt.rootNode = 3; + grSet.libraryDesign = 'facet'; + grSet.libraryLayout = 'normal'; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.countsRight = true; + libSet.itemShowStatistics = 0; + libSet.nodeStyle = 1; + libSet.inlineRoot = true; + libSet.autoCollapse = false; + libSet.treeAutoExpandSingle = false; + libSet.facetView = true; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = false; + libSet.albumArtLabelType = 1; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 2; // In default the same + libSet.artId = 0; + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 2; + libSet.highLightNode = true; + libSet.verticalPad = 5; + libSet.rootNode = 3; this.load(); // } // } // const caption = 'Quick Setup: Facet'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 5: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'coversLabelsRight'; - pref.libraryLayout = 'normal'; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 0; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.nodeStyle = 5; // In default does not exist - ppt.inlineRoot = true; // In default does not exist - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 1; - ppt.highLightNode = false; - ppt.verticalPad = 5; - ppt.rootNode = 3; - ppt.facetView = false; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = true; - if (!ppt.presetLoadCurView) ppt.viewBy = 0; // ppt.viewBy = 1; - ppt.albumArtFlowMode = false; - ppt.albumArtLabelType = 2; - ppt.imgStyleFront = 1; - ppt.itemOverlayType = 2; - ppt.artId = 0; - ppt.albumArtGrpLevel = 0; + grSet.libraryDesign = 'coversLabelsRight'; + grSet.libraryLayout = 'normal'; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 0; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.nodeStyle = 5; // In default does not exist + libSet.inlineRoot = true; // In default does not exist + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 1; + libSet.highLightNode = false; + libSet.verticalPad = 5; + libSet.rootNode = 3; + libSet.facetView = false; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = true; + if (!libSet.presetLoadCurView) libSet.viewBy = 0; // libSet.viewBy = 1; + libSet.albumArtFlowMode = false; + libSet.albumArtLabelType = 2; + libSet.imgStyleFront = 1; + libSet.itemOverlayType = 2; + libSet.artId = 0; + libSet.albumArtGrpLevel = 0; this.load(); // } // } // const caption = 'Quick Setup: Covers [Labels Right]'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 6: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'coversLabelsBottom'; - pref.libraryLayout = 'normal'; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 0; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.nodeStyle = 5; // In default does not exist - ppt.inlineRoot = true; // In default does not exist - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 1; - ppt.highLightNode = false; - ppt.verticalPad = 5; - ppt.rootNode = 3; - ppt.facetView = false; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = true; - if (!ppt.presetLoadCurView) ppt.viewBy = 1; // ppt.viewBy = 1; - ppt.albumArtFlowMode = false; - ppt.albumArtLabelType = 1; - ppt.albumArtFlipLabels = false; - ppt.itemShowStatistics = 0; - ppt.imgStyleFront = 1; - ppt.itemOverlayType = 1; - ppt.artId = 0; + grSet.libraryDesign = 'coversLabelsBottom'; + grSet.libraryLayout = 'normal'; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 0; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.nodeStyle = 5; // In default does not exist + libSet.inlineRoot = true; // In default does not exist + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 1; + libSet.highLightNode = false; + libSet.verticalPad = 5; + libSet.rootNode = 3; + libSet.facetView = false; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = true; + if (!libSet.presetLoadCurView) libSet.viewBy = 1; // libSet.viewBy = 1; + libSet.albumArtFlowMode = false; + libSet.albumArtLabelType = 1; + libSet.albumArtFlipLabels = false; + libSet.itemShowStatistics = 0; + libSet.imgStyleFront = 1; + libSet.itemOverlayType = 1; + libSet.artId = 0; this.load(); // } // } // const caption = 'Quick Setup: Covers [Labels Bottom]'; - // cconst wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // cconst wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 7: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'coversLabelsBlend'; - pref.libraryLayout = 'normal'; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 0; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.nodeStyle = 5; // In default does not exist - ppt.inlineRoot = true; // In default does not exist - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 1; - ppt.highLightNode = false; - ppt.verticalPad = 5; - ppt.rootNode = 3; - ppt.facetView = false; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = true; - if (!ppt.presetLoadCurView) ppt.viewBy = 0; // if (!ppt.presetLoadCurView) ppt.viewBy = 1; - ppt.albumArtFlowMode = false; - ppt.albumArtLabelType = 4; - ppt.albumArtFlipLabels = false; - ppt.itemShowStatistics = 0; - ppt.imgStyleFront = 0; - ppt.itemOverlayType = 1; - ppt.artId = 0; - ppt.albumArtGrpLevel = 0; + grSet.libraryDesign = 'coversLabelsBlend'; + grSet.libraryLayout = 'normal'; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 0; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.nodeStyle = 5; // In default does not exist + libSet.inlineRoot = true; // In default does not exist + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 1; + libSet.highLightNode = false; + libSet.verticalPad = 5; + libSet.rootNode = 3; + libSet.facetView = false; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = true; + if (!libSet.presetLoadCurView) libSet.viewBy = 0; // if (!libSet.presetLoadCurView) libSet.viewBy = 1; + libSet.albumArtFlowMode = false; + libSet.albumArtLabelType = 4; + libSet.albumArtFlipLabels = false; + libSet.itemShowStatistics = 0; + libSet.imgStyleFront = 0; + libSet.itemOverlayType = 1; + libSet.artId = 0; + libSet.albumArtGrpLevel = 0; this.load(); // } // } // const caption = 'Quick Setup: Covers [Labels Blend]'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 8: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'artistLabelsRight'; - pref.libraryLayout = ppt.albumArtShow ? 'full' : 'normal'; - if (pref.libraryThumbnailSize === 'auto') ppt.thumbNailSize = 0; - ui.sbar.type = 1; - ppt.sbarType = 1; - ppt.sbarShow = 1; - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; // ppt.rowStripes = true; - ppt.highLightRow = 1; - ppt.highLightNode = false; - ppt.verticalPad = 5; - ppt.rootNode = 3; - ppt.facetView = false; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = true; - if (!ppt.presetLoadCurView) ppt.viewBy = 0; - ppt.albumArtFlowMode = false; - ppt.albumArtLabelType = 2; - ppt.itemShowStatistics = 0; - ppt.imgStyleArtist = 1; - ppt.itemOverlayType = 0; - ppt.thumbNailSize = 1; - ppt.artId = 4; - ppt.albumArtGrpLevel = 0; + grSet.libraryDesign = 'artistLabelsRight'; + grSet.libraryLayout = libSet.albumArtShow ? 'full' : 'normal'; + if (grSet.libraryThumbnailSize === 'auto') libSet.thumbNailSize = 0; + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + libSet.sbarShow = 1; + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; // libSet.rowStripes = true; + libSet.highLightRow = 1; + libSet.highLightNode = false; + libSet.verticalPad = 5; + libSet.rootNode = 3; + libSet.facetView = false; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = true; + if (!libSet.presetLoadCurView) libSet.viewBy = 0; + libSet.albumArtFlowMode = false; + libSet.albumArtLabelType = 2; + libSet.itemShowStatistics = 0; + libSet.imgStyleArtist = 1; + libSet.itemOverlayType = 0; + libSet.thumbNailSize = 1; + libSet.artId = 4; + libSet.albumArtGrpLevel = 0; this.load(); // } // } // const caption = 'Quick Setup: Artist Photos [Labels Right]'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } - case 9: ppt.thumbNailSize++; this.load(); break; - case 10: ppt.thumbNailSize--; this.load(); break; + case 9: libSet.thumbNailSize++; this.load(); break; + case 10: libSet.thumbNailSize--; this.load(); break; case 11: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'flowMode'; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = true; - pref.libraryLayout = ppt.albumArtShow ? 'full' : 'normal'; - if (pref.playerSize_HD_small && ppt.thumbNailSize === 'auto') ppt.thumbNailSize = 2; - ppt.highLightNowplaying = true; // In default does not exist - ppt.zoomNode = 100; // In default does not exist - ppt.countsRight = true; - ppt.nodeStyle = 5; // ppt.nodeStyle = 2; - ppt.inlineRoot = true; - ppt.autoCollapse = false; - ppt.treeAutoExpandSingle = false; - ppt.facetView = false; - ppt.highLightRow = 1; - if (!ppt.presetLoadCurView) ppt.viewBy = 0; // if (!ppt.presetLoadCurView) ppt.viewBy = 1; - ppt.albumArtFlowMode = true; - ppt.albumArtLabelType = 1; - ppt.itemShowStatistics = 0; - ppt.imgStyleFront = 1; - ppt.itemOverlayType = 0; - if (!ppt.presetLoadCurView) ppt.artId = 0; - ppt.albumArtGrpLevel = 0; + grSet.libraryDesign = 'flowMode'; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = true; + grSet.libraryLayout = libSet.albumArtShow ? 'full' : 'normal'; + if (grSet.playerSize_HD_small && libSet.thumbNailSize === 'auto') libSet.thumbNailSize = 2; + libSet.highLightNowplaying = true; // In default does not exist + libSet.zoomNode = 100; // In default does not exist + libSet.countsRight = true; + libSet.nodeStyle = 5; // libSet.nodeStyle = 2; + libSet.inlineRoot = true; + libSet.autoCollapse = false; + libSet.treeAutoExpandSingle = false; + libSet.facetView = false; + libSet.highLightRow = 1; + if (!libSet.presetLoadCurView) libSet.viewBy = 0; // if (!libSet.presetLoadCurView) libSet.viewBy = 1; + libSet.albumArtFlowMode = true; + libSet.albumArtLabelType = 1; + libSet.itemShowStatistics = 0; + libSet.imgStyleFront = 1; + libSet.itemOverlayType = 0; + if (!libSet.presetLoadCurView) libSet.artId = 0; + libSet.albumArtGrpLevel = 0; this.load(); // } // } // const caption = 'Quick Setup: Flow Mode'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 12: { // const continue_confirmation = (status, confirmed) => { // if (confirmed) { - pref.libraryDesign = 'reborn'; - pref.libraryLayout = 'normal'; - if (pref.playerSize_HD_small && ppt.thumbNailSize === 'auto') ppt.thumbNailSize = 1; - ppt.highLightNowplaying = true; - ppt.zoomNode = 100; - ppt.countsRight = true; - ppt.nodeStyle = 5; - ppt.inlineRoot = true; - ppt.autoCollapse = false; - ppt.treeAutoExpandSingle = true; - ppt.facetView = false; - ui.sbar.type = 1; - ppt.sbarType = 1; - // ppt.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar - ppt.fullLineSelection = true; - ppt.highLightText = false; - ppt.rowStripes = false; - ppt.highLightRow = 1; - ppt.highLightNode = true; - ppt.verticalPad = 5; - ppt.rootNode = 3; - panel.imgView = pref.savedAlbumArtShow = ppt.albumArtShow = false; - if (!ppt.presetLoadCurView) ppt.viewBy = 2; - ppt.albumArtLabelType = 1; - ppt.itemOverlayType = 0; - ppt.artId = 0; - ppt.albumArtFlowMode = false; + grSet.libraryDesign = 'reborn'; + grSet.libraryLayout = 'normal'; + if (grSet.playerSize_HD_small && libSet.thumbNailSize === 'auto') libSet.thumbNailSize = 1; + libSet.highLightNowplaying = true; + libSet.zoomNode = 100; + libSet.countsRight = true; + libSet.nodeStyle = 5; + libSet.inlineRoot = true; + libSet.autoCollapse = false; + libSet.treeAutoExpandSingle = true; + libSet.facetView = false; + lib.ui.sbar.type = 1; + libSet.sbarType = 1; + // libSet.sbarShow = 1; // Disabled here, state controlled in georgia-reborn-main -> library scrollbar menu -> pref.libraryAutoHideScrollbar + libSet.fullLineSelection = true; + libSet.highLightText = false; + libSet.rowStripes = false; + libSet.highLightRow = 1; + libSet.highLightNode = true; + libSet.verticalPad = 5; + libSet.rootNode = 3; + lib.panel.imgView = grSet.savedAlbumArtShow = libSet.albumArtShow = false; + if (!libSet.presetLoadCurView) libSet.viewBy = 2; + libSet.albumArtLabelType = 1; + libSet.itemOverlayType = 0; + libSet.artId = 0; + libSet.albumArtFlowMode = false; this.load(); // } // } // const caption = 'Quick Setup: Georgia-ReBORN Style'; - // const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; + // const wsh = lib.popUpBox.isHtmlDialogSupported() ? lib.popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; // if (wsh) continue_confirmation('ok', $Lib.wshPopup(prompt, caption)); break; } case 13: - ppt.toggle('presetLoadCurView'); + libSet.toggle('presetLoadCurView'); } break; case 'Filter': - lib.searchCache = {}; - pop.cache.filter = {}; - pop.cache.search = {}; + lib.lib.searchCache = {}; + lib.pop.cache.filter = {}; + lib.pop.cache.search = {}; switch (i) { case this.filter.menu.length: - ppt.toggle('reset'); - if (ppt.reset) { + libSet.toggle('reset'); + if (libSet.reset) { this.searchPaint(); - lib.treeState(true, 2); + lib.lib.treeState(true, 2); } break; default: - ppt.filterBy = i; + libSet.filterBy = i; this.calcText(); - if (this.search.txt) lib.upd_search = true; - if (!ppt.reset) { - const ix = pop.get_ix(!panel.imgView ? 0 : img.panel.x + 1, (!panel.imgView || img.style.vertical ? panel.tree.y : panel.tree.x) + sbar.row.h / 2, true, false); - const l = Math.min(Math.floor(ix + panel.rows), pop.tree.length); + if (this.search.txt) lib.lib.upd_search = true; + if (!libSet.reset) { + const ix = lib.pop.get_ix(!lib.panel.imgView ? 0 : libImg.panel.x + 1, (!lib.panel.imgView || libImg.style.vertical ? lib.panel.tree.y : lib.panel.tree.x) + lib.sbar.row.h / 2, true, false); + const l = Math.min(Math.floor(ix + lib.panel.rows), lib.pop.tree.length); if (ix != -1) { for (i = ix; i < l; i++) { - if (pop.tree[i].sel) { - sbar.checkScroll(sbar.row.h * i, 'full', true); - lib.logTree(); + if (lib.pop.tree[i].sel) { + lib.sbar.checkScroll(lib.sbar.row.h * i, 'full', true); + lib.lib.logTree(); break; } } } - if (!ppt.rememberTree && !ppt.reset) - { lib.logTree(); } - else if (ppt.rememberTree) - { lib.logFilter(); } + if (!libSet.rememberTree && !libSet.reset) + { lib.lib.logTree(); } + else if (libSet.rememberTree) + { lib.lib.logFilter(); } } - lib.getLibrary(); - lib.rootNodes(!ppt.reset ? 1 : 0, true); - but.refresh(true); + lib.lib.getLibrary(); + lib.lib.rootNodes(!libSet.reset ? 1 : 0, true); + lib.but.refresh(true); this.searchPaint(); - if (!pop.notifySelection()) { - const list = !this.search.txt.length || !lib.list.Count ? lib.list : this.list; - window.NotifyOthers(window.Name, ppt.filterBy ? list : new FbMetadbHandleList()); + if (!lib.pop.notifySelection()) { + const list = !this.search.txt.length || !lib.lib.list.Count ? lib.lib.list : this.list; + window.NotifyOthers(window.Name, libSet.filterBy ? list : new FbMetadbHandleList()); } - if (ppt.searchSend == 2 && this.search.txt.length) pop.load(this.list, false, false, false, true, false); + if (libSet.searchSend == 2 && this.search.txt.length) lib.pop.load(this.list, false, false, false, true, false); break; } - pop.checkAutoHeight(); + lib.pop.checkAutoHeight(); break; case 'view': { if (this.colMarker) this.draw = false; - if (this.search.txt) lib.upd_search = true; - this.getFields(i < this.grp.length ? i : ppt.viewBy, ppt.filterBy); + if (this.search.txt) lib.lib.upd_search = true; + this.getFields(i < this.grp.length ? i : libSet.viewBy, libSet.filterBy); this.on_size(); - lib.searchCache = {}; - pop.cache = { + lib.lib.searchCache = {}; + lib.pop.cache = { standard: {}, search: {}, filter: {} }; - lib.checkView(); - const key = !ppt.rememberView ? 'def' : this.viewName; - if ((ppt.rememberView || treeArtToggle) && lib.exp[key]) lib.readTreeState(false, treeArtToggle); - lib.getLibrary(treeArtToggle); - lib.rootNodes((ppt.rememberView || treeArtToggle), !!((ppt.rememberView || treeArtToggle))); - if (ppt.rememberView) { + lib.lib.checkView(); + const key = !libSet.rememberView ? 'def' : this.viewName; + if ((libSet.rememberView || treeArtToggle) && lib.lib.exp[key]) lib.lib.readTreeState(false, treeArtToggle); + lib.lib.getLibrary(treeArtToggle); + lib.lib.rootNodes((libSet.rememberView || treeArtToggle), !!((libSet.rememberView || treeArtToggle))); + if (libSet.rememberView) { this.calcText(); - but.refresh(true); + lib.but.refresh(true); this.searchPaint(); - lib.logTree(); - if (!pop.notifySelection()) { - const list = !this.search.txt.length || !lib.list.Count ? lib.list : this.list; - window.NotifyOthers(window.Name, ppt.filterBy ? list : new FbMetadbHandleList()); + lib.lib.logTree(); + if (!lib.pop.notifySelection()) { + const list = !this.search.txt.length || !lib.lib.list.Count ? lib.lib.list : this.list; + window.NotifyOthers(window.Name, libSet.filterBy ? list : new FbMetadbHandleList()); } } this.draw = true; - if (ppt.searchSend == 2 && this.search.txt.length) pop.load(this.list, false, false, false, true, false); - pop.checkAutoHeight(); + if (libSet.searchSend == 2 && this.search.txt.length) lib.pop.load(this.list, false, false, false, true, false); + lib.pop.checkAutoHeight(); break; } } @@ -1170,41 +1170,41 @@ class Panel { setHeight(n) { if (!this.pn_h_auto) return; - ppt.pn_h = n || this.imgView ? ppt.pn_h_max : ppt.pn_h_min; - window.MaxHeight = window.MinHeight = ppt.pn_h; + libSet.pn_h = n || this.imgView ? libSet.pn_h_max : libSet.pn_h_min; + window.MaxHeight = window.MinHeight = libSet.pn_h; } setRootName() { - this.sourceName = ['Active Playlist', !ppt.fixedPlaylist ? 'Library' : ppt.fixedPlaylistName, 'Panel'][ppt.libSource]; - this.viewName = this.grp[ppt.viewBy].name; - switch (ppt.rootNode) { + this.sourceName = ['Active Playlist', !libSet.fixedPlaylist ? 'Library' : libSet.fixedPlaylistName, 'Panel'][libSet.libSource]; + this.viewName = this.grp[libSet.viewBy].name; + switch (libSet.rootNode) { case 1: - this.rootName = !ppt.showSource ? 'All Music' : this.sourceName; + this.rootName = !libSet.showSource ? 'All Music' : this.sourceName; break; case 2: - this.rootName = this.viewName + (!ppt.showSource ? '' : ` [${this.sourceName}]`); + this.rootName = this.viewName + (!libSet.showSource ? '' : ` [${this.sourceName}]`); break; case 3: { const nm = this.viewName.replace(/view by|^by\b/i, '').trim(); const basenames = nm.split(' ').map(v => pluralize(v)); const basename = basenames.join(' ').replace(/(album|artist|top|track)s\s/gi, '$1 ').replace(/(similar artist)\s/gi, '$1s ').replace(/years - albums/gi, 'Year - Albums'); - this.rootName = (!this.imgView ? `${!ppt.showSource ? 'All' : this.sourceName} (#^^^^# ${basename})` : `All #^^^^# ${basename}`); - this.rootName1 = (!this.imgView ? `${!ppt.showSource ? 'All' : this.sourceName} (1 ${nm})` : `All 1 ${nm}`); + this.rootName = (!this.imgView ? `${!libSet.showSource ? 'All' : this.sourceName} (#^^^^# ${basename})` : `All #^^^^# ${basename}`); + this.rootName1 = (!this.imgView ? `${!libSet.showSource ? 'All' : this.sourceName} (1 ${nm})` : `All 1 ${nm}`); break; } } } setSelection() { - const flowMode = this.imgView && ppt.albumArtFlowMode; - return (flowMode && ppt.flowModeFollowSelection || !flowMode && ppt.stndModeFollowSelection) && (!ppt.followPlaylistFocus || ppt.libSource) && panel.m.x == -1; + const flowMode = this.imgView && libSet.albumArtFlowMode; + return (flowMode && libSet.flowModeFollowSelection || !flowMode && libSet.stndModeFollowSelection) && (!libSet.followPlaylistFocus || libSet.libSource) && lib.panel.m.x == -1; } setTopBar() { - const sz = Math.round(12 * $Lib.scale * this.zoomFilter); - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`] || 14; - const mod = sz > 15 ? (sz % 2) - 1 : 0; - this.filter.font = gdi.Font(fontDefault, this.zoomFilter > 1.05 ? Math.floor(libraryFontSize /*11 * $Lib.scale * this.zoomFilter*/) : Math.max((libraryFontSize) /*11 * $Lib.scale * this.zoomFilter*/, 9), 1); + // const sz = Math.round(12 * $Lib.scale * this.zoomFilter); + // const mod = sz > 15 ? (sz % 2) - 1 : 0; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`] || 14; + this.filter.font = gdi.Font(grFont.fontDefault, this.zoomFilter > 1.05 ? Math.floor(libraryFontSize /*11 * $Lib.scale * this.zoomFilter*/) : Math.max((libraryFontSize) /*11 * $Lib.scale * this.zoomFilter*/, 9), 1); this.settings.font = gdi.Font('Segoe UI Symbol', libraryFontSize /*sz + mod*/, 0); this.settings.icon = '\uE10C'; this.settings.offset = Math.round(1 * this.settings.font.Size / 17); @@ -1224,164 +1224,164 @@ class Panel { } treePaint() { - window.RepaintRect(ui.x, ui.y, ui.w, ui.h); + window.RepaintRect(lib.ui.x, lib.ui.y, lib.ui.w, lib.ui.h); } updateProp(prop, value) { - const curActionMode = ppt.actionMode; + const curActionMode = libSet.actionMode; Object.entries(prop).forEach(v => { - ppt[v[0].replace('_internal', '')] = v[1][value]; + libSet[v[0].replace('_internal', '')] = v[1][value]; }); - img.asyncBypass = Date.now(); - img.preLoadItems = []; - clearInterval(img.timer.preLoad); - img.timer.preLoad = null; - - pop.autoPlay.send = ppt.autoPlay; - pop.setActions(); - if (ppt.actionMode != curActionMode) { - if (ppt.actionMode != 2) { - ppt.itemShowStatistics = ppt.itemShowStatisticsLast; - ppt.highLightNowplaying = ppt.highLightNowplayingLast; - ppt.nowPlayingIndicator = ppt.nowPlayingIndicatorLast; - ppt.nowPlayingSidemarker = ppt.nowPlayingSidemarkerLast; + libImg.asyncBypass = Date.now(); + libImg.preLoadItems = []; + clearInterval(libImg.timer.preLoad); + libImg.timer.preLoad = null; + + lib.pop.autoPlay.send = libSet.autoPlay; + lib.pop.setActions(); + if (libSet.actionMode != curActionMode) { + if (libSet.actionMode != 2) { + libSet.itemShowStatistics = libSet.itemShowStatisticsLast; + libSet.highLightNowplaying = libSet.highLightNowplayingLast; + libSet.nowPlayingIndicator = libSet.nowPlayingIndicatorLast; + libSet.nowPlayingSidemarker = libSet.nowPlayingSidemarkerLast; } else { - ppt.itemShowStatisticsLast = ppt.itemShowStatistics; - ppt.highLightNowplayingLast = ppt.highLightNowplaying; - ppt.nowPlayingIndicatorLast = ppt.nowPlayingIndicator; - ppt.nowPlayingSidemarkerLast = ppt.nowPlayingSidemarker; - ppt.itemShowStatistics = 7; - ppt.highLightNowplaying = true; - ppt.nowPlayingIndicator = true; - ppt.nowPlayingSidemarker = true; + libSet.itemShowStatisticsLast = libSet.itemShowStatistics; + libSet.highLightNowplayingLast = libSet.highLightNowplaying; + libSet.nowPlayingIndicatorLast = libSet.nowPlayingIndicator; + libSet.nowPlayingSidemarkerLast = libSet.nowPlayingSidemarker; + libSet.itemShowStatistics = 7; + libSet.highLightNowplaying = true; + libSet.nowPlayingIndicator = true; + libSet.nowPlayingSidemarker = true; } } - ppt.autoExpandLimit = Math.round(ppt.autoExpandLimit); - if (isNaN(ppt.autoExpandLimit)) ppt.autoExpandLimit = 350; - ppt.autoExpandLimit = $Lib.clamp(ppt.autoExpandLimit, 10, 1000); - ppt.margin = Math.round(ppt.margin); - if (isNaN(ppt.margin)) ppt.margin = 8 * $Lib.scale; - ppt.margin = $Lib.clamp(ppt.margin, 0, 100); - ppt.treeIndent = Math.round(ppt.treeIndent); - if (isNaN(ppt.treeIndent)) ppt.treeIndent = 19 * $Lib.scale; - ppt.treeIndent = $Lib.clamp(ppt.treeIndent, 0, 100); - - pop.cache = { + libSet.autoExpandLimit = Math.round(libSet.autoExpandLimit); + if (isNaN(libSet.autoExpandLimit)) libSet.autoExpandLimit = 350; + libSet.autoExpandLimit = $Lib.clamp(libSet.autoExpandLimit, 10, 1000); + libSet.margin = Math.round(libSet.margin); + if (isNaN(libSet.margin)) libSet.margin = 8 * $Lib.scale; + libSet.margin = $Lib.clamp(libSet.margin, 0, 100); + libSet.treeIndent = Math.round(libSet.treeIndent); + if (isNaN(libSet.treeIndent)) libSet.treeIndent = 19 * $Lib.scale; + libSet.treeIndent = $Lib.clamp(libSet.treeIndent, 0, 100); + + lib.pop.cache = { standard: {}, search: {}, filter: {} }; - pop.tf = { - added: FbTitleFormat(ppt.tfAdded), + lib.pop.tf = { + added: FbTitleFormat(libSet.tfAdded), bitrate: FbTitleFormat('%bitrate%'), bytes: FbTitleFormat('%path%|%filesize%'), - date: FbTitleFormat(ppt.tfDate), - firstPlayed: FbTitleFormat(ppt.tfFirstPlayed), - lastPlayed: FbTitleFormat(ppt.tfLastPlayed), - pc: FbTitleFormat(ppt.tfPc), - popularity: FbTitleFormat(ppt.tfPopularity), - rating: FbTitleFormat(ppt.tfRating) + date: FbTitleFormat(libSet.tfDate), + firstPlayed: FbTitleFormat(libSet.tfFirstPlayed), + lastPlayed: FbTitleFormat(libSet.tfLastPlayed), + pc: FbTitleFormat(libSet.tfPc), + popularity: FbTitleFormat(libSet.tfPopularity), + rating: FbTitleFormat(libSet.tfRating) } - pop.tree.forEach(v => { + lib.pop.tree.forEach(v => { v.id = ''; v.count = ''; delete v.statistics; delete v._statistics; }); - lib.checkView(); - lib.logTree(); - img.setRoot(); - ppt.zoomImg = Math.round($Lib.clamp(ppt.zoomImg, 10, 500)); + lib.lib.checkView(); + lib.lib.logTree(); + libImg.setRoot(); + libSet.zoomImg = Math.round($Lib.clamp(libSet.zoomImg, 10, 500)); const o = !this.imgView ? 'verticalPad' : 'verticalAlbumArtPad'; - if (ppt[o] === null) ppt[o] = !this.imgView ? 3 : 2; - ppt[o] = Math.round(ppt[o]); - if (isNaN(ppt[o])) ppt[o] = !this.imgView ? 3 : 2; - ppt[o] = $Lib.clamp(ppt[o], 0, !this.imgView ? 100 : 20); - - ppt.iconCustom = ppt.iconCustom.trim(); - ui.setNodes(); - sbar.active = true; - sbar.duration = { + if (libSet[o] === null) libSet[o] = !this.imgView ? 3 : 2; + libSet[o] = Math.round(libSet[o]); + if (isNaN(libSet[o])) libSet[o] = !this.imgView ? 3 : 2; + libSet[o] = $Lib.clamp(libSet[o], 0, !this.imgView ? 100 : 20); + + libSet.iconCustom = libSet.iconCustom.trim(); + lib.ui.setNodes(); + lib.sbar.active = true; + lib.sbar.duration = { drag: 200, - inertia: ppt.durationTouchFlick, - full: ppt.durationScroll + inertia: libSet.durationTouchFlick, + full: libSet.durationScroll }; - sbar.duration.scroll = Math.round(sbar.duration.full * 0.8); - sbar.duration.step = Math.round(sbar.duration.full * 2 / 3); - sbar.duration.bar = sbar.duration.full; - sbar.duration.barFast = sbar.duration.step; - if (!ppt.butCustIconFont.length) ppt.butCustIconFont = 'Segoe UI Symbol'; - ui.setSbar(); + lib.sbar.duration.scroll = Math.round(lib.sbar.duration.full * 0.8); + lib.sbar.duration.step = Math.round(lib.sbar.duration.full * 2 / 3); + lib.sbar.duration.bar = lib.sbar.duration.full; + lib.sbar.duration.barFast = lib.sbar.duration.step; + if (!libSet.butCustIconFont.length) libSet.butCustIconFont = 'Segoe UI Symbol'; + lib.ui.setSbar(); on_colours_changed(); - if (ui.col.counts) panel.colMarker = true; - if (ppt.themed && ppt.theme) { - const themed_image = pref.customLibraryDir ? `${globals.customLibraryDir}cache\\library\\themed\\themed_image.bmp` : `${fb.ProfilePath}cache\\library\\themed\\themed_image.bmp`; - if ($Lib.file(themed_image)) sync.image(gdi.Image(themed_image)); + if (lib.ui.col.counts) lib.panel.colMarker = true; + if (libSet.themed && libSet.theme) { + const themed_image = grSet.customLibraryDir ? `${grCfg.customLibraryDir}cache\\library\\themed\\themed_image.bmp` : `${fb.ProfilePath}cache\\library\\themed\\themed_image.bmp`; + if ($Lib.file(themed_image)) libSync.image(gdi.Image(themed_image)); } - if (img.labels.overlayDark) ui.getItemColours(); - initLibraryColors(); - themeColorAdjustments(); + if (libImg.labels.overlayDark) lib.ui.getItemColours(); + grm.theme.initLibraryColors(); + grm.theme.themeColorAdjustments(); this.setRootName(); - but.setSbarIcon(); - pop.setValues(); - pop.inlineRoot = ppt.rootNode && (ppt.inlineRoot || ppt.facetView); + lib.but.setSbarIcon(); + lib.pop.setValues(); + lib.pop.inlineRoot = libSet.rootNode && (libSet.inlineRoot || libSet.facetView); - ui.getFont(); + lib.ui.getFont(); this.on_size(); this.tree.y = this.search.h; - but.createImages(); - but.refresh(true); - find.on_size(); - pop.createImages(); - - if (ppt.highLightNowplaying || ppt.nowPlayingSidemarker) { - pop.getNowplaying(); - pop.nowPlayingShow(); + lib.but.createImages(); + lib.but.refresh(true); + lib.find.on_size(); + lib.pop.createImages(); + + if (libSet.highLightNowplaying || libSet.nowPlayingSidemarker) { + lib.pop.getNowplaying(); + lib.pop.nowPlayingShow(); } - if (panel.imgView && pop.tree.length) { - img.trimCache(pop.tree[0].key); - img.metrics(); + if (lib.panel.imgView && lib.pop.tree.length) { + libImg.trimCache(lib.pop.tree[0].key); + libImg.metrics(); } - lib.rootNodes(1, true); - this.pn_h_auto = ppt.pn_h_auto && ppt.rootNode; + lib.lib.rootNodes(1, true); + this.pn_h_auto = libSet.pn_h_auto && libSet.rootNode; if (this.pn_h_auto) { - window.MaxHeight = window.MinHeight = ppt.pn_h; + window.MaxHeight = window.MinHeight = libSet.pn_h; } - if (panel.pn_h_auto && !panel.imgView && ppt.pn_h == ppt.pn_h_min && this.tree[0]) this.clearChild(this.tree[0]); - pop.checkAutoHeight(); - if (sbar.scroll > sbar.max_scroll) sbar.checkScroll(sbar.max_scroll); + if (lib.panel.pn_h_auto && !lib.panel.imgView && libSet.pn_h == libSet.pn_h_min && this.tree[0]) this.clearChild(this.tree[0]); + lib.pop.checkAutoHeight(); + if (lib.sbar.scroll > lib.sbar.max_scroll) lib.sbar.checkScroll(lib.sbar.max_scroll); - if (ui.sbarType != 0) sbar.narrow.show = false; - sbar.resetAuto(); + if (lib.ui.sbarType != 0) lib.sbar.narrow.show = false; + lib.sbar.resetAuto(); window.Repaint(); } zoomReset() { - sbar.logScroll(); - ppt.zoomFont = 100; - ppt.zoomNode = 100; + lib.sbar.logScroll(); + libSet.zoomFont = 100; + libSet.zoomNode = 100; this.zoomFilter = 1; - ppt.zoomFilter = 100; - ppt.zoomTooltipBut = 100; + libSet.zoomFilter = 100; + libSet.zoomTooltipBut = 100; this.setTopBar(); - ui.getFont(); + lib.ui.getFont(); this.on_size(); - find.on_size(); - if (panel.imgView) { - ppt.zoomImg = 100; - img.clearCache(); - img.metrics(); + lib.find.on_size(); + if (lib.panel.imgView) { + libSet.zoomImg = 100; + libImg.clearCache(); + libImg.metrics(); } - if (ui.style.topBarShow || ppt.sbarShow) but.refresh(true); + if (lib.ui.style.topBarShow || libSet.sbarShow) lib.but.refresh(true); window.Repaint(); - sbar.setScroll(); + lib.sbar.setScroll(); } } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-populate.js b/profile/georgia-reborn/scripts/Library/scripts/lib-populate.js index ea67043c..317d9b39 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-populate.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-populate.js @@ -1,20 +1,20 @@ 'use strict'; -class Populate { +class LibPopulate { constructor() { this.alt_dbl_clicked = false; this.childCount = 0; this.clicked_on = 'none'; this.cur = []; this.cur_ix = 0; - this.customSort = FbTitleFormat(ppt.customSort); + this.customSort = FbTitleFormat(libSet.customSort); this.dbl_clicked = false; this.expandedTracks = 0; this.expandLmt = 500; - this.hotKeys = $Lib.split(ppt.hotKeys, 0); + this.hotKeys = $Lib.split(libSet.hotKeys, 0); this.hand = false; this.id = ''; - this.inlineRoot = ppt.rootNode && (ppt.inlineRoot || ppt.facetView); + this.inlineRoot = libSet.rootNode && (libSet.inlineRoot || libSet.facetView); this.is_focused = false; this.last_sel = -1; this.lbtnDn = false; @@ -67,15 +67,15 @@ class Populate { }; this.tf = { - added: FbTitleFormat(ppt.tfAdded), + added: FbTitleFormat(libSet.tfAdded), bitrate: FbTitleFormat('%bitrate%'), bytes: FbTitleFormat('%path%|%filesize%'), - date: FbTitleFormat(ppt.tfDate), - firstPlayed: FbTitleFormat(ppt.tfFirstPlayed), - lastPlayed: FbTitleFormat(ppt.tfLastPlayed), - pc: FbTitleFormat(ppt.tfPc), - popularity: FbTitleFormat(ppt.tfPopularity), - rating: FbTitleFormat(ppt.tfRating) + date: FbTitleFormat(libSet.tfDate), + firstPlayed: FbTitleFormat(libSet.tfFirstPlayed), + lastPlayed: FbTitleFormat(libSet.tfLastPlayed), + pc: FbTitleFormat(libSet.tfPc), + popularity: FbTitleFormat(libSet.tfPopularity), + rating: FbTitleFormat(libSet.tfRating) }; this.triangle = { @@ -96,28 +96,28 @@ class Populate { // * METHODS * // activateTooltip(value) { - if (!pref.showTooltipLibrary && !pref.showTooltipTruncated || tooltipLib.Text == value) return; + if (!grSet.showTooltipLibrary && !grSet.showTooltipTruncated || libTooltip.Text == value) return; this.checkTooltipFont('tree'); - if (pref.showStyledTooltips) { - styledTooltipText = value; - styledTooltipReady = true; + if (grSet.showStyledTooltips) { + grm.ui.styledTooltipText = value; + grm.ui.styledTooltipReady = true; } else { - tooltipLib.Text = value; - tooltipLib.Activate(); + libTooltip.Text = value; + libTooltip.Activate(); } } add(x, y, pl) { - if (y < panel.search.h) return; + if (y < lib.panel.search.h) return; const ix = this.get_ix(x, y, true, false); - panel.pos = ix; + lib.panel.pos = ix; if (ix < this.tree.length && ix >= 0) { if (this.check_ix(this.tree[ix], x, y, true)) { this.clearSelected(); this.tree[ix].sel = true; this.getTreeSel(); this.load(this.sel_items, true, true, false, pl, false); - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } } } @@ -141,7 +141,7 @@ class Populate { } branch(br, base, node, block) { - if (!br || br.track || !lib.initialised || lib.list.Count != lib.libNode.length) return; + if (!br || br.track || !lib.lib.initialised || lib.lib.list.Count != lib.lib.libNode.length) return; const ix = this.showTracks ? 2 : 3; const l = base ? 0 : this.rootNode ? br.level : br.level + 1; if (base) node = false; @@ -150,28 +150,28 @@ class Populate { let n_o = '#get_branch#'; let nU = ''; this.range(br.item).forEach(v => { - n = lib.node[v][l]; + n = lib.lib.node[v][l]; nU = n.toUpperCase(); if (n_o != nU) { n_o = nU; - if (panel.multiPrefix) n = lib.prefixes(n); + if (lib.panel.multiPrefix) n = lib.lib.prefixes(n); br.child[i] = { nm: n, sel: false, child: [], - track: l > lib.node[v].length - ix, + track: l > lib.lib.node[v].length - ix, item: [v], - srt: lib.sort(n) + srt: lib.lib.sort(n) }; i++; } else br.child[i - 1].item.push(v); }); this.condense(br.child); - this.buildTree(lib.root, 0, node, true, block); + this.buildTree(lib.lib.root, 0, node, true, block); } branchChange(br) { - const arr = br.level == 0 ? lib.root : this.tree[br.par].child; + const arr = br.level == 0 ? lib.lib.root : this.tree[br.par].child; this.childCount = 0; this.getChildCount(arr, br.ix); arr.forEach(v => v.child = []); @@ -179,7 +179,7 @@ class Populate { } branchCount(br, base, node, block, key, type) { - if (!br || !lib.node.length) return; + if (!br || !lib.lib.node.length) return; if (this.cache[type][key]) return this.cache[type][key].value; const l = base ? 0 : this.rootNode ? br.level : br.level + 1; const b = []; @@ -189,21 +189,21 @@ class Populate { if (base) node = false; const full = !!br.root; this.range(br.item).forEach(v => { - if (l < lib.node[v].length) { - n = lib.node[v][l]; + if (l < lib.lib.node[v].length) { + n = lib.lib.node[v][l]; nU = n.toUpperCase(); if (n_o != nU) { n_o = nU; - if (panel.multiPrefix) n = lib.prefixes(n); + if (lib.panel.multiPrefix) n = lib.lib.prefixes(n); b.push({ nm: n, - srt: lib.sort(n) + srt: lib.lib.sort(n) }); } } }); - if (!panel.multiProcess && (!node || node && !full)) this.merge(b, true); - if (panel.multiProcess) { + if (!lib.panel.multiProcess && (!node || node && !full)) this.merge(b, true); + if (lib.panel.multiProcess) { const multi_cond = []; const multi_obj = []; const multi_rem = []; @@ -221,7 +221,7 @@ class Populate { multi.forEach(w => { multi_obj.push({ nm: w.join(''), - srt: lib.sort(w.join('')) + srt: lib.lib.sort(w.join('')) }); }); } @@ -267,7 +267,7 @@ class Populate { let i = 0; let j = 0; if (!br[0].sorted) { - switch (panel.multiProcess) { + switch (lib.panel.multiProcess) { case false: if (!node || node && !full) this.merge(br); break; @@ -282,7 +282,7 @@ class Populate { let n_o = '#condense#'; let nU = ''; br.forEach((v, i) => { - if (v.nm.includes('@@') || v.nm.includes(panel.softSplitter)) { + if (v.nm.includes('@@') || v.nm.includes(lib.panel.softSplitter)) { multi = this.getAllCombinations(v.nm); multi_rem.push(i); multi.forEach(w => { @@ -290,7 +290,7 @@ class Populate { nm: w.join(''), item: this.copy(v.item), track: v.track, - srt: lib.sort(w.join('')) + srt: lib.lib.sort(w.join('')) }); }); } @@ -351,55 +351,55 @@ class Populate { item.level = level; item.par = par; switch (true) { - case ppt.facetView: + case libSet.facetView: if (!item.root) item.track = true; break; case l != -1 && !this.showTracks: this.range(item.item).some(v => { - if (lib.node[v] && (lib.node[v].length == l + 1 || lib.node[v].length == l + 2)) return item.track = true; + if (lib.lib.node[v] && (lib.lib.node[v].length == l + 1 || lib.lib.node[v].length == l + 2)) return item.track = true; }); break; - case l == 0 && lib.node[item.item[0].start] && lib.node[item.item[0].start].length == 1: + case l == 0 && lib.lib.node[item.item[0].start] && lib.lib.node[item.item[0].start].length == 1: item.track = true; break; } - if (ui.col.counts && (!item.track || !this.showTracks)) { - const str = `@!#${ui.col.counts}\`${this.highlight.text ? ui.col.text_h : ui.col.counts}\`${ui.col.textSel}@!#`; + if (lib.ui.col.counts && (!item.track || !this.showTracks)) { + const str = `@!#${lib.ui.col.counts}\`${this.highlight.text ? lib.ui.col.text_h : lib.ui.col.counts}\`${lib.ui.col.textSel}@!#`; if (!item.nm.endsWith(str)) item.nm += str; } - item.name = !panel.noDisplay ? item.nm : item.nm.replace(/#@#.*?#@#/g, ''); + item.name = !lib.panel.noDisplay ? item.nm : item.nm.replace(/#@#.*?#@#/g, ''); if (v.child.length > 0) this.buildTree(v.child, level + 1, node, !!item.root); }); - if (ui.style.squareNode && ui.col.line) { + if (lib.ui.style.squareNode && lib.ui.col.line) { this.row.lineMax = []; this.tree.forEach(v => { const depth = !this.inlineRoot ? v.level : Math.max(v.level - 1, 0) this.row.lineMax[depth] = v.ix }); } - if (this.rootNode == 3) this.tree[0].name = this.tree[0].child.length > 1 ? panel.rootName.replace('#^^^^#', this.tree[0].child.length) : panel.rootName1; - find.initials = null; + if (this.rootNode == 3) this.tree[0].name = this.tree[0].child.length > 1 ? lib.panel.rootName.replace('#^^^^#', this.tree[0].child.length) : lib.panel.rootName1; + lib.find.initials = null; if (!block) { - sbar.setRows(this.tree.length); - panel.treePaint(); + lib.sbar.setRows(this.tree.length); + lib.panel.treePaint(); } } butTooltipFont() { - return [fontDefault, 15 * $Lib.scale * ppt.zoomTooltipBut / 100, 0]; + return [grFont.fontDefault, 15 * $Lib.scale * libSet.zoomTooltipBut / 100, 0]; } calcStatistics(v) { const key = `stat${this.getKey(v)}`; - const type = panel.search.txt ? 'search' : ppt.filterBy ? 'filter' : 'standard'; + const type = lib.panel.search.txt ? 'search' : libSet.filterBy ? 'filter' : 'standard'; if (this.cache[type][key]) return this.cache[type][key].value; const handleList = new FbMetadbHandleList(); let items = []; this.addItems(items, v.item); items = [...new Set(items)].sort(this.numSort) items.some(w => { - if (w >= panel.list.Count) return true; - handleList.Add(panel.list[w]); + if (w >= lib.panel.list.Count) return true; + handleList.Add(lib.panel.list[w]); }); let date = ''; let dates; @@ -409,7 +409,7 @@ class Populate { let tf; let value; let values; - switch (ppt.itemShowStatistics) { + switch (libSet.itemShowStatistics) { case 1: // bitrate values = this.tf.bitrate.EvalWithMetadbs(handleList); if (values.length == 1) { @@ -423,7 +423,7 @@ class Populate { lengths = lengths.map(v => parseFloat(v)).reduce((a, b) => a + b, 0); value = Number(Math.round(totals / lengths)) || ''; } - if (panel.imgView && value) value = `${value} kbps`; + if (lib.panel.imgView && value) value = `${value} kbps`; this.cache[type][key] = { value, items @@ -455,7 +455,7 @@ class Populate { } case 4: // rating case 5: // popularity - tf = ppt.itemShowStatistics == 4 ? this.tf.rating : this.tf.popularity; + tf = libSet.itemShowStatistics == 4 ? this.tf.rating : this.tf.popularity; values = tf.EvalWithMetadbs(handleList); values = this.getNumbers(values); values = values.filter(Boolean) @@ -463,7 +463,7 @@ class Populate { if (!ln) return ''; values = values.map(v => parseFloat(v)).reduce((a, b) => a + b, 0); value = Math.ceil(values / ln); - if (panel.imgView && this.label) value = (ppt.itemShowStatistics == 4 ? 'Rating ' : 'Popularity ') + value; + if (lib.panel.imgView && this.label) value = (libSet.itemShowStatistics == 4 ? 'Rating ' : 'Popularity ') + value; this.cache[type][key] = { value, items @@ -474,9 +474,9 @@ class Populate { case 10: // lastPlayed case 11: // added tf = - ppt.itemShowStatistics == 6 ? this.tf.date : - ppt.itemShowStatistics == 9 ? this.tf.firstPlayed : - ppt.itemShowStatistics == 10 ? this.tf.lastPlayed : + libSet.itemShowStatistics == 6 ? this.tf.date : + libSet.itemShowStatistics == 9 ? this.tf.firstPlayed : + libSet.itemShowStatistics == 10 ? this.tf.lastPlayed : this.tf.added; dates = tf.EvalWithMetadbs(handleList); dates = dates.filter(v => v !== ''); @@ -484,13 +484,13 @@ class Populate { if (ln) { if (ln == 1) date = dates[0]; else { - date = ppt.itemShowStatistics == 6 || ppt.itemShowStatistics == 9 || ppt.itemShowStatistics == 11 ? + date = libSet.itemShowStatistics == 6 || libSet.itemShowStatistics == 9 || libSet.itemShowStatistics == 11 ? dates.reduce((pre, cur) => Date.parse(pre) > Date.parse(cur) ? cur : pre) : dates.reduce((pre, cur) => Date.parse(cur) > Date.parse(pre) ? cur : pre) } } if (!date) return ''; - if (panel.imgView && this.label) date = ['', '', '', '', '', '', (v.root ? 'First release ' : ''), '', '', 'First played ', 'Last played ', 'Added '][ppt.itemShowStatistics] + date; + if (lib.panel.imgView && this.label) date = ['', '', '', '', '', '', (v.root ? 'First release ' : ''), '', '', 'First played ', 'Last played ', 'Added '][libSet.itemShowStatistics] + date; this.cache[type][key] = { value: date, items @@ -513,7 +513,7 @@ class Populate { } } if (!index) return ''; - if (panel.imgView && this.label) index = `Queue ${index}`; + if (lib.panel.imgView && this.label) index = `Queue ${index}`; this.cache[type][key] = { value: index, items @@ -530,7 +530,7 @@ class Populate { playcount = this.getNumbers(n); if (!playcount.length) return ''; playcount = n.map(v => parseInt(v)).reduce((a, b) => a + b, 0); - if (panel.imgView) playcount = `${(this.label ? 'Played ' : '') + playcount}x`; + if (lib.panel.imgView) playcount = `${(this.label ? 'Played ' : '') + playcount}x`; this.cache[type][key] = { value: playcount, items @@ -541,27 +541,27 @@ class Populate { } checkAutoHeight() { - if (panel.pn_h_auto && !panel.imgView && ppt.pn_h == ppt.pn_h_min && this.tree[0]) this.clearChild(this.tree[0]); + if (lib.panel.pn_h_auto && !lib.panel.imgView && libSet.pn_h == libSet.pn_h_min && this.tree[0]) this.clearChild(this.tree[0]); } check_ix(br, x, y, type) { - if (panel.imgView) return true; + if (lib.panel.imgView) return true; if (!br) return false; - x -= ui.x; + x -= lib.ui.x; const level = !this.inlineRoot ? br.level : Math.max(br.level - 1, 0); - const icon_w = this.inlineRoot && br.ix == 0 ? 0 : ui.icon.w + (!this.fullLineSelection ? ui.l.wf : 0); - return type ? (x >= Math.round(this.treeIndent * level + ui.sz.margin) && x < Math.round(this.treeIndent * level + ui.sz.margin) + br.w + icon_w) : - (x >= Math.round(this.treeIndent * level + ui.sz.margin) + icon_w) && x < Math.min(Math.round(this.treeIndent * level + ui.sz.margin) + icon_w + br.w, panel.tree.w); + const icon_w = this.inlineRoot && br.ix == 0 ? 0 : lib.ui.icon.w + (!this.fullLineSelection ? lib.ui.l.wf : 0); + return type ? (x >= Math.round(this.treeIndent * level + lib.ui.sz.margin) && x < Math.round(this.treeIndent * level + lib.ui.sz.margin) + br.w + icon_w) : + (x >= Math.round(this.treeIndent * level + lib.ui.sz.margin) + icon_w) && x < Math.min(Math.round(this.treeIndent * level + lib.ui.sz.margin) + icon_w + br.w, lib.panel.tree.w); } checkNode(gr) { - if (sbar.draw_timer || this.nodeStyle != 7) return; + if (lib.sbar.draw_timer || this.nodeStyle != 7) return; try { - ui.style.symb.SetPartAndStateID(2, 1); - ui.style.symb.SetPartAndStateID(2, 2); - ui.style.symb.DrawThemeBackground(gr, -ui.sz.node, -ui.sz.node, ui.sz.node, ui.sz.node); + lib.ui.style.symb.SetPartAndStateID(2, 1); + lib.ui.style.symb.SetPartAndStateID(2, 2); + lib.ui.style.symb.DrawThemeBackground(gr, -lib.ui.sz.node, -lib.ui.sz.node, lib.ui.sz.node, lib.ui.sz.node); } catch (e) { - ppt.nodeStyle = 0; + libSet.nodeStyle = 0; this.nodeStyle = 0; } } @@ -570,68 +570,68 @@ class Populate { this.m.br = -1; const im = this.get_ix(x, y, true, false); if (im >= this.tree.length || im < 0) return -1; - if (panel.imgView) return im; + if (lib.panel.imgView) return im; const item = this.tree[im]; const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); - if (x < Math.round(this.treeIndent * level) + ui.icon.w + ui.sz.margin + ui.x + ui.w && (!item.track || item.root)) this.m.br = im; + if (x < Math.round(this.treeIndent * level) + lib.ui.icon.w + lib.ui.sz.margin + lib.ui.x + lib.ui.w && (!item.track || item.root)) this.m.br = im; return im; } check_tooltip(ix, x, y) { - if (this.lbtnDn || sbar.draw_timer) return; + if (this.lbtnDn || lib.sbar.draw_timer) return; const item = this.tree[ix]; let text = ''; if (!item) return; switch (true) { - case !panel.imgView: { - const trace1 = item.tt && item.tt.needed && x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y && y <= item.tt.y + ui.row.h; - const trace2 = item.stats_tt && item.stats_tt.needed && x >= item.stats_tt.x + item.stats_tt.w && x <= ui.w - ui.sz.marginRight && y >= item.stats_tt.y && y <= item.stats_tt.y + ui.row.h * 0.9; + case !lib.panel.imgView: { + const trace1 = item.tt && item.tt.needed && x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y && y <= item.tt.y + lib.ui.row.h; + const trace2 = item.stats_tt && item.stats_tt.needed && x >= item.stats_tt.x + item.stats_tt.w && x <= lib.ui.w - lib.ui.sz.marginRight && y >= item.stats_tt.y && y <= item.stats_tt.y + lib.ui.row.h * 0.9; if (trace2) { text = this.statisticsShow ? (item.statistics !== undefined ? `${this.statistics[this.statisticsShow]}: ${item.statistics}` : '') : (item.count ? `${['', 'Tracks', 'Items'][this.nodeCounts]}:${item.count}` : ''); } else if (trace1) { - text = (!panel.colMarker ? item.name : item.name.replace(/@!#.*?@!#/g, '')) + (!this.countsRight || this.statisticsShow ? item.count : ''); + text = (!lib.panel.colMarker ? item.name : item.name.replace(/@!#.*?@!#/g, '')) + (!this.countsRight || this.statisticsShow ? item.count : ''); text = text.replace(/&/g, '&&'); } - if (text != tooltipLib.Text) this.deactivateTooltip(); + if (text != libTooltip.Text) this.deactivateTooltip(); if (!trace1 && !trace2 || !item.tt && !item.stats_tt) { this.deactivateTooltip(); return; } break; } - case panel.imgView: { + case lib.panel.imgView: { let trace1 = false; let trace2 = false; let trace3 = false; - if (!img.labels.hide) { + if (!libImg.labels.hide) { if (!item.tt) { this.deactivateTooltip(); return; } - trace1 = x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y1 && y <= item.tt.y1 + img.text.h; - trace2 = item.tt.y2 == -1 ? false : x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y2 && y <= item.tt.y2 + img.text.h; - trace3 = item.tt.y3 == -1 ? false : x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y3 && y <= item.tt.y3 + img.text.h; + trace1 = x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y1 && y <= item.tt.y1 + libImg.text.h; + trace2 = item.tt.y2 == -1 ? false : x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y2 && y <= item.tt.y2 + libImg.text.h; + trace3 = item.tt.y3 == -1 ? false : x >= item.tt.x && x <= item.tt.x + item.tt.w && y >= item.tt.y3 && y <= item.tt.y3 + libImg.text.h; text = trace1 || trace2 || trace3 ? item.tt.text : ''; - if (panel.colMarker) text = text.replace(/@!#.*?@!#/g, ''); + if (lib.panel.colMarker) text = text.replace(/@!#.*?@!#/g, ''); text = text.replace(/&/g, '&&'); - if (text != tooltipLib.Text) this.deactivateTooltip(); + if (text != libTooltip.Text) this.deactivateTooltip(); if (!trace1 && !trace2 && !trace3 || !item.tt[1] && !item.tt[2] && !item.tt[3]) { this.deactivateTooltip(); return; } } else { - text = panel.lines == 2 ? !ppt.albumArtFlipLabels ? `${item.grp}\n${item.lot}` : `${item.lot}\n${item.grp}` : item.grp; - if (panel.colMarker) text = text.replace(/@!#.*?@!#/g, ''); + text = lib.panel.lines == 2 ? !libSet.albumArtFlipLabels ? `${item.grp}\n${item.lot}` : `${item.lot}\n${item.grp}` : item.grp; + if (lib.panel.colMarker) text = text.replace(/@!#.*?@!#/g, ''); text = text.replace(/&/g, '&&'); - if (text != tooltipLib.Text) this.deactivateTooltip(); + if (text != libTooltip.Text) this.deactivateTooltip(); } break; } } this.activateTooltip(text); - timer.tooltipLib(); + lib.timer.tooltipLib(); } checkTooltip(item, x, y, txt_w, w) { @@ -644,7 +644,7 @@ class Populate { item.stats_tt = { needed: !this.tooltipStatistics || !this.statisticsShow || item.root ? false : [false, true, false, true, true, true, true, true, true, true, true, true][this.statisticsShow] && item.statistics !== undefined, x, - y: y + ui.row.h * 0.1, + y: y + lib.ui.row.h * 0.1, w }; } @@ -664,7 +664,7 @@ class Populate { break; } } - tooltipLib.SetFont(this.cur[0], this.cur[1], this.cur[2]); + libTooltip.SetFont(this.cur[0], this.cur[1], this.cur[2]); } clearSelected() { @@ -672,24 +672,24 @@ class Populate { } clearTree() { - if (panel.imgView && this.tree.length) img.trimCache(this.tree[0].key); + if (lib.panel.imgView && this.tree.length) libImg.trimCache(this.tree[0].key); this.tree = []; } clearChild(br) { br.child = []; - this.buildTree(lib.root, 0, true, true); + this.buildTree(lib.lib.root, 0, true, true); } clickedOn(x, y, item) { - if (panel.imgView) return 'text'; + if (lib.panel.imgView) return 'text'; if (this.inlineRoot && item.ix == 0) return this.check_ix(item, x, y, false) ? 'text' : 'none'; const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); - return x < ui.x + Math.round(this.treeIndent * level) + ui.icon.w + ui.sz.margin ? 'node' : this.check_ix(item, x, y, false) ? 'text' : 'none'; + return x < lib.ui.x + Math.round(this.treeIndent * level) + lib.ui.icon.w + lib.ui.sz.margin ? 'node' : this.check_ix(item, x, y, false) ? 'text' : 'none'; } collapseAll() { - let ic = this.get_ix(ui.x, ui.y + panel.tree.y + ui.row.h / 2, true, false); + let ic = this.get_ix(lib.ui.x, lib.ui.y + lib.panel.tree.y + lib.ui.row.h / 2, true, false); if (ic >= this.tree.length || ic < 0) return; let j = this.tree[ic].level; if (this.rootNode) j -= 1; @@ -705,19 +705,19 @@ class Populate { this.tree.forEach(v => { if (!v.root) v.child = []; }); - this.buildTree(lib.root, 0); + this.buildTree(lib.lib.root, 0); let scr_pos = false; this.tree.some((v, i) => { if (v.srt[0].toUpperCase() == nm) { - sbar.checkScroll(i * ui.row.h); + lib.sbar.checkScroll(i * lib.ui.row.h); return scr_pos = true; } }); if (!scr_pos) { - sbar.reset(); - panel.treePaint(); + lib.sbar.reset(); + lib.panel.treePaint(); } - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } condense(child) { @@ -732,9 +732,9 @@ class Populate { } createImages() { - if (!ui.w || !ui.h) return; + if (!lib.ui.w || !lib.ui.h) return; if (!this.nodeStyle) { - const sz = ui.sz.node; + const sz = lib.ui.sz.node; const ln_w = Math.max(Math.floor(sz / 9), 1); let plus = true; let hot = false; @@ -746,44 +746,44 @@ class Populate { this.nd[j] = $Lib.gr(sz, sz, true, g => { hot = j > 1; plus = !j || j == 2; - if (pref.libraryDesign !== 'reborn') { + if (grSet.libraryDesign !== 'reborn') { g.FillSolidRect(x, y, sz, sz, RGB(145, 145, 145)); - if (!hot) g.FillGradRect(x + ln_w, y + ln_w, sz - ln_w * 2, sz - ln_w * 2, 91, plus ? ui.col.icon_e[0] : ui.col.icon_c[0], plus ? ui.col.icon_e[1] : ui.col.icon_c[1], 1.0); - else g.FillGradRect(x + ln_w, y + ln_w, sz - ln_w * 2, sz - ln_w * 2, 91, ui.col.icon_h[0], ui.col.icon_h[1], 1.0); - const x_o = [x, x + sz - ln_w, x, x + sz - ln_w]; - const y_o = [y, y, y + sz - ln_w, y + sz - ln_w]; + if (!hot) g.FillGradRect(x + ln_w, y + ln_w, sz - ln_w * 2, sz - ln_w * 2, 91, plus ? lib.ui.col.icon_e[0] : lib.ui.col.icon_c[0], plus ? lib.ui.col.icon_e[1] : lib.ui.col.icon_c[1], 1.0); + else g.FillGradRect(x + ln_w, y + ln_w, sz - ln_w * 2, sz - ln_w * 2, 91, lib.ui.col.icon_h[0], lib.ui.col.icon_h[1], 1.0); + // const x_o = [x, x + sz - ln_w, x, x + sz - ln_w]; + // const y_o = [y, y, y + sz - ln_w, y + sz - ln_w]; for (let i = 0; i < 4; i++) { // g.FillSolidRect(x_o[i], y_o[i], ln_w, ln_w, RGB(186, 187, 188)); - if (pref.libraryDesign === 'traditional') g.FillSolidRect(x, y, sz, sz, ui.col.iconPlusBg); + if (grSet.libraryDesign === 'traditional') g.FillSolidRect(x, y, sz, sz, lib.ui.col.iconPlusBg); } - } else if (ppt.nodeStyle === 0) { - g.DrawRect(x, y, sz - 1, sz - 1, 1, ui.col.iconPlusBg); + } else if (libSet.nodeStyle === 0) { + g.DrawRect(x, y, sz - 1, sz - 1, 1, lib.ui.col.iconPlusBg); } - if (plus) g.FillSolidRect(Math.floor(x + (sz - sy_w) / 2), y + ln_w + Math.min(ln_w, sy_w), sy_w, sz - ln_w * 2 - Math.min(ln_w, sy_w) * 2, !hot ? ui.col.iconPlus : ui.col.iconPlus_h); - g.FillSolidRect(x + ln_w + Math.min(ln_w, sy_w), Math.floor(y + (sz - sy_w) / 2), sz - ln_w * 2 - Math.min(ln_w, sy_w) * 2, sy_w, !hot ? (plus ? ui.col.iconMinus_e : ui.col.iconMinus_c) : ui.col.iconMinus_h); + if (plus) g.FillSolidRect(Math.floor(x + (sz - sy_w) / 2), y + ln_w + Math.min(ln_w, sy_w), sy_w, sz - ln_w * 2 - Math.min(ln_w, sy_w) * 2, !hot ? lib.ui.col.iconPlus : lib.ui.col.iconPlus_h); + g.FillSolidRect(x + ln_w + Math.min(ln_w, sy_w), Math.floor(y + (sz - sy_w) / 2), sz - ln_w * 2 - Math.min(ln_w, sy_w) * 2, sy_w, !hot ? (plus ? lib.ui.col.iconMinus_e : lib.ui.col.iconMinus_c) : lib.ui.col.iconMinus_h); }); } } else { - let lightCol = ui.isLightCol(ui.col.icon_h); + let lightCol = lib.ui.isLightCol(lib.ui.col.icon_h); $Lib.gr(1, 1, false, g => { - const h = this.nodeStyle != 7 ? g.CalcTextHeight('String', ui.icon.font) / 15 : g.CalcTextHeight('String', ui.font.main) / 20; - this.sy_sz = Math.floor(Math.max(8 * ppt.zoomNode / 100 * h, 5)); + const h = this.nodeStyle != 7 ? g.CalcTextHeight('String', lib.ui.icon.font) / 15 : g.CalcTextHeight('String', lib.ui.font.main) / 20; + this.sy_sz = Math.floor(Math.max(8 * libSet.zoomNode / 100 * h, 5)); }); const sz = Math.max(Math.round(this.sy_sz * 1.666667), 1); this.triangle.highlight = $Lib.gr(sz, sz, true, g => { g.SetSmoothingMode(4); - g.FillPolygon(ui.col.icon_h, 1, [sz, 0, sz, sz, 0, sz]); + g.FillPolygon(lib.ui.col.icon_h, 1, [sz, 0, sz, sz, 0, sz]); g.SetSmoothingMode(0); }); - lightCol = ui.isLightCol(ui.col.icon_e); + lightCol = lib.ui.isLightCol(lib.ui.col.icon_e); this.triangle.expand = $Lib.gr(sz, sz, true, g => { g.SetSmoothingMode(4); - g.FillPolygon(ui.col.icon_e & (lightCol ? 0xC0ffffff : 0xBAffffff), 1, [sz, 0, sz, sz, 0, sz]); + g.FillPolygon(lib.ui.col.icon_e & (lightCol ? 0xC0ffffff : 0xBAffffff), 1, [sz, 0, sz, sz, 0, sz]); g.SetSmoothingMode(0); }); this.triangle.select = $Lib.gr(sz, sz, true, g => { g.SetSmoothingMode(4); - g.FillPolygon(ui.col.textSel & (lightCol ? 0xC0ffffff : 0xBAffffff), 1, [sz, 0, sz, sz, 0, sz]); + g.FillPolygon(lib.ui.col.textSel & (lightCol ? 0xC0ffffff : 0xBAffffff), 1, [sz, 0, sz, sz, 0, sz]); g.SetSmoothingMode(0); }); } @@ -840,7 +840,7 @@ class Populate { text[i + 2] = ''; ellipsis_corr = ellipsisSpace; } - col[i] = i > 0 ? (text[i - 1]).split('`') : (!panel.imgView || !img.labels.overlayDark ? ui.col.txtArr : [RGB(240, 240, 240), ui.col.text_h, ui.col.text]); + col[i] = i > 0 ? (text[i - 1]).split('`') : (!lib.panel.imgView || !libImg.labels.overlayDark ? lib.ui.col.txtArr : [RGB(240, 240, 240), lib.ui.col.text_h, lib.ui.col.text]); col_x[i] = x; col_w[i] = w - x - ellipsis_corr > ellipsis_corr ? w - x - ellipsis_corr : w - x; x += w_arr[i]; @@ -857,29 +857,29 @@ class Populate { } text.forEach((v, i) => { if (i % 2 == 0 && text[i]) { - gr.GdiDrawText(text[i], font, !np ? col[i][type] : ui.col.nowp, item_x + col_x[i], item_y, col_w[i], h, panel.lc); + gr.GdiDrawText(text[i], font, !np ? col[i][type] : lib.ui.col.nowp, item_x + col_x[i], item_y, col_w[i], h, lib.panel.lc); } }); } deactivateTooltip() { - if (!tooltipLib.Text && !pref.showStyledTooltips || but.trace) return; - tooltipLib.Text = ''; - but.tooltipLib.delay = false; - styledTooltipReady = false; - tooltipLib.Deactivate(); + if (!libTooltip.Text && !grSet.showStyledTooltips || lib.but.trace) return; + libTooltip.Text = ''; + lib.but.tooltipLib.delay = false; + grm.ui.styledTooltipReady = false; + libTooltip.Deactivate(); } dragDrop(x, y) { - x -= ui.x; y -= ui.y; + x -= lib.ui.x; y -= lib.ui.y; if (!this.lbtnDn) return; - const drag_diff = !ppt.touchControl ? Math.sqrt((Math.pow(this.last_pressed_coord.x - x, 2) + Math.pow(this.last_pressed_coord.y - y, 2))) : Math.abs(x - this.last_pressed_coord.x); + const drag_diff = !libSet.touchControl ? Math.sqrt((Math.pow(this.last_pressed_coord.x - x, 2) + Math.pow(this.last_pressed_coord.y - y, 2))) : Math.abs(x - this.last_pressed_coord.x); if (drag_diff > 7) { - if (ppt.touchControl) { + if (libSet.touchControl) { const ix = this.get_ix(x, y, true, false); const item = this.tree[ix]; - if (ui.id.dragDrop != ix || ix >= this.tree.length || ix < 0) return; - if (!item.sel && !vk.k('ctrl')) this.setTreeSel(ix, item.sel); + if (lib.ui.id.dragDrop != ix || ix >= this.tree.length || ix < 0) return; + if (!item.sel && !lib.vk.k('ctrl')) this.setTreeSel(ix, item.sel); } this.last_pressed_coord = { x: undefined, @@ -889,8 +889,8 @@ class Populate { const handleList = this.getHandleList('newItems'); this.sortIfNeeded(handleList); - if (displayLibrarySplit()) { // * Drag and drop action from Library to Playlist in split layout - librarySplitDragDrop(); + if (grm.ui.displayLibrarySplit()) { // * Drag and drop action from Library to Playlist in split layout + grm.ui.librarySplitDragDrop(); } else { fb.DoDragDrop(0, handleList, handleList.Count ? 1 | 4 : 0); } @@ -900,17 +900,17 @@ class Populate { } draw(gr) { // * Heavily modified - if (lib.empty) return gr.DrawString(lib.empty, ui.font.main, ui.col.text, ui.x, wh * 0.5 - ui.y - panel.search.h * 0.5, panel.tree.w, ui.row.h * 3, g_string_format.align_center); - if (!this.tree.length || !panel.draw) return gr.GdiDrawText(this.libItems && !panel.search.txt && !ppt.filterBy && ppt.libSource ? 'Loading...\n\n' : lib.none, ui.font.main, ui.col.text, ui.x + ui.sz.margin, ui.y + panel.search.h, panel.tree.w, ui.row.h * 3); - if (panel.imgView) return; - const b = $Lib.clamp(Math.round(sbar.delta / ui.row.h + 0.4), 0, this.tree.length - 1); - const bar_x = this.nodeStyle && this.nodeStyle < 5 ? 0 : ui.sz.pad; - const f = Math.min(b + panel.rows, this.tree.length); + if (lib.lib.empty) return gr.DrawString(lib.lib.empty, lib.ui.font.main, lib.ui.col.text, lib.ui.x, grm.ui.wh * 0.5 - lib.ui.y - lib.panel.search.h * 0.5, lib.panel.tree.w, lib.ui.row.h * 3, Stringformat.align_center); + if (!this.tree.length || !lib.panel.draw) return gr.GdiDrawText(this.libItems && !lib.panel.search.txt && !libSet.filterBy && libSet.libSource ? 'Loading...\n\n' : lib.lib.none, lib.ui.font.main, lib.ui.col.text, lib.ui.x + lib.ui.sz.margin, lib.ui.y + lib.panel.search.h, lib.panel.tree.w, lib.ui.row.h * 3); + if (lib.panel.imgView) return; + const b = $Lib.clamp(Math.round(lib.sbar.delta / lib.ui.row.h + 0.4), 0, this.tree.length - 1); + // const bar_x = this.nodeStyle && this.nodeStyle < 5 ? 0 : lib.ui.sz.pad; + const f = Math.min(b + lib.panel.rows, this.tree.length); const nm = []; const nowp_c = []; - const updatedNowpBg = g_pl_colors.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing + const updatedNowpBg = pl.col.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing const row = []; - const y1 = Math.round(panel.search.h - sbar.delta + panel.node_y) + Math.floor(ui.sz.node / 2); + const y1 = Math.round(lib.panel.search.h - lib.sbar.delta + lib.panel.node_y) + Math.floor(lib.ui.sz.node / 2); let i = 0; let item_x = 0; let item_y = 0; @@ -918,9 +918,9 @@ class Populate { let sel_w = 0; let level = 0; this.checkNode(gr); - if (!ui.style.squareNode) gr.SetTextRenderingHint(5); + if (!lib.ui.style.squareNode) gr.SetTextRenderingHint(5); this.rows = 0; - if (ui.style.squareNode && ui.col.line) { + if (lib.ui.style.squareNode && lib.ui.col.line) { level = !this.inlineRoot ? this.tree[b].level : Math.max(this.tree[b].level - 1, 0); for (let j = 0; j <= level; j++) row[j] = b; } @@ -930,14 +930,14 @@ class Populate { nm[i] = item.name + (i || this.rootNode != 3 || this.nodeCounts == 1 && (this.countsRight || this.statisticsShow) ? (!this.countsRight || this.statisticsShow ? item.count : '') : ''); const counts = !this.statisticsShow ? item.count : item.statistics; if (this.highlight.nowPlayingShow && !item.root && this.inRange(this.nowp, item.item)) nowp_c.push(i); - item.np = nowp_c.includes(i) || panel.textDiffHighlight && this.m.i == i ? '\u266B ' : ''; - if (item.np && item.id != this.id) this.row.note_w = gr.CalcTextWidth(item.np, ui.font.main); + item.np = nowp_c.includes(i) || lib.panel.textDiffHighlight && this.m.i == i ? '\u266B ' : ''; + if (item.np && item.id != this.id) this.row.note_w = gr.CalcTextWidth(item.np, lib.ui.font.main); if (item.id != this.id || this.highlight.nowPlayingIndicator) { - let itemName = !panel.colMarker ? nm[i] : nm[i].replace(/@!#.*?@!#/g, ''); + let itemName = !lib.panel.colMarker ? nm[i] : nm[i].replace(/@!#.*?@!#/g, ''); if (item.np && this.highlight.nowPlayingIndicator && item.track) itemName = item.np + itemName; - item.name_w = gr.CalcTextWidth(itemName, ui.font.main, true); + item.name_w = gr.CalcTextWidth(itemName, lib.ui.font.main, true); item.count_w = this.nodeCounts && this.countsRight || this.statisticsShow ? - gr.CalcTextWidth(counts || '000', ui.font.small) + (counts ? ui.row.h * 0.2 : 0) : 0; + gr.CalcTextWidth(counts || '000', lib.ui.font.small) + (counts ? lib.ui.row.h * 0.2 : 0) : 0; if (!this.fullLineSelection) { item.w = item.name_w; item.id = this.id; @@ -946,98 +946,98 @@ class Populate { level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); if (this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item)) nowp_c.push(i); - item_y = Math.round(ui.y + ui.row.h * i + panel.search.h - sbar.delta); - if (item_y < panel.filter.y) { + item_y = Math.round(lib.ui.y + lib.ui.row.h * i + lib.panel.search.h - lib.sbar.delta); + if (item_y < lib.panel.filter.y) { this.rows++; - if ((item.sel || this.highlight.nowPlaying) && (ui.col.bgSel != 0 || col.primary != 0)) { - const icon_w = !this.inlineRoot || i ? ui.icon.w : 0; - item_x = Math.round(ui.x + this.treeIndent * level + ui.sz.margin) + icon_w; - sel_x = Math.round(item_x - ui.sz.sel); - if (this.inlineRoot && !i) sel_x = Math.max(sel_x - ui.sz.sel, 0); - sel_w = Math.min(item.name_w + ui.sz.sel * 2, ui.x + panel.tree.w - sel_x - item.count_w - 1); + if ((item.sel || this.highlight.nowPlaying) && (lib.ui.col.bgSel != 0 || grCol.primary != 0)) { + const icon_w = !this.inlineRoot || i ? lib.ui.icon.w : 0; + item_x = Math.round(lib.ui.x + this.treeIndent * level + lib.ui.sz.margin) + icon_w; + sel_x = Math.round(item_x - lib.ui.sz.sel); + if (this.inlineRoot && !i) sel_x = Math.max(sel_x - lib.ui.sz.sel, 0); + sel_w = Math.min(item.name_w + lib.ui.sz.sel * 2, lib.ui.x + lib.panel.tree.w - sel_x - item.count_w - 1); if (this.fullLineSelection) { - sel_x = ui.x; - sel_w = sbar.w ? ui.w - SCALE(42) : ui.w + 1; + sel_x = lib.ui.x; + sel_w = lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w + 1; } if (!nowp_c.includes(i)) { - if (this.fullLineSelection && this.sbarShow === 1 && ui.sbar.type === 2 && (this.highlight.row || !this.fullLineSelection)) { - gr.FillSolidRect(sel_x, item_y, sel_w + ui.l.w, ui.row.h, ui.col.bgSel); - gr.FillSolidRect(sel_x, item_y, sel_w + ui.l.w, ui.l.w, ui.col.bgSelframe); - gr.FillSolidRect(sel_x, item_y + ui.row.h, sel_w + ui.l.w, ui.l.w, ui.col.bgSelframe); + if (this.fullLineSelection && this.sbarShow === 1 && lib.ui.sbar.type === 2 && (this.highlight.row || !this.fullLineSelection)) { + gr.FillSolidRect(sel_x, item_y, sel_w + lib.ui.l.w, lib.ui.row.h, lib.ui.col.bgSel); + gr.FillSolidRect(sel_x, item_y, sel_w + lib.ui.l.w, lib.ui.l.w, lib.ui.col.bgSelframe); + gr.FillSolidRect(sel_x, item_y + lib.ui.row.h, sel_w + lib.ui.l.w, lib.ui.l.w, lib.ui.col.bgSelframe); } } // * Now playing bg selection else if (this.highlight.nowPlaying && updatedNowpBg) { - gr.FillSolidRect(pref.libraryDesign === 'traditional' ? item_x - SCALE(2) : ui.x, item_y, pref.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - ui.sz.margin - ui.sz.node + ui.l.w : pref.libraryDesign === 'traditional' && !this.fullLineSelection ? sel_w + sel_x - item_x + SCALE(2) : !this.fullLineSelection ? sel_w + ui.sz.margin + sel_x - ui.x - ui.sz.sideMarker : sel_w, ui.row.h, ui.col.nowPlayingBg); + gr.FillSolidRect(grSet.libraryDesign === 'traditional' ? item_x - SCALE(2) : lib.ui.x, item_y, grSet.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - lib.ui.sz.margin - lib.ui.sz.node + lib.ui.l.w : grSet.libraryDesign === 'traditional' && !this.fullLineSelection ? sel_w + sel_x - item_x + SCALE(2) : !this.fullLineSelection ? sel_w + lib.ui.sz.margin + sel_x - lib.ui.x - lib.ui.sz.sideMarker : sel_w, lib.ui.row.h, lib.ui.col.nowPlayingBg); - if (pref.libraryDesign !== 'traditional') { - gr.FillSolidRect(ui.x, item_y, ui.sz.sideMarker, ui.row.h, ui.col.sideMarker); + if (grSet.libraryDesign !== 'traditional') { + gr.FillSolidRect(lib.ui.x, item_y, lib.ui.sz.sideMarker, lib.ui.row.h, lib.ui.col.sideMarker); } } // * Marker selection with now playing active if (item.sel && this.highlight.nowPlaying) { - if (pref.libraryDesign !== 'traditional') { - if (!this.inRange(this.nowp, item.item) || item.root) gr.DrawRect(this.fullLineSelection ? sel_x : ui.x, item_y, this.fullLineSelection ? sel_w - 1 : sel_w + ui.sz.margin + sel_x - ui.x - ui.sz.sideMarker, ui.row.h, 1, ui.col.selectionFrame); - gr.FillSolidRect(ui.x, this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) ? item_y + 1 : item_y, ui.sz.sideMarker, this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) ? ui.row.h - 1 : ui.row.h + 1, ui.col.sideMarker); + if (grSet.libraryDesign !== 'traditional') { + if (!this.inRange(this.nowp, item.item) || item.root) gr.DrawRect(this.fullLineSelection ? sel_x : lib.ui.x, item_y, this.fullLineSelection ? sel_w - 1 : sel_w + lib.ui.sz.margin + sel_x - lib.ui.x - lib.ui.sz.sideMarker, lib.ui.row.h, 1, lib.ui.col.selectionFrame); + gr.FillSolidRect(lib.ui.x, this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) ? item_y + 1 : item_y, lib.ui.sz.sideMarker, this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) ? lib.ui.row.h - 1 : lib.ui.row.h + 1, lib.ui.col.sideMarker); } - else if (pref.libraryDesign === 'traditional') { - gr.FillSolidRect(item_x - SCALE(2), item_y, pref.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - ui.sz.margin - ui.sz.node + ui.l.w : sel_w, ui.row.h, ui.col.nowPlayingBg); + else if (grSet.libraryDesign === 'traditional') { + gr.FillSolidRect(item_x - SCALE(2), item_y, grSet.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - lib.ui.sz.margin - lib.ui.sz.node + lib.ui.l.w : sel_w, lib.ui.row.h, lib.ui.col.nowPlayingBg); } } // * Marker selection with now playing deactivated if (item.sel && !this.highlight.nowPlaying && updatedNowpBg) { - gr.FillSolidRect(pref.libraryDesign === 'traditional' && this.fullLineSelection ? item_x - SCALE(2) : sel_x, item_y, pref.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - ui.sz.margin - ui.sz.node + ui.l.w : sel_w, ui.row.h, ui.col.nowPlayingBg); + gr.FillSolidRect(grSet.libraryDesign === 'traditional' && this.fullLineSelection ? item_x - SCALE(2) : sel_x, item_y, grSet.libraryDesign === 'traditional' && this.fullLineSelection ? sel_w - item_x - lib.ui.sz.margin - lib.ui.sz.node + lib.ui.l.w : sel_w, lib.ui.row.h, lib.ui.col.nowPlayingBg); - if (pref.libraryDesign !== 'traditional') { - gr.FillSolidRect(ui.x, item_y, ui.w, ui.row.h, ui.col.nowPlayingBg); - gr.FillSolidRect(ui.x, item_y, ui.sz.sideMarker, ui.row.h, ui.col.sideMarker); + if (grSet.libraryDesign !== 'traditional') { + gr.FillSolidRect(lib.ui.x, item_y, lib.ui.w, lib.ui.row.h, lib.ui.col.nowPlayingBg); + gr.FillSolidRect(lib.ui.x, item_y, lib.ui.sz.sideMarker, lib.ui.row.h, lib.ui.col.sideMarker); } } } if (this.rowStripes) { - if (i % 2 == 0) gr.FillSolidRect(ui.x, item_y + 1, panel.tree.stripe.w, ui.row.h - 2, ui.col.rowStripes /*ui.col.bg1*/); - else gr.FillSolidRect(ui.x, item_y, panel.tree.stripe.w, ui.row.h, ui.col.bg2); + if (i % 2 == 0) gr.FillSolidRect(lib.ui.x, item_y + 1, lib.panel.tree.stripe.w, lib.ui.row.h - 2, lib.ui.col.rowStripes /*ui.col.bg1*/); + else gr.FillSolidRect(lib.ui.x, item_y, lib.panel.tree.stripe.w, lib.ui.row.h, lib.ui.col.bg2); } } } for (i = b; i < f; i++) { const item = this.tree[i]; const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); - item_y = Math.round(ui.y + ui.row.h * i + panel.search.h - sbar.delta); - if (item_y < panel.filter.y) { - item_x = Math.round(ui.x + this.treeIndent * level + ui.sz.margin); - if (this.inlineRoot && !item.level) item_x = ui.x + ui.sz.marginSearch; + item_y = Math.round(lib.ui.y + lib.ui.row.h * i + lib.panel.search.h - lib.sbar.delta); + if (item_y < lib.panel.filter.y) { + item_x = Math.round(lib.ui.x + this.treeIndent * level + lib.ui.sz.margin); + if (this.inlineRoot && !item.level) item_x = lib.ui.x + lib.ui.sz.marginSearch; if ((this.fullLineSelection && this.row.i == i || this.m.i == i)) { - sel_x = Math.round(item_x - ui.sz.sel); - if (!this.inlineRoot || item.level) sel_x += ui.icon.w; - sel_w = Math.min(item.name_w + ui.sz.sel * 2, ui.x + panel.tree.w - sel_x - item.count_w - 1); + sel_x = Math.round(item_x - lib.ui.sz.sel); + if (!this.inlineRoot || item.level) sel_x += lib.ui.icon.w; + sel_w = Math.min(item.name_w + lib.ui.sz.sel * 2, lib.ui.x + lib.panel.tree.w - sel_x - item.count_w - 1); if (this.fullLineSelection) { - sel_x = ui.x; - sel_w = sbar.w ? ui.w - SCALE(42) : ui.w + 1; + sel_x = lib.ui.x; + sel_w = lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w + 1; } - if (this.highlight.row == 3 && (this.fullLineSelection && this.sbarShow == 1 && ui.sbar.type == 2)) { - gr.DrawLine(sel_x, item_y, sel_w, item_y, ui.l.w, ui.col.frame); - gr.DrawLine(sel_x, item_y + ui.row.h, sel_w, item_y + ui.row.h, ui.l.w, ui.col.frame); + if (this.highlight.row == 3 && (this.fullLineSelection && this.sbarShow == 1 && lib.ui.sbar.type == 2)) { + gr.DrawLine(sel_x, item_y, sel_w, item_y, lib.ui.l.w, lib.ui.col.frame); + gr.DrawLine(sel_x, item_y + lib.ui.row.h, sel_w, item_y + lib.ui.row.h, lib.ui.l.w, lib.ui.col.frame); } } - if (ui.style.squareNode && ui.col.line) { + if (lib.ui.style.squareNode && lib.ui.col.line) { if (item.top) row[level] = i; - const ff = sbar.rows_drawn == this.rows || i < sbar.rows_drawn ? f : f - 1; + const ff = lib.sbar.rows_drawn == this.rows || i < lib.sbar.rows_drawn ? f : f - 1; if (item.bot || i === ff - 1) { for (let depth = (i === ff - 1 ? 0 : level); depth <= level; depth++) { if (row[depth] !== undefined && (this.inlineRoot || !item.root)) { const start = row[depth]; let end = i + (item.bot && depth === level ? 0.5 : 1); - if (item_y >= panel.filter.y) end -= 1; - const l_x = Math.round(ui.x + this.treeIndent * depth + ui.sz.margin) + Math.floor(ui.sz.node / 2) - ui.l.wf; - let l_y = Math.round(ui.y + ui.row.h * start + panel.search.h - sbar.delta); - let l_h = Math.ceil(ui.row.h * (end - start)) + ui.l.wc; + if (item_y >= lib.panel.filter.y) end -= 1; + const l_x = Math.round(lib.ui.x + this.treeIndent * depth + lib.ui.sz.margin) + Math.floor(lib.ui.sz.node / 2) - lib.ui.l.wf; + let l_y = Math.round(lib.ui.y + lib.ui.row.h * start + lib.panel.search.h - lib.sbar.delta); + let l_h = Math.ceil(lib.ui.row.h * (end - start)) + lib.ui.l.wc; if (!start) { - l_y += ui.row.h / 2; - l_h -= ui.row.h / 2; + l_y += lib.ui.row.h / 2; + l_h -= lib.ui.row.h / 2; } - if (i <= this.row.lineMax[depth] && (!this.inlineRoot || item.level) && pref.libraryDesign === 'traditional') gr.FillSolidRect(l_x, l_y, ui.l.w, l_h, ui.col.line); + if (i <= this.row.lineMax[depth] && (!this.inlineRoot || item.level) && grSet.libraryDesign === 'traditional') gr.FillSolidRect(l_x, l_y, lib.ui.l.w, l_h, lib.ui.col.line); } } if (item.bot) row[level] = undefined; @@ -1049,46 +1049,46 @@ class Populate { for (i = b; i < f; i++) { const item = this.tree[i]; const level = !this.inlineRoot ? item.level : Math.max(item.level - 1, 0); - item_y = Math.round(ui.y + ui.row.h * i + panel.search.h - sbar.delta); - if (item_y < panel.filter.y) { - item_x = Math.round(ui.x + this.treeIndent * level + ui.sz.margin); - if (this.inlineRoot && !item.level) item_x = ui.x + ui.sz.marginSearch; - if (ui.style.squareNode) { + item_y = Math.round(lib.ui.y + lib.ui.row.h * i + lib.panel.search.h - lib.sbar.delta); + if (item_y < lib.panel.filter.y) { + item_x = Math.round(lib.ui.x + this.treeIndent * level + lib.ui.sz.margin); + if (this.inlineRoot && !item.level) item_x = lib.ui.x + lib.ui.sz.marginSearch; + if (lib.ui.style.squareNode) { if (!item.track && (!this.inlineRoot || item.level)) { - const y2 = ui.y + ui.row.h * i + y1 - ui.l.wf; - if (ui.col.line) if (pref.libraryDesign !== 'reborn') gr.FillSolidRect(item_x + ui.sz.node, y2, ui.l.s1, ui.l.w, ui.col.line); - this.drawNode(gr, item.child.length < 1 ? this.m.br != i ? 0 : 2 : this.m.br != i ? 1 : 3, item_x, item_y + panel.node_y); - } else if (ui.col.line && (!this.inlineRoot || item.level)) { - if (pref.libraryDesign !== 'reborn') { - const y2 = Math.round(ui.y + panel.search.h - sbar.delta) + Math.ceil(ui.row.h * (i + 0.5)) - ui.l.wf; - gr.FillSolidRect(item_x + ui.l.s2, y2, ui.l.s3, ui.l.w, ui.col.line); + const y2 = lib.ui.y + lib.ui.row.h * i + y1 - lib.ui.l.wf; + if (lib.ui.col.line) if (grSet.libraryDesign !== 'reborn') gr.FillSolidRect(item_x + lib.ui.sz.node, y2, lib.ui.l.s1, lib.ui.l.w, lib.ui.col.line); + this.drawNode(gr, item.child.length < 1 ? this.m.br != i ? 0 : 2 : this.m.br != i ? 1 : 3, item_x, item_y + lib.panel.node_y); + } else if (lib.ui.col.line && (!this.inlineRoot || item.level)) { + if (grSet.libraryDesign !== 'reborn') { + const y2 = Math.round(lib.ui.y + lib.panel.search.h - lib.sbar.delta) + Math.ceil(lib.ui.row.h * (i + 0.5)) - lib.ui.l.wf; + gr.FillSolidRect(item_x + lib.ui.l.s2, y2, lib.ui.l.s3, lib.ui.l.w, lib.ui.col.line); } } } else if (!item.track && (!this.inlineRoot || item.level)) this.drawNode(gr, item, item_x, item_y, item.child.length < 1, this.m.br == i, item.sel); - if (!this.inlineRoot || item.level) item_x += ui.icon.w + (!this.fullLineSelection ? ui.l.wf : 0); - const w = ui.x + panel.tree.w - item_x - ui.sz.sel - item.count_w; + if (!this.inlineRoot || item.level) item_x += lib.ui.icon.w + (!this.fullLineSelection ? lib.ui.l.wf : 0); + const w = lib.ui.x + lib.panel.tree.w - item_x - lib.ui.sz.sel - item.count_w; this.checkTooltip(item, item_x, item_y, item.name_w, w); if (this.fullLineSelection && item.id != this.id) { - item.w = ui.x + panel.tree.w - item_x - (RES_4K ? 45 : 25); + item.w = lib.ui.x + lib.panel.tree.w - item_x - (RES._4K ? 45 : 25); item.id = this.id; } if (item.np && this.highlight.nowPlayingSidemarker) { - gr.FillSolidRect(ui.l.w, item_y, ui.sz.sideMarker, ui.row.h, ui.col.nowp); + gr.FillSolidRect(lib.ui.l.w, item_y, lib.ui.sz.sideMarker, lib.ui.row.h, lib.ui.col.nowp); } if (item.np && this.highlight.nowPlayingIndicator && item.track) nm[i] = item.np + nm[i]; const np = item.np && this.highlight.nowPlaying; - const txt_co = np && !item.sel ? ui.col.nowp : item.sel && this.fullLineSelection ? (this.highlight.row ? ui.col.textSel : ui.col.text) : this.m.i == i && this.highlight.text ? ui.col.text_h : ui.col.counts || ui.col.count; + // const txt_co = np && !item.sel ? lib.ui.col.nowp : item.sel && this.fullLineSelection ? (this.highlight.row ? lib.ui.col.textSel : lib.ui.col.text) : this.m.i == i && this.highlight.text ? lib.ui.col.text_h : lib.ui.col.counts || lib.ui.col.count; const type = item.sel ? (this.highlight.row || !this.fullLineSelection ? 2 : 0) : this.m.i == i && this.highlight.text ? 1 : 0; - const txt_c = // np && !item.sel ? ui.col.nowp : ui.col.txtArr[type]; - this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) && updatedNowpBg ? ui.col.text_nowp : - item.sel ? ui.col.textSel : - this.m.i === i ? ui.col.text_h : ui.col.text; + const txt_c = // np && !item.sel ? lib.ui.col.nowp : lib.ui.col.txtArr[type]; + this.highlight.nowPlaying && !item.root && this.inRange(this.nowp, item.item) && updatedNowpBg ? lib.ui.col.text_nowp : + item.sel ? lib.ui.col.textSel : + this.m.i === i ? lib.ui.col.text_h : lib.ui.col.text; - !panel.colMarker ? gr.GdiDrawText(nm[i], ui.font.main, txt_c, item_x, item_y, w, ui.row.h, panel.lc) : this.cusCol(gr, nm[i], item, item_x, item_y, w, ui.row.h, type, np, ui.font.main, ui.font.mainEllipsisSpace, 'text'); + !lib.panel.colMarker ? gr.GdiDrawText(nm[i], lib.ui.font.main, txt_c, item_x, item_y, w, lib.ui.row.h, lib.panel.lc) : this.cusCol(gr, nm[i], item, item_x, item_y, w, lib.ui.row.h, type, np, lib.ui.font.main, lib.ui.font.mainEllipsisSpace, 'text'); if (this.countsRight || this.statisticsShow) { - const scrollbar = sbar.w === SCALE(12) && sbar.scrollable_lines > 0; - const x = panel.tree.w - item_x + (pref.libraryLayout === 'split' ? 0 : ui.x) - (scrollbar ? RES_4K ? 45 : 24 : 0); - gr.GdiDrawText(!this.statisticsShow ? item.count : item.statistics, !item.root || !this.label ? ui.font.small : ui.font.label, txt_c, item_x, item_y, x, ui.row.h, panel.rc); + const scrollbar = lib.sbar.w === SCALE(12) && lib.sbar.scrollable_lines > 0; + const x = lib.panel.tree.w - item_x + (grSet.libraryLayout === 'split' ? 0 : lib.ui.x) - (scrollbar ? RES._4K ? 45 : 24 : 0); + gr.GdiDrawText(!this.statisticsShow ? item.count : item.statistics, !item.root || !this.label ? lib.ui.font.small : lib.ui.font.label, txt_c, item_x, item_y, x, lib.ui.row.h, lib.panel.rc); } } } @@ -1101,7 +1101,7 @@ class Populate { if (ix >= this.tree.length || ix < 0) return; const itemtr = this.tree[ix]; const nowp = this.highlight.nowPlaying && !itemtr.root && this.inRange(this.nowp, itemtr.item); - const icon_c = nowp ? ui.col.text_nowp : hover ? ui.col.iconPlus_h : selCol ? ui.col.iconPlus_sel : ui.col.iconPlus; + const icon_c = nowp ? lib.ui.col.text_nowp : hover ? lib.ui.col.iconPlus_h : selCol ? lib.ui.col.iconPlus_sel : lib.ui.col.iconPlus; switch (this.nodeStyle) { case 0: // * Squares - Traditional design @@ -1112,33 +1112,33 @@ class Populate { break; case 1: case 2: // * Angles/Arrows - Modern design - if (!this.highlight.row && this.fullLineSelection) x += ui.l.w; + if (!this.highlight.row && this.fullLineSelection) x += lib.ui.l.w; if (parent) { if (hover) { - gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x + 1, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x + 1, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } else { - gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - if (this.nodeStyle === 1) gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x + 1, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + if (this.nodeStyle === 1) gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x + 1, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } } else { - gr.DrawString(ui.icon.collapse, ui.icon.font, icon_c, x - ui.icon.offset, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - gr.DrawString(ui.icon.collapse, ui.icon.font, icon_c, x - ui.icon.offset, y2 + 1, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2 + 1, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } break; case 3: case 4: { // * Triangle - Ultra-modern design - if (!this.highlight.row && this.fullLineSelection) x += ui.l.w; - const y3 = Math.round(y + (ui.row.h - this.sy_sz) / 2 - 2); + if (!this.highlight.row && this.fullLineSelection) x += lib.ui.l.w; + // const y3 = Math.round(y + (lib.ui.row.h - this.sy_sz) / 2 - 2); gr.SetSmoothingMode(4); if (parent) { if (hover) { - gr.DrawString(ui.icon.expand2, ui.icon.font, icon_c, x, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand2, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } else { - gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - if (this.nodeStyle === 3) gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x + 1, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + if (this.nodeStyle === 3) gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x + 1, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } } else { - gr.DrawString(ui.icon.expand2, ui.icon.font, icon_c, x, y2, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand2, lib.ui.icon.font, icon_c, x, y2, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } gr.SetSmoothingMode(0); break; @@ -1146,28 +1146,28 @@ class Populate { case 5: // * Custom node - Georgia-ReBORN design ( Clean +|- ) if (parent) { // Plus if (hover) { - gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x, y2 - (RES_4K ? -1 : 1), ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x, y2 - (RES_4K ? -1 : 1), ui.x + panel.tree.w - x + 2, ui.row.h + 2, panel.s_lc); - } else gr.DrawString(ui.icon.expand, ui.icon.font, icon_c, x, y2 - (DetectWine ? (RES_4K ? -1 : 0) : (RES_4K ? -1 : 1)), ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2 - (RES._4K ? -1 : 1), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2 - (RES._4K ? -1 : 1), lib.ui.x + lib.panel.tree.w - x + 2, lib.ui.row.h + 2, lib.panel.s_lc); + } else gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, icon_c, x, y2 - (DetectWine ? (RES._4K ? -1 : 0) : (RES._4K ? -1 : 1)), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } else { // Minus - gr.DrawString(ui.icon.collapse, ui.icon.font, icon_c, x - ui.icon.offset, y2 - (DetectWine ? (RES_4K ? -1 : 1) : (RES_4K ? 0 : 1)), ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - gr.DrawString(ui.icon.collapse, ui.icon.font, icon_c, x - ui.icon.offset, y2 - (RES_4K ? -1 : 1), ui.x + panel.tree.w - x, ui.row.h + 2, panel.s_lc); + gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2 - (DetectWine ? (RES._4K ? -1 : 1) : (RES._4K ? 0 : 1)), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, icon_c, x - lib.ui.icon.offset, y2 - (RES._4K ? -1 : 1), lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h + 2, lib.panel.s_lc); } break; case 7: if (item > 1) item -= 2; - ui.style.symb.SetPartAndStateID(2, !item ? 1 : 2); - ui.style.symb.DrawThemeBackground(gr, x, y, ui.sz.node, ui.sz.node); + lib.ui.style.symb.SetPartAndStateID(2, !item ? 1 : 2); + lib.ui.style.symb.DrawThemeBackground(gr, x, y, lib.ui.sz.node, lib.ui.sz.node); break; default: if (parent) { if (hover) { - gr.DrawString(ui.icon.expand, ui.icon.font, selCol ? ui.col.textSel : this.highlight.node ? ui.col.icon_h : ui.col.icon_e, x, y + this.iconVerticalPad, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - } else gr.DrawString(ui.icon.expand, ui.icon.font, !selCol ? ui.col.icon_c : ui.col.textSel, x, y + this.iconVerticalPad, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, selCol ? lib.ui.col.textSel : this.highlight.node ? lib.ui.col.icon_h : lib.ui.col.icon_e, x, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + } else gr.DrawString(lib.ui.icon.expand, lib.ui.icon.font, !selCol ? lib.ui.col.icon_c : lib.ui.col.textSel, x, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } else { if (hover) { - gr.DrawString(ui.icon.collapse, ui.icon.font, selCol ? ui.col.textSel : this.highlight.node ? ui.col.icon_h : ui.col.icon_c, x - ui.icon.offset, y + this.iconVerticalPad, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); - } else gr.DrawString(ui.icon.collapse, ui.icon.font, !selCol ? ui.col.icon_e : ui.col.textSel, x - ui.icon.offset, y + this.iconVerticalPad, ui.x + panel.tree.w - x, ui.row.h, panel.s_lc); + gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, selCol ? lib.ui.col.textSel : this.highlight.node ? lib.ui.col.icon_h : lib.ui.col.icon_c, x - lib.ui.icon.offset, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); + } else gr.DrawString(lib.ui.icon.collapse, lib.ui.icon.font, !selCol ? lib.ui.col.icon_e : lib.ui.col.textSel, x - lib.ui.icon.offset, y + this.iconVerticalPad, lib.ui.x + lib.panel.tree.w - x, lib.ui.row.h, lib.panel.s_lc); } break; } @@ -1178,7 +1178,7 @@ class Populate { let m = 0; if (ie) this.tree[ie].sel = true; if (!this.tree.some(v => v.sel)) return; - if (ppt.autoCollapse) { + if (libSet.autoCollapse) { const parent = []; const pr_pr = []; let par = 0; @@ -1198,22 +1198,22 @@ class Populate { this.tree.forEach((v, i) => { if (!parent.includes(i) && !v.sel && !v.root) v.child = []; }); - this.buildTree(lib.root, 0); + this.buildTree(lib.lib.root, 0); } const start_l = this.tree.length; let nm_n = ''; let nodes = -1; m = this.tree.length; this.expandedTracks = 0; - this.expandLmt = men.treeExpandLimit; + this.expandLmt = lib.men.treeExpandLimit; while (m--) { if (this.tree[m].sel) { this.expandNodes(this.tree[m], !(!this.rootNode || m)); nodes++; } } - sbar.setRows(this.tree.length); - panel.treePaint(); + lib.sbar.setRows(this.tree.length); + lib.panel.treePaint(); if (nm) { this.tree.some((v, i, arr) => { nm_n = (v.level ? arr[v.par].srt[0] : '') + v.srt[0]; @@ -1232,20 +1232,20 @@ class Populate { }); } const new_items = this.tree.length - start_l + nodes; - const b = Math.round(sbar.scroll / ui.row.h + 0.4); + const b = Math.round(lib.sbar.scroll / lib.ui.row.h + 0.4); const n = Math.max(h - b, this.rootNode ? 1 : 0); let scrollChk = false; if (n + 1 + new_items > this.rows) { scrollChk = true; - if (new_items > this.rows - 2) sbar.checkScroll(h * ui.row.h); - else sbar.checkScroll(Math.min(h * ui.row.h, (h + 1 - sbar.rows_drawn + new_items) * ui.row.h)); + if (new_items > this.rows - 2) lib.sbar.checkScroll(h * lib.ui.row.h); + else lib.sbar.checkScroll(Math.min(h * lib.ui.row.h, (h + 1 - lib.sbar.rows_drawn + new_items) * lib.ui.row.h)); } - if (sbar.scroll > h * ui.row.h) { + if (lib.sbar.scroll > h * lib.ui.row.h) { scrollChk = true; - sbar.checkScroll(h * ui.row.h); + lib.sbar.checkScroll(h * lib.ui.row.h); } - if (!scrollChk) sbar.scrollRound(); - lib.treeState(false, ppt.rememberTree); + if (!scrollChk) lib.sbar.scrollRound(); + lib.lib.treeState(false, libSet.rememberTree); } expandCollapse(x, y, item, ix) { @@ -1253,35 +1253,35 @@ class Populate { switch (expanded) { case 0: { let n = 0; - if (ppt.autoCollapse) { + if (libSet.autoCollapse) { n = this.branchChange(item, false, true); - sbar.checkScroll(sbar.scroll - n * ui.row.h, 'step'); + lib.sbar.checkScroll(lib.sbar.scroll - n * lib.ui.row.h, 'step'); } const row = this.getRowNumber(y); this.branch(item, !!item.root, true); - if (!ix) panel.setHeight(true); + if (!ix) lib.panel.setHeight(true); n = 2; - if (item.child.length == 1 && ppt.treeAutoExpandSingle) { + if (item.child.length == 1 && libSet.treeAutoExpandSingle) { this.branch(item.child[0], false, true); n += item.child[0].child.length; } - if (ppt.autoCollapse) ix = item.ix; + if (libSet.autoCollapse) ix = item.ix; if (row + n + item.child.length > this.rows) { - if (item.child.length > (this.rows - n)) sbar.checkScroll(ix * ui.row.h); - else sbar.checkScroll(Math.min(ix * ui.row.h, (ix + n - sbar.rows_drawn + item.child.length) * ui.row.h)); + if (item.child.length > (this.rows - n)) lib.sbar.checkScroll(ix * lib.ui.row.h); + else lib.sbar.checkScroll(Math.min(ix * lib.ui.row.h, (ix + n - lib.sbar.rows_drawn + item.child.length) * lib.ui.row.h)); } break; } case 1: { this.clearChild(item); - if (!ix && this.tree.length == 1) panel.setHeight(false); - const b = $Lib.clamp(Math.round(sbar.delta / ui.row.h + 0.4), 0, this.tree.length - 1); - const f = Math.min(b + panel.rows, this.tree.length); - if (f - b < panel.rows) sbar.checkScroll((this.tree.length - panel.rows) * ui.row.h); + if (!ix && this.tree.length == 1) lib.panel.setHeight(false); + const b = $Lib.clamp(Math.round(lib.sbar.delta / lib.ui.row.h + 0.4), 0, this.tree.length - 1); + const f = Math.min(b + lib.panel.rows, this.tree.length); + if (f - b < lib.panel.rows) lib.sbar.checkScroll((this.tree.length - lib.panel.rows) * lib.ui.row.h); break; } } - if (sbar.scroll > ix * ui.row.h) sbar.checkScroll(ix * ui.row.h); + if (lib.sbar.scroll > ix * lib.ui.row.h) lib.sbar.checkScroll(ix * lib.ui.row.h); } expandNodes(obj, am) { @@ -1312,7 +1312,7 @@ class Populate { focusShow(i) { this.setTreeSel(i); - panel.treePaint(); + lib.panel.treePaint(); this.showItem(i); } @@ -1330,14 +1330,14 @@ class Populate { getAllCombinations(n) { n = this.fixMarkers(n); - return n.includes('^@^') && n.includes(panel.softSplitter) ? this.imgView(n) : this.getCombos(n); + return n.includes('^@^') && n.includes(lib.panel.softSplitter) ? this.imgView(n) : this.getCombos(n); } getCombos(n) { const combinations = []; const divisors = []; const arraysToCombine = []; - n = n.replace(RegExp(`(#!#|)${panel.softSplitter}(#!#|)`, 'g'), '@@').split('#!#'); + n = n.replace(RegExp(`(#!#|)${lib.panel.softSplitter}(#!#|)`, 'g'), '@@').split('#!#'); const ln = n.length; let i = 0; for (i = 0; i < ln; i++) { @@ -1371,29 +1371,29 @@ class Populate { } getItemCount(v) { - const prop = !panel.imgView ? 'statistics' : '_statistics'; - if (this.statisticsShow && v[prop] == null) v[prop] = v.root && this.label && !panel.imgView ? this.label : this.calcStatistics(v); + const prop = !lib.panel.imgView ? 'statistics' : '_statistics'; + if (this.statisticsShow && v[prop] == null) v[prop] = v.root && this.label && !lib.panel.imgView ? this.label : this.calcStatistics(v); if (v.count === '' && this.nodeCounts) { - if (!panel.imgView) { + if (!lib.panel.imgView) { if (v.root && this.label) { if (!this.statisticsShow) v.count = this.label; } else { - const type = panel.search.txt ? 'search' : ppt.filterBy ? 'filter' : 'standard'; + const type = lib.panel.search.txt ? 'search' : libSet.filterBy ? 'filter' : 'standard'; const key = this.getKey(v); v.count = !v.track || !this.showTracks ? (v.name ? ' ' : '') + (this.nodeCounts == 1 ? `(${this.trackCount(v.item)})` : this.nodeCounts == 2 ? `(${this.branchCount(v, !!v.root, true, false, key, type)})` : '') : ''; if (!this.showTracks && v.count == `${v.name ? ' ' : ''}(0)`) v.count = ''; if (this.countsRight && !this.statisticsShow) v.count = v.count.replace(/[()]/g, ''); } } else { - const getTracks = [true, true, true, true, false, false, true, false, false, false, false][ppt.itemShowStatistics]; + const getTracks = [true, true, true, true, false, false, true, false, false, false, false][libSet.itemShowStatistics]; if (getTracks) { v.count = this.trackCount(v.item); v.count += v.count > 1 ? ' tracks' : ' track'; } - const getItemCount = !v.root && ppt.itemOverlayType != 1 && ppt.albumArtLabelType == 2 && !ppt.itemShowStatistics && (pop.nodeCounts == 1 || pop.nodeCounts == 2); + const getItemCount = !v.root && libSet.itemOverlayType != 1 && libSet.albumArtLabelType == 2 && !libSet.itemShowStatistics && (lib.pop.nodeCounts == 1 || lib.pop.nodeCounts == 2); if (getItemCount) { const count = v.count.replace(/\D/g, ''); - if (panel.lines == 1 || ppt.albumArtFlipLabels) v.grp += ` (${count})`; + if (lib.panel.lines == 1 || libSet.albumArtFlipLabels) v.grp += ` (${count})`; else v.lot += ` (${count})`; } } @@ -1404,28 +1404,28 @@ class Populate { if (n == 'newItems') this.getTreeSel(); const handleList = new FbMetadbHandleList(); this.sel_items.some(v => { - if (v >= panel.list.Count) return true; - handleList.Add(panel.list[v]); + if (v >= lib.panel.list.Count) return true; + handleList.Add(lib.panel.list[v]); }); return handleList; } get_ix(x, y, simple, type) { let ix; - y -= ui.y - 1; // - 1 = workaround to adjust and fix background color selection ( text_nowp in drawNode() ) to draw correct colored nodes in tree when option "Nowplaying in highlight" is active - x -= ui.x; - if (panel.imgView) { - if (y > img.panel.y && y < img.panel.y + img.panel.h && x > img.panel.x && x < img.panel.x + img.panel.w) { - const row_ix = img.style.vertical ? Math.ceil((y + sbar.delta - img.panel.y) / img.row.h) - 1 : 0; - const column_ix = img.style.vertical ? (!img.labels.right && !ppt.albumArtFlowMode ? Math.ceil((x - img.panel.x) / img.columnWidth) - 1 : 0) : Math.ceil((x + sbar.delta - img.panel.x) / img.columnWidth) - 1; - ix = (row_ix * img.columns) + column_ix; + y -= lib.ui.y - 1; // - 1 = workaround to adjust and fix background color selection ( text_nowp in drawNode() ) to draw correct colored nodes in tree when option "Nowplaying in highlight" is active + x -= lib.ui.x; + if (lib.panel.imgView) { + if (y > libImg.panel.y && y < libImg.panel.y + libImg.panel.h && x > libImg.panel.x && x < libImg.panel.x + libImg.panel.w) { + const row_ix = libImg.style.vertical ? Math.ceil((y + lib.sbar.delta - libImg.panel.y) / libImg.row.h) - 1 : 0; + const column_ix = libImg.style.vertical ? (!libImg.labels.right && !libSet.albumArtFlowMode ? Math.ceil((x - libImg.panel.x) / libImg.columnWidth) - 1 : 0) : Math.ceil((x + lib.sbar.delta - libImg.panel.x) / libImg.columnWidth) - 1; + ix = (row_ix * libImg.columns) + column_ix; return ix > this.tree.length - 1 ? -1 : ix; } return -1; } - ix = y > panel.tree.y && y < panel.tree.y + this.rows * ui.row.h ? Math.round((y + sbar.delta - panel.search.h - ui.row.h * 0.5) / ui.row.h) : -1; + ix = y > lib.panel.tree.y && y < lib.panel.tree.y + this.rows * lib.ui.row.h ? Math.round((y + lib.sbar.delta - lib.panel.search.h - lib.ui.row.h * 0.5) / lib.ui.row.h) : -1; if (simple) return ix; - return this.tree.length > ix && ix >= 0 && x < panel.tree.w && y > panel.tree.y && y < panel.tree.y + this.rows * ui.row.h && this.check_ix(this.tree[ix], x + ui.x, y + ui.y, type) ? ix : -1; + return this.tree.length > ix && ix >= 0 && x < lib.panel.tree.w && y > lib.panel.tree.y && y < lib.panel.tree.y + this.rows * lib.ui.row.h && this.check_ix(this.tree[ix], x + lib.ui.x, y + lib.ui.y, type) ? ix : -1; } getKey(v) { @@ -1441,13 +1441,13 @@ class Populate { getNowplaying(handle, stop) { if (stop) { - panel.treePaint(); + lib.panel.treePaint(); return this.nowp = -1; } if (!handle && fb.IsPlaying) handle = fb.GetNowPlaying(); if (!handle) return this.nowp = -1; - this.nowp = panel.list.Find(handle); - panel.treePaint(); + this.nowp = lib.panel.list.Find(handle); + lib.panel.treePaint(); } getNumbers(arr) { // test [0, '0', "0", "0.5", 10, '10', "", '', '-', null, true, false, 'Oh'] @@ -1457,11 +1457,11 @@ class Populate { } getRowNumber(y) { - return Math.round((y - panel.tree.y - ui.row.h * 0.5) / ui.row.h); + return Math.round((y - lib.panel.tree.y - lib.ui.row.h * 0.5) / lib.ui.row.h); } getTreeSel() { - panel.treePaint(); + lib.panel.treePaint(); this.sel_items = []; this.tree.forEach(v => { if (v.sel) this.addItems(this.sel_items, v.item); @@ -1491,13 +1491,13 @@ class Populate { } lbtn_dblclk(x, y) { - if (this.autoPlay.click > 2 && ppt.libSource) return; - if (vk.k('alt')) { + if (this.autoPlay.click > 2 && libSet.libSource) return; + if (lib.vk.k('alt')) { this.mbtnDblClickOrAltDblClick(x, y, '', 'alt'); return; } this.dbl_clicked = true; - if (y < panel.search.h) return; + if (y < lib.panel.search.h) return; const ix = this.get_ix(x, y, true, false); if (ix >= this.tree.length || ix < 0) return; const item = this.tree[ix]; @@ -1510,7 +1510,7 @@ class Populate { if (this.dblClickAction == 3) { const handleList = new FbMetadbHandleList(); this.range(item.item).forEach(v => { - if (v < panel.list.Count) handleList.Add(panel.list[v]); + if (v < lib.panel.list.Count) handleList.Add(lib.panel.list[v]); }); if (handleList.Count) plman.FlushPlaybackQueue(); for (let i = 0; i < handleList.Count; i++) { @@ -1519,31 +1519,31 @@ class Populate { fb.Play(); return; } - if (!ppt.libSource) { + if (!libSet.libSource) { plman.ExecutePlaylistDefaultAction($Lib.pl_active, this.range(item.item)[0]); return; } if (!this.dblClickAction && !this.autoFill.mouse && !this.autoPlay.click) return this.send(item, x, y); - if (this.dblClickAction === 2 && !item.track && !panel.imgView) { + if (this.dblClickAction === 2 && !item.track && !lib.panel.imgView) { this.expandCollapse(x, y, item, ix); - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } if (!this.dblClickAction || this.autoPlay.click === 2) return; - if (this.dblClickAction !== 2 && this.dblClickAction !== 3 || this.dblClickAction === 2 && item.track || this.dblClickAction === 2 && panel.imgView) { + if (this.dblClickAction !== 2 && this.dblClickAction !== 3 || this.dblClickAction === 2 && item.track || this.dblClickAction === 2 && lib.panel.imgView) { if (!this.autoFill.mouse) this.send(item, x, y); - let pl_stnd_idx = plman.FindOrCreatePlaylist(ppt.libPlaylist.replace(/%view_name%/i, panel.viewName), false); - if (ppt.sendToCur) pl_stnd_idx = plman.ActivePlaylist; + let pl_stnd_idx = plman.FindOrCreatePlaylist(libSet.libPlaylist.replace(/%view_name%/i, lib.panel.viewName), false); + if (libSet.sendToCur) pl_stnd_idx = plman.ActivePlaylist; else plman.ActivePlaylist = pl_stnd_idx; plman.ActivePlaylist = pl_stnd_idx; const c = (plman.PlaybackOrder === 3 || plman.PlaybackOrder === 4) ? Math.ceil(plman.PlaylistItemCount(pl_stnd_idx) * Math.random() - 1) : 0; plman.ExecutePlaylistDefaultAction(pl_stnd_idx, c); } - if (ppt.dblClickAction === 3) { + if (libSet.dblClickAction === 3) { if (plman.PlaylistItemCount(plman.ActivePlaylist) <= 0) { - panel.pos = 0; - this.setTreeSel(this.tree[panel.pos].ix); - this.setPlaylist(panel.pos, this.tree[panel.pos]); - this.load(panel.pos, true, true, true, false, false); + lib.panel.pos = 0; + this.setTreeSel(this.tree[lib.panel.pos].ix); + this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); + this.load(lib.panel.pos, true, true, true, false, false); } plman.ExecutePlaylistDefaultAction($Lib.pl_active, this.sel_items[0]); this.load(this.sel_items, true, false, true, false, false); @@ -1558,68 +1558,73 @@ class Populate { lbtn_dn(x, y) { this.lbtnDn = false; this.dbl_clicked = false; - if (y < panel.search.h) return; + if (y < lib.panel.search.h) return; const ix = this.get_ix(x, y, true, false); if (ix >= this.tree.length || ix < 0) return; this.deactivateTooltip(); - if (ppt.touchControl) { - ui.id.dragDrop = ui.id.touch_dn = ix; + if (libSet.touchControl) { + lib.ui.id.dragDrop = lib.ui.id.touch_dn = ix; } const item = this.tree[ix]; this.clicked_on = this.clickedOn(x, y, item); switch (this.clicked_on) { case 'node': - panel.pos = ix; + lib.panel.pos = ix; this.expandCollapse(x, y, item, ix); this.checkRow(x, y); break; case 'text': - this.last_pressed_coord.x = x - ui.x; - this.last_pressed_coord.y = y - ui.y; + this.last_pressed_coord.x = x - lib.ui.x; + this.last_pressed_coord.y = y - lib.ui.y; this.lbtnDn = true; - panel.pos = ix; - if (ppt.touchControl) break; - if (vk.k('alt')) { + lib.panel.pos = ix; + if (libSet.touchControl) break; + if (lib.vk.k('alt')) { this.alt_dbl_clicked = false; - if (ppt.altClickAction == 2) { + if (libSet.altClickAction == 2) { return; } if (this.autoFill.mouse) return; } - if (!item.sel && !vk.k('ctrl')) this.setTreeSel(ix, item.sel); + if (!item.sel && !lib.vk.k('ctrl')) this.setTreeSel(ix, item.sel); break; } - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } lbtn_up(x, y) { - if (lib.empty && ppt.libSource == 1 && !ppt.fixedPlaylist && y > panel.search.h) fb.RunMainMenuCommand('Library/Configure'); + if (lib.lib.empty && libSet.libSource == 1 && !libSet.fixedPlaylist && y > lib.panel.search.h) fb.RunMainMenuCommand('Library/Configure'); this.last_pressed_coord = { x: undefined, y: undefined }; this.lbtnDn = false; - if (y < panel.search.h || x < ui.x || this.dbl_clicked || but.Dn) return; + if (y < lib.panel.search.h || x < lib.ui.x || this.dbl_clicked || lib.but.Dn) return; const ix = this.get_ix(x, y, true, false); - panel.pos = ix; + lib.panel.pos = ix; if (ix >= this.tree.length || ix < 0) return; - if (ppt.touchControl && (this.autoFill.mouse || this.autoPlay.click) && ui.id.touch_dn != ix) return; + if (libSet.touchControl && (this.autoFill.mouse || this.autoPlay.click) && lib.ui.id.touch_dn != ix) return; const item = this.tree[ix]; if (this.clicked_on != 'text') return; - if (!ppt.libSource) return this.setPlaylistSelection(ix, item); - if (vk.k('alt')) { - if (pref.libraryPlaylistSwitch) { - btns.library.enabled = false; - btns.library.changeState(ButtonState.Default); - displayLibrary = false; - displayPlaylist = true; - if (!pref.playlistAutoScrollNowPlaying) playlist.on_size(ww, wh); + if (!libSet.libSource) return this.setPlaylistSelection(ix, item); + if (lib.vk.k('alt')) { + if (grSet.addTracksPlaylistSwitch) { + grm.ui.btn.library.enabled = false; + grm.ui.btn.library.changeState(ButtonState.Default); + grm.ui.displayLibrary = false; + grm.ui.displayPlaylist = true; + if (!grSet.playlistAutoScrollNowPlaying) pl.call.on_size(grm.ui.ww, grm.ui.wh); + setTimeout(() => { + if (pl.playlist.is_scrollbar_available) { + pl.playlist.scrollbar.scroll_to_end(); + } + }, 500); window.Repaint(); } this.mbtnUpOrAltClickUp(x, y, '', 'alt'); // { return; } - if (!vk.k('ctrl')) { + if (!lib.vk.k('ctrl')) { this.clearSelected(); if (!item.sel) this.setTreeSel(ix, item.sel); } else this.setTreeSel(ix, item.sel); @@ -1627,44 +1632,44 @@ class Populate { window.Repaint(true); this.send(item, x, y); } else { - panel.treePaint(); + lib.panel.treePaint(); } this.track(this.autoFill.mouse || this.autoPlay.click); - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } leave() { this.deactivateTooltip(); - panel.m.x = -1; - panel.m.y = -1; - if (men.r_up) return; + lib.panel.m.x = -1; + lib.panel.m.y = -1; + if (lib.men.r_up) return; this.m.br = -1; this.m.cur_br = 0; this.m.i = -1; this.cur_ix = 0; this.row.i = -1; this.row.cur = 0; - panel.treePaint(); + lib.panel.treePaint(); } leftKeyCheckScroll() { - const row = (panel.pos * ui.row.h - sbar.scroll) / ui.row.h; - if (sbar.scroll > panel.pos * ui.row.h) sbar.checkScroll(panel.pos * ui.row.h); - else if (row - sbar.rows_drawn > 0) { - sbar.checkScroll((panel.pos + 3 - sbar.rows_drawn) * ui.row.h); + const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; + if (lib.sbar.scroll > lib.panel.pos * lib.ui.row.h) lib.sbar.checkScroll(lib.panel.pos * lib.ui.row.h); + else if (row - lib.sbar.rows_drawn > 0) { + lib.sbar.checkScroll((lib.panel.pos + 3 - lib.sbar.rows_drawn) * lib.ui.row.h); } - else sbar.scrollRound(); - lib.treeState(false, ppt.rememberTree); + else lib.sbar.scrollRound(); + lib.lib.treeState(false, libSet.rememberTree); } load(list, isArray, add, autoPlay, def_pl, insert) { let np_item = -1; let pid = -1; - const pl_stnd = ppt.libPlaylist.replace(/%view_name%/i, panel.viewName); + const pl_stnd = libSet.libPlaylist.replace(/%view_name%/i, lib.panel.viewName); let pl_stnd_idx = plman.FindOrCreatePlaylist(pl_stnd, true); if (!def_pl && plman.ActivePlaylist != -1) pl_stnd_idx = plman.ActivePlaylist; - else if (ppt.activateOnChange) plman.ActivePlaylist = pl_stnd_idx; + else if (libSet.activateOnChange) plman.ActivePlaylist = pl_stnd_idx; if (autoPlay == 4 && plman.PlaylistItemCount(pl_stnd_idx) || autoPlay == 3 && fb.IsPlaying) { autoPlay = false; @@ -1679,8 +1684,8 @@ class Populate { const pllockRemoveOrAdd = plnIsValid ? plman.GetPlaylistLockedActions(pl_stnd_idx).includes('RemoveItems') || plman.GetPlaylistLockedActions(pl_stnd_idx).includes('ReplaceItems') || plman.GetPlaylistLockedActions(pl_stnd_idx).includes('AddItems') : false; if (!add && pllockRemoveOrAdd) return; if (fb.IsPlaying && !add) { - if (ppt.actionMode == 1) { - const pl_playing = `${ppt.libPlaylist} (playing)`; + if (libSet.actionMode == 1) { + const pl_playing = `${libSet.libPlaylist} (playing)`; const pl_playing_idx = plman.FindOrCreatePlaylist(pl_playing, false); if (plman.PlayingPlaylist == pl_stnd_idx) { plman.RenamePlaylist(pl_stnd_idx, pl_playing); @@ -1707,7 +1712,7 @@ class Populate { else pid = np.PlaylistItemIndex; } if (pl_chk && pid == -1 && items.Count < 5000) { - if (ui.dui) plman.SetActivePlaylistContext(); + if (lib.ui.dui) plman.SetActivePlaylistContext(); const start = Date.now(); for (let i = 0; i < 20; i++) { if (Date.now() - start > 300) break; @@ -1760,12 +1765,12 @@ class Populate { mbtnDblClickOrAltDblClick(x, y, mask, type) { this[`${type}_dbl_clicked`] = true; - if (type == 'mbtn' && (ppt.actionMode == 2 || ppt.mbtnClickAction == 2) || type == 'alt' && ppt.altClickAction == 2) { + if (type == 'mbtn' && (libSet.actionMode == 2 || libSet.mbtnClickAction == 2) || type == 'alt' && libSet.altClickAction == 2) { const ix = this.get_ix(x, y, true, false); if (ix < this.tree.length && ix >= 0) { const handleList = new FbMetadbHandleList(); this.range(this.tree[ix].item).forEach(v => { - if (v < panel.list.Count) handleList.Add(panel.list[v]); + if (v < lib.panel.list.Count) handleList.Add(lib.panel.list[v]); }); const queueHandles = plman.GetPlaybackQueueHandles(); let remove = []; @@ -1784,14 +1789,14 @@ class Populate { mbtnUpOrAltClickUp(x, y, mask, type) { if (this[`${type}_dbl_clicked`]) return; - if (type == 'mbtn' && (ppt.actionMode == 2 || ppt.mbtnClickAction == 2) || type == 'alt' && ppt.altClickAction == 2) { + if (type == 'mbtn' && (libSet.actionMode == 2 || libSet.mbtnClickAction == 2) || type == 'alt' && libSet.altClickAction == 2) { setTimeout(() => { // timeout: wait & see if double click, but adds a little lag to single click: timeout can be commented out if (this[`${type}_dbl_clicked`]) return; const ix = this.get_ix(x, y, true, false); if (ix < this.tree.length && ix >= 0) { const handleList = new FbMetadbHandleList(); this.range(this.tree[ix].item).forEach(v => { - if (v < panel.list.Count) handleList.Add(panel.list[v]); + if (v < lib.panel.list.Count) handleList.Add(lib.panel.list[v]); }); const add = new FbMetadbHandleList(); handleList.Convert().forEach(h => { @@ -1812,13 +1817,13 @@ class Populate { } }, 180); } else { - if (!ppt.libSource) return; - this.add(x, y, !ppt[`${type}ClickAction`]); + if (!libSet.libSource) return; + this.add(x, y, !libSet[`${type}ClickAction`]); } } merge(m, mergeBrCount) { - if (!ppt.libSource && !panel.multiProcess) return; + if (!libSet.libSource && !lib.panel.multiProcess) return; const seen = {}; for (let i = 0; i < m.length; i++) { const v = m[i].srt[0].toUpperCase(); @@ -1832,7 +1837,7 @@ class Populate { } move(x, y) { - if (but.Dn) return; + if (lib.but.Dn) return; const ix = this.get_ix(x, y, false, false); this.row.i = this.checkRow(x, y); this.m.i = -1; @@ -1842,15 +1847,15 @@ class Populate { } else if (this.countsRight || this.statisticsShow) { this.check_tooltip(this.row.i, x, y); } else this.deactivateTooltip(); - if (!ppt.mousePointerOnly) { - if (this.highlight.node || panel.imgView) { + if (!libSet.mousePointerOnly) { + if (this.highlight.node || lib.panel.imgView) { if (ix != -1 || this.inlineRoot && !this.m.br) this.hand = true; } else if (this.m.br != -1 && !(this.inlineRoot && !this.m.br)) this.hand = true; } - window.SetCursor(this.hand ? 32649 : !but.Dn && y > ui.y && y < ui.y + panel.search.h && ppt.searchShow && x > ui.x + but.q.h + but.margin && x < panel.search.x + panel.search.w ? 32513 : 32512); + window.SetCursor(this.hand ? 32649 : !lib.but.Dn && y > lib.ui.y && y < lib.ui.y + lib.panel.search.h && libSet.searchShow && x > lib.ui.x + lib.but.q.h + lib.but.margin && x < lib.panel.search.x + lib.panel.search.w ? 32513 : 32512); const same = this.m.i == this.cur_ix && this.m.br == this.m.cur_br && this.row.i == this.row.cur; - if (same && !sbar.touch.dn) return; - if (!sbar.draw_timer && !same) panel.treePaint(); + if (same && !lib.sbar.touch.dn) return; + if (!lib.sbar.draw_timer && !same) lib.panel.treePaint(); this.cur_ix = this.m.i; this.m.cur_br = this.m.br; this.row.cur = this.row.i; @@ -1870,12 +1875,12 @@ class Populate { if (this.inRange(this.nowp, v.item)) { np_i = i; if (!v.root) { - if (panel.imgView) i = this.tree.length; + if (lib.panel.imgView) i = this.tree.length; else if (!v.track) this.branch(this.tree[np_i]); } } } - if (!panel.imgView && !this.tree[np_i].root) { + if (!lib.panel.imgView && !this.tree[np_i].root) { this.clearSelected(); if (!this.highlight.nowPlaying) this.tree[np_i].sel = true; } @@ -1888,14 +1893,14 @@ class Populate { } on_char(code) { - if (panel.search.active) return; + if (lib.panel.search.active) return; switch (code) { - case vk.copy: { + case lib.vk.copy: { const handleList = this.getHandleList('newItems'); fb.CopyHandleListToClipboard(handleList); break; } - case vk.selAll: + case lib.vk.selAll: this.tree.forEach(v => { if (!v.root) v.sel = true; }); @@ -1903,11 +1908,11 @@ class Populate { if (!this.sel_items.length) return; this.setPlaylist(); break; - case vk.eFocusSearch: - case vk.lFocusSearch: - if (ppt.searchShow) search.focus(); + case lib.vk.eFocusSearch: + case lib.vk.lFocusSearch: + if (libSet.searchShow) lib.search.focus(); break; - case vk.insert: + case lib.vk.insert: this.getTreeSel(); if (!this.sel_items.length) return; this.load(this.sel_items, true, true, false, false, true); @@ -1921,33 +1926,33 @@ class Populate { } setPlaylist(ix, item) { - if (ppt.libSource) { - if (this.autoFill.key) this.load(this.sel_items, true, false, false, !ppt.sendToCur, false); + if (libSet.libSource) { + if (this.autoFill.key) this.load(this.sel_items, true, false, false, !libSet.sendToCur, false); this.track(true); } else if (this.autoFill.key) this.setPlaylistSelection(ix, item); } on_key_down(vkey) { - if (vkey == vk.collapseAll && !panel.imgView) this.collapseAll(); - if (vkey == vk.expand && !panel.imgView) { + if (vkey == lib.vk.collapseAll && !lib.panel.imgView) this.collapseAll(); + if (vkey == lib.vk.expand && !lib.panel.imgView) { const isSel = this.tree.some(v => v.sel); this.expand(); if (isSel) { - panel.setHeight(true); + lib.panel.setHeight(true); this.checkAutoHeight(); } } - if (panel.search.active) return; - if (vk.k('enter')) { + if (lib.panel.search.active) return; + if (lib.vk.k('enter')) { if (!this.sel_items.length) return; - if (!ppt.libSource) { + if (!libSet.libSource) { if (this.autoPlay.send) plman.ExecutePlaylistDefaultAction($Lib.pl_active, this.sel_items[0]); return; } switch (true) { - case vk.k('shift'): + case lib.vk.k('shift'): return this.load(this.sel_items, true, true, false, false, false); - case vk.k('ctrl'): + case lib.vk.k('ctrl'): return this.sendToNewPlaylist(); default: return this.load(this.sel_items, true, false, this.autoPlay.send, false, false); @@ -1955,145 +1960,145 @@ class Populate { } let item = -1; switch (vkey) { - case vk.left: - if (panel.imgView) panel.pos -= 1; - panel.pos = $Lib.clamp(panel.pos, 0, this.tree.length - 1); + case lib.vk.left: + if (lib.panel.imgView) lib.panel.pos -= 1; + lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); this.row.i = -1; this.m.i = -1; - if (panel.imgView) { - item = this.tree[panel.pos]; - this.m.i = panel.pos; + if (lib.panel.imgView) { + item = this.tree[lib.panel.pos]; + this.m.i = lib.panel.pos; this.showItem(item.ix, 'left'); - this.setPlaylist(panel.pos, item); + this.setPlaylist(lib.panel.pos, item); break; } // !imgView - if ((this.tree[panel.pos].level == (this.rootNode ? 1 : 0)) && this.tree[panel.pos].child.length < 1) { - item = this.tree[panel.pos]; - this.m.i = panel.pos = item.ix; + if ((this.tree[lib.panel.pos].level == (this.rootNode ? 1 : 0)) && this.tree[lib.panel.pos].child.length < 1) { + item = this.tree[lib.panel.pos]; + this.m.i = lib.panel.pos = item.ix; this.leftKeyCheckScroll(); break; } - if (this.tree[panel.pos].child.length > 0) { - item = this.tree[panel.pos]; + if (this.tree[lib.panel.pos].child.length > 0) { + item = this.tree[lib.panel.pos]; this.clearChild(item); this.setTreeSel(item.ix); - this.m.i = panel.pos = item.ix; + this.m.i = lib.panel.pos = item.ix; } else { - item = this.tree[this.tree[panel.pos].par]; + item = this.tree[this.tree[lib.panel.pos].par]; if (item) this.clearChild(item); this.setTreeSel(item.ix); - this.m.i = panel.pos = item.ix; + this.m.i = lib.panel.pos = item.ix; } - panel.treePaint(); - this.setPlaylist(panel.pos, item); - sbar.setRows(this.tree.length); + lib.panel.treePaint(); + this.setPlaylist(lib.panel.pos, item); + lib.sbar.setRows(this.tree.length); this.leftKeyCheckScroll(); break; - case vk.right: { - if (panel.imgView) panel.pos += 1; - panel.pos = $Lib.clamp(panel.pos, 0, this.tree.length - 1); + case lib.vk.right: { + if (lib.panel.imgView) lib.panel.pos += 1; + lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); this.row.i = -1; this.m.i = -1; - item = this.tree[panel.pos]; - if (panel.imgView) { - this.m.i = panel.pos; + item = this.tree[lib.panel.pos]; + if (lib.panel.imgView) { + this.m.i = lib.panel.pos; this.showItem(item.ix, 'right'); - this.setPlaylist(panel.pos, item); + this.setPlaylist(lib.panel.pos, item); break; } // !imgView if (item.child.length) { - panel.pos++; - panel.pos = $Lib.clamp(panel.pos, !this.rootNode ? 0 : 1, this.tree.length - 1); + lib.panel.pos++; + lib.panel.pos = $Lib.clamp(lib.panel.pos, !this.rootNode ? 0 : 1, this.tree.length - 1); this.upDnKeyCheckScroll(vkey); break; } - if (ppt.autoCollapse) this.branchChange(item, false, true); + if (libSet.autoCollapse) this.branchChange(item, false, true); this.branch(item, !!item.root, true); let n = 2; - if (item.child.length == 1 && ppt.treeAutoExpandSingle) { + if (item.child.length == 1 && libSet.treeAutoExpandSingle) { this.branch(item.child[0], false, true); n += item.child[0].child.length; } this.setTreeSel(item.ix); - panel.treePaint(); - this.m.i = panel.pos = item.ix; - this.setPlaylist(panel.pos, item); - sbar.setRows(this.tree.length); - const row = (panel.pos * ui.row.h - sbar.scroll) / ui.row.h; - if (row + n + item.child.length > sbar.rows_drawn || row < 0) { - if (item.child.length > (sbar.rows_drawn - n) || row < 0) sbar.checkScroll(panel.pos * ui.row.h); - else sbar.checkScroll(Math.min(panel.pos * ui.row.h, (panel.pos + n - sbar.rows_drawn + item.child.length) * ui.row.h)); - } else sbar.scrollRound(); - lib.treeState(false, ppt.rememberTree); + lib.panel.treePaint(); + this.m.i = lib.panel.pos = item.ix; + this.setPlaylist(lib.panel.pos, item); + lib.sbar.setRows(this.tree.length); + const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; + if (row + n + item.child.length > lib.sbar.rows_drawn || row < 0) { + if (item.child.length > (lib.sbar.rows_drawn - n) || row < 0) lib.sbar.checkScroll(lib.panel.pos * lib.ui.row.h); + else lib.sbar.checkScroll(Math.min(lib.panel.pos * lib.ui.row.h, (lib.panel.pos + n - lib.sbar.rows_drawn + item.child.length) * lib.ui.row.h)); + } else lib.sbar.scrollRound(); + lib.lib.treeState(false, libSet.rememberTree); break; } - case vk.pgUp: + case lib.vk.pgUp: if (this.tree.length == 0) break; - if (panel.imgView) { - panel.pos = panel.pos - img.columns * (panel.rows - 1); - panel.pos = $Lib.clamp(panel.pos, 0, this.tree.length - 1); - } else panel.pos = Math.max(Math.round(sbar.scroll / ui.row.h + 0.4) - Math.floor(panel.rows) + 1, !this.rootNode ? 0 : 1); - sbar.pageThrottle(1); - this.setTreeSel(this.tree[panel.pos].ix); - panel.treePaint(); - this.setPlaylist(panel.pos, this.tree[panel.pos]); - lib.treeState(false, ppt.rememberTree); + if (lib.panel.imgView) { + lib.panel.pos = lib.panel.pos - libImg.columns * (lib.panel.rows - 1); + lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); + } else lib.panel.pos = Math.max(Math.round(lib.sbar.scroll / lib.ui.row.h + 0.4) - Math.floor(lib.panel.rows) + 1, !this.rootNode ? 0 : 1); + lib.sbar.pageThrottle(1); + this.setTreeSel(this.tree[lib.panel.pos].ix); + lib.panel.treePaint(); + this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); + lib.lib.treeState(false, libSet.rememberTree); break; - case vk.pgDn: + case lib.vk.pgDn: if (this.tree.length == 0) break; - if (panel.imgView) { - panel.pos = panel.pos + img.columns * (panel.rows - 1); - panel.pos = $Lib.clamp(panel.pos, 0, this.tree.length - 1); - } else panel.pos = Math.min(Math.round(sbar.scroll / ui.row.h + 0.4) + Math.floor(panel.rows) * 2 - 2, this.tree.length - 1); - sbar.pageThrottle(-1); - this.setTreeSel(this.tree[panel.pos].ix); - panel.treePaint(); - this.setPlaylist(panel.pos, this.tree[panel.pos]); - lib.treeState(false, ppt.rememberTree); + if (lib.panel.imgView) { + lib.panel.pos = lib.panel.pos + libImg.columns * (lib.panel.rows - 1); + lib.panel.pos = $Lib.clamp(lib.panel.pos, 0, this.tree.length - 1); + } else lib.panel.pos = Math.min(Math.round(lib.sbar.scroll / lib.ui.row.h + 0.4) + Math.floor(lib.panel.rows) * 2 - 2, this.tree.length - 1); + lib.sbar.pageThrottle(-1); + this.setTreeSel(this.tree[lib.panel.pos].ix); + lib.panel.treePaint(); + this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); + lib.lib.treeState(false, libSet.rememberTree); break; - case vk.home: + case lib.vk.home: if (this.tree.length == 0) break; - panel.pos = !this.rootNode ? 0 : 1; - sbar.checkScroll(0, 'full'); - this.setTreeSel(this.tree[panel.pos].ix); - panel.treePaint(); - this.setPlaylist(panel.pos, this.tree[panel.pos]); - lib.treeState(false, ppt.rememberTree); + lib.panel.pos = !this.rootNode ? 0 : 1; + lib.sbar.checkScroll(0, 'full'); + this.setTreeSel(this.tree[lib.panel.pos].ix); + lib.panel.treePaint(); + this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); + lib.lib.treeState(false, libSet.rememberTree); break; - case vk.end: + case lib.vk.end: if (this.tree.length == 0) break; - panel.pos = this.tree.length - 1; - sbar.scrollToEnd(); - this.setTreeSel(this.tree[panel.pos].ix); - panel.treePaint(); - this.setPlaylist(panel.pos, this.tree[panel.pos]); - lib.treeState(false, ppt.rememberTree); + lib.panel.pos = this.tree.length - 1; + lib.sbar.scrollToEnd(); + this.setTreeSel(this.tree[lib.panel.pos].ix); + lib.panel.treePaint(); + this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); + lib.lib.treeState(false, libSet.rememberTree); break; - case vk.dn: - case vk.up: + case lib.vk.dn: + case lib.vk.up: if (this.tree.length == 0) break; - if ((panel.pos == 0 && vkey == vk.up) || (panel.pos == this.tree.length - 1 && vkey == vk.dn)) { + if ((lib.panel.pos == 0 && vkey == lib.vk.up) || (lib.panel.pos == this.tree.length - 1 && vkey == lib.vk.dn)) { this.setTreeSel(-1); break; } this.row.i = -1; this.m.i = -1; - if (!panel.imgView) { - if (vkey == vk.dn) panel.pos++; - if (vkey == vk.up) panel.pos--; + if (!lib.panel.imgView) { + if (vkey == lib.vk.dn) lib.panel.pos++; + if (vkey == lib.vk.up) lib.panel.pos--; } else { - if (vkey == vk.dn) panel.pos += img.columns; - if (vkey == vk.up) panel.pos -= img.columns; + if (vkey == lib.vk.dn) lib.panel.pos += libImg.columns; + if (vkey == lib.vk.up) lib.panel.pos -= libImg.columns; } - panel.pos = $Lib.clamp(panel.pos, !this.rootNode ? 0 : 1, this.tree.length - 1); - if (panel.imgView) { - item = this.tree[panel.pos]; - this.m.i = panel.pos; - this.showItem(item.ix, vkey == vk.up ? 'up' : 'down'); - this.setPlaylist(panel.pos, item); + lib.panel.pos = $Lib.clamp(lib.panel.pos, !this.rootNode ? 0 : 1, this.tree.length - 1); + if (lib.panel.imgView) { + item = this.tree[lib.panel.pos]; + this.m.i = lib.panel.pos; + this.showItem(item.ix, vkey == lib.vk.up ? 'up' : 'down'); + this.setPlaylist(lib.panel.pos, item); return; } this.upDnKeyCheckScroll(vkey); @@ -2118,8 +2123,8 @@ class Populate { if (!this.sel_items.length) return; this.sendToNewPlaylist(); } - if (index == this.getMainMenuIndex.searchClear && ppt.searchShow) search.clear(); - if (index == this.getMainMenuIndex.searchFocus && this.is_focused && ppt.searchShow) search.focus(); + if (index == this.getMainMenuIndex.searchClear && libSet.searchShow) lib.search.clear(); + if (index == this.getMainMenuIndex.searchFocus && this.is_focused && libSet.searchShow) lib.search.focus(); } range(item) { @@ -2137,8 +2142,8 @@ class Populate { send(item, x, y) { if (!this.check_ix(item, x, y, false)) return; - if (vk.k('ctrl') || vk.k('shift')) this.load(this.sel_items, true, false, false, !ppt.sendToCur, false); - else this.load(this.sel_items, true, false, this.autoPlay.click, !ppt.sendToCur, false); + if (lib.vk.k('ctrl') || lib.vk.k('shift')) this.load(this.sel_items, true, false, false, !libSet.sendToCur, false); + else this.load(this.sel_items, true, false, this.autoPlay.click, !libSet.sendToCur, false); } sendToNewPlaylist() { @@ -2149,26 +2154,26 @@ class Populate { setActions() { this.autoPlay = { - click: ppt.clickAction < 2 ? false : ppt.clickAction, - send: ppt.autoPlay + click: libSet.clickAction < 2 ? false : libSet.clickAction, + send: libSet.autoPlay }; this.autoFill = { - mouse: ppt.actionMode == 2 ? false : ppt.clickAction == 1 || ppt.actionMode == 1, - key: ppt.keyAction + mouse: libSet.actionMode == 2 ? false : libSet.clickAction == 1 || libSet.actionMode == 1, + key: libSet.keyAction }; - this.dblClickAction = ppt.actionMode == 1 ? 1 : ppt.actionMode == 2 ? 3 : ppt.dblClickAction; + this.dblClickAction = libSet.actionMode == 1 ? 1 : libSet.actionMode == 2 ? 3 : libSet.dblClickAction; } setPlaylistSelection(ix, item) { this.clearSelected(); if (!item.sel) this.setTreeSel(ix, item.sel); - panel.treePaint(); + lib.panel.treePaint(); plman.ClearPlaylistSelection($Lib.pl_active); let items = []; - if (panel.search.txt || ppt.filterBy || panel.multiProcess) { + if (lib.panel.search.txt || libSet.filterBy || lib.panel.multiProcess) { const hl = this.getHandleList(); hl.Convert().forEach(h => { - const i = lib.full_list.Find(h); + const i = lib.lib.full_list.Find(h); if (i != -1) items.push(i); }); } else { @@ -2178,15 +2183,15 @@ class Populate { this.setFocus = true; plman.SetPlaylistFocusItem($Lib.pl_active, items[0]); this.track(false); - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } setPos(pos) { - this.m.i = this.row.i = panel.pos = pos; + this.m.i = this.row.i = lib.panel.pos = pos; } setTreeSel(idx, state) { - const sel_type = idx == -1 ? 0 : vk.k('shift') && this.last_sel > -1 && ppt.libSource ? 1 : vk.k('ctrl') ? 2 : !state ? 3 : 0; + const sel_type = idx == -1 ? 0 : lib.vk.k('shift') && this.last_sel > -1 && libSet.libSource ? 1 : lib.vk.k('ctrl') ? 2 : !state ? 3 : 0; switch (sel_type) { case 0: this.clearSelected(); @@ -2194,13 +2199,13 @@ class Populate { break; case 1: { const direction = (idx > this.last_sel) ? 1 : -1; - if (!vk.k('ctrl')) this.clearSelected(); + if (!lib.vk.k('ctrl')) this.clearSelected(); for (let i = this.last_sel; ; i += direction) { this.tree[i].sel = true; if (i == idx) break; } this.getTreeSel(); - panel.treePaint(); + lib.panel.treePaint(); break; } case 2: @@ -2220,75 +2225,75 @@ class Populate { } setValues() { - this.countsRight = ppt.countsRight; - this.fullLineSelection = ppt.fullLineSelection; + this.countsRight = libSet.countsRight; + this.fullLineSelection = libSet.fullLineSelection; this.highlight = { - node: ppt.highLightNode, - nowPlaying: ppt.highLightNowplaying, - nowPlayingIndicator: ppt.nowPlayingIndicator, - nowPlayingSidemarker: ppt.nowPlayingSidemarker, - nowPlayingShow: ppt.highLightNowplaying || ppt.nowPlayingIndicator || ppt.nowPlayingSidemarker, - row: ppt.highLightRow, - text: ppt.highLightText + node: libSet.highLightNode, + nowPlaying: libSet.highLightNowplaying, + nowPlayingIndicator: libSet.nowPlayingIndicator, + nowPlayingSidemarker: libSet.nowPlayingSidemarker, + nowPlayingShow: libSet.highLightNowplaying || libSet.nowPlayingIndicator || libSet.nowPlayingSidemarker, + row: libSet.highLightRow, + text: libSet.highLightText }; - this.iconVerticalPad = ppt.iconVerticalPad; - this.nodeCounts = ppt.nodeCounts; - this.nodeStyle = ppt.nodeStyle; - this.rootNode = ppt.rootNode; - this.rowStripes = ppt.rowStripes; - this.sbarShow = ppt.sbarShow; - this.showTracks = !ppt.facetView ? ppt.showTracks : false; + this.iconVerticalPad = libSet.iconVerticalPad; + this.nodeCounts = libSet.nodeCounts; + this.nodeStyle = libSet.nodeStyle; + this.rootNode = libSet.rootNode; + this.rowStripes = libSet.rowStripes; + this.sbarShow = libSet.sbarShow; + this.showTracks = !libSet.facetView ? libSet.showTracks : false; this.statistics = ['', 'Bitrate', 'Duration', 'Total size', 'Rating', 'Popularity', 'Date', 'Queue', 'Playcount', 'First played', 'Last played', 'Added']; - this.statisticsShow = ppt.itemShowStatistics; - this.label = !ppt.labelStatistics ? '' : this.statisticsShow ? this.statistics[this.statisticsShow] : ''; - this.tooltipStatistics = ppt.tooltipStatistics; - this.treeIndent = ppt.treeIndent; - this.imgGetItemCount = ppt.itemOverlayType != 1 && ppt.albumArtLabelType == 2 && !this.statisticsShow && (this.nodeCounts == 1 || this.nodeCounts == 2); + this.statisticsShow = libSet.itemShowStatistics; + this.label = !libSet.labelStatistics ? '' : this.statisticsShow ? this.statistics[this.statisticsShow] : ''; + this.tooltipStatistics = libSet.tooltipStatistics; + this.treeIndent = libSet.treeIndent; + this.imgGetItemCount = libSet.itemOverlayType != 1 && libSet.albumArtLabelType == 2 && !this.statisticsShow && (this.nodeCounts == 1 || this.nodeCounts == 2); } showItem(i, type) { - if (!panel.imgView) { - sbar.checkScroll(i * ui.row.h - Math.round(sbar.rows_drawn / 2 - 1) * ui.row.h, 'full'); + if (!lib.panel.imgView) { + lib.sbar.checkScroll(i * lib.ui.row.h - Math.round(lib.sbar.rows_drawn / 2 - 1) * lib.ui.row.h, 'full'); return; } this.m.i = -1; - const b = $Lib.clamp(Math.round(sbar.delta / sbar.row.h), 0, panel.rows - 1); - const f = Math.min(b + Math.floor(img.panel.h / sbar.row.h), panel.rows); + const b = $Lib.clamp(Math.round(lib.sbar.delta / lib.sbar.row.h), 0, lib.panel.rows - 1); + const f = Math.min(b + Math.floor(libImg.panel.h / lib.sbar.row.h), lib.panel.rows); this.setTreeSel(i); - panel.treePaint(); - const row1 = Math.floor(i / (img.style.vertical ? img.columns : 1)); + lib.panel.treePaint(); + const row1 = Math.floor(i / (libImg.style.vertical ? libImg.columns : 1)); if (row1 <= b || row1 >= f) { switch (type) { case 'np': case 'focus': { - const delta = (img.style.vertical ? img.panel.h : img.panel.w) / 2 > sbar.row.h ? Math.floor((img.style.vertical ? img.panel.h : img.panel.w) / 2) : 0; - const deltaRows = Math.floor(delta / sbar.row.h) * sbar.row.h; - sbar.checkScroll((img.style.vertical ? row1 : i) * sbar.row.h - deltaRows, 'full'); + const delta = (libImg.style.vertical ? libImg.panel.h : libImg.panel.w) / 2 > lib.sbar.row.h ? Math.floor((libImg.style.vertical ? libImg.panel.h : libImg.panel.w) / 2) : 0; + const deltaRows = Math.floor(delta / lib.sbar.row.h) * lib.sbar.row.h; + lib.sbar.checkScroll((libImg.style.vertical ? row1 : i) * lib.sbar.row.h - deltaRows, 'full'); break; } case 'up': case 'down': case 'left': case 'right': { - const row2 = (row1 * sbar.row.h - sbar.scroll) / sbar.row.h; - if (sbar.rows_drawn - row2 < 1 || row2 < 0) sbar.checkScroll((row1 + 1) * sbar.row.h - sbar.rows_drawn * sbar.row.h); - else if (row2 < 1 & (type == 'up' || type == 'left')) sbar.checkScroll(row1 * sbar.row.h); + const row2 = (row1 * lib.sbar.row.h - lib.sbar.scroll) / lib.sbar.row.h; + if (lib.sbar.rows_drawn - row2 < 1 || row2 < 0) lib.sbar.checkScroll((row1 + 1) * lib.sbar.row.h - lib.sbar.rows_drawn * lib.sbar.row.h); + else if (row2 < 1 & (type == 'up' || type == 'left')) lib.sbar.checkScroll(row1 * lib.sbar.row.h); } } } - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } sort(data) { - if (!ppt.libSource && !panel.multiProcess) return; + if (!libSet.libSource && !lib.panel.multiProcess) return; this.specialCharSort(data); data.sort((a, b) => this.collator.compare(a.srt[2], b.srt[2]) || (a.srt[3] && !b.srt[3] ? 1 : 0)); } sortIfNeeded(items) { - if (panel.multiProcess && !ppt.customSort.length) items.OrderByFormat(panel.playlistSort, 1); - else if (ppt.customSort.length) items.OrderByFormat(this.customSort, 1); + if (lib.panel.multiProcess && !libSet.customSort.length) items.OrderByFormat(lib.panel.playlistSort, 1); + else if (libSet.customSort.length) items.OrderByFormat(this.customSort, 1); } specialCharHas(name) { @@ -2339,8 +2344,8 @@ class Populate { } treeTooltipFont() { - const libraryFontSize = ppt[`baseFontSize_${pref.layout}`] || 14; - return !panel.imgView ? [ui.font.main.Name, /* ui.font.main.Size */ libraryFontSize + 3, ui.font.main.Style] : [ui.font.group.Name, /* ui.font.group.Size */ libraryFontSize + 3, ui.font.group.Style]; + const libraryFontSize = libSet[`baseFontSize_${grSet.layout}`] || 14; + return !lib.panel.imgView ? [lib.ui.font.main.Name, /* ui.font.main.Size */ libraryFontSize + 3, lib.ui.font.main.Style] : [lib.ui.font.group.Name, /* ui.font.group.Size */ libraryFontSize + 3, lib.ui.font.group.Style]; } uniq(arr) { @@ -2348,13 +2353,13 @@ class Populate { } upDnKeyCheckScroll(vkey) { - const row = (panel.pos * ui.row.h - sbar.scroll) / ui.row.h; - if (sbar.rows_drawn - row < 3 || row < 0) sbar.checkScroll((panel.pos + 3) * ui.row.h - sbar.rows_drawn * ui.row.h); - else if (row < 2 && vkey == vk.up) sbar.checkScroll((panel.pos - 1) * ui.row.h); - this.m.i = panel.pos; - this.setTreeSel(panel.pos); - panel.treePaint(); - this.setPlaylist(panel.pos, this.tree[panel.pos]); - lib.treeState(false, ppt.rememberTree); + const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; + if (lib.sbar.rows_drawn - row < 3 || row < 0) lib.sbar.checkScroll((lib.panel.pos + 3) * lib.ui.row.h - lib.sbar.rows_drawn * lib.ui.row.h); + else if (row < 2 && vkey == lib.vk.up) lib.sbar.checkScroll((lib.panel.pos - 1) * lib.ui.row.h); + this.m.i = lib.panel.pos; + this.setTreeSel(lib.panel.pos); + lib.panel.treePaint(); + this.setPlaylist(lib.panel.pos, this.tree[lib.panel.pos]); + lib.lib.treeState(false, libSet.rememberTree); } } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-popupbox.js b/profile/georgia-reborn/scripts/Library/scripts/lib-popupbox.js index 8b28b443..e53188b6 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-popupbox.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-popupbox.js @@ -1,6 +1,6 @@ 'use strict'; -class PopUpBox { +class LibPopUpBox { constructor() { this.getHtmlCode(); this.ok = true; @@ -9,9 +9,9 @@ class PopUpBox { // * METHODS * // - config(cfg, ppt, cfgWindow, ok_callback) { + config(cfg, libSet, cfgWindow, ok_callback) { utils.ShowHtmlDialog(0, this.configHtmlCode, { - data: [cfg, ppt, cfgWindow, ok_callback], + data: [cfg, libSet, cfgWindow, ok_callback], resizable: true }); } @@ -23,24 +23,24 @@ class PopUpBox { } getHtmlCode() { - let cssPath = `${my_utilsLib.packagePath}/assets/html/`; + let cssPath = `${lib_my_utils.packagePath}/assets/html/`; cssPath += this.getWindowsVersion() === '6.1' ? 'styles7.css' : 'styles10.css'; - this.configHtmlCode = my_utilsLib.getAsset('\\html\\config.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); - this.inputHtmlCode = my_utilsLib.getAsset('\\html\\input.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); - this.messageHtmlCode = my_utilsLib.getAsset('\\html\\messageBox.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); - this.confirmHtmlCode = my_utilsLib.getAsset('\\html\\confirm.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.configHtmlCode = lib_my_utils.getAsset('\\html\\config.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.inputHtmlCode = lib_my_utils.getAsset('\\html\\input.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.messageHtmlCode = lib_my_utils.getAsset('\\html\\messageBox.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); + this.confirmHtmlCode = lib_my_utils.getAsset('\\html\\confirm.html').replace(/href="styles10.css"/i, `href="${cssPath}"`); } getWindowsVersion() { let version = ''; try { - version = (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString(); + version = (libWshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString(); version += '.'; - version += (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString(); + version += (libWshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString(); return version; } catch (e) {} try { - version = WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion'); + version = libWshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion'); return version; } catch (e) {} return '6.1'; @@ -53,26 +53,26 @@ class PopUpBox { } isHtmlDialogSupported() { - if (ppt.isHtmlDialogSupported != 2) return ppt.isHtmlDialogSupported; + if (libSet.isHtmlDialogSupported != 2) return libSet.isHtmlDialogSupported; - if (typeof doc === 'undefined' || !doc) { + if (typeof libDoc === 'undefined' || !libDoc) { this.soFeat.gecko = false; } if (this.soFeat.gecko) { let cache = null; let clText = 'test'; try { - cache = doc.parentWindow.clipboardData.getData('Text'); + cache = libDoc.parentWindow.clipboardData.getData('Text'); } catch (e) {} try { - doc.parentWindow.clipboardData.setData('Text', clText); - clText = doc.parentWindow.clipboardData.getData('Text'); + libDoc.parentWindow.clipboardData.setData('Text', clText); + clText = libDoc.parentWindow.clipboardData.getData('Text'); } catch (e) { this.soFeat.clipboard = false; } if (cache) { // Just in case previous clipboard data is needed try { - doc.parentWindow.clipboardData.setData('Text', cache); + libDoc.parentWindow.clipboardData.setData('Text', cache); } catch (e) {} } if (clText !== 'test') { @@ -82,8 +82,8 @@ class PopUpBox { this.soFeat.clipboard = false; } - ppt.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard || this.isIEInstalled() ? 1 : 0; - if (!ppt.isHtmlDialogSupported) { + libSet.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard || this.isIEInstalled() ? 1 : 0; + if (!libSet.isHtmlDialogSupported) { const caption = 'Show HTML Dialog'; const prompt = `A feature check indicates that Spider Monkey Panel show html dialog isn't supported by the current operating system. @@ -95,15 +95,15 @@ Supported-1; unsupported-0`; let ns = ''; let status = 'ok' try { - ns = utils.InputBox(0, prompt, caption, ppt.isHtmlDialogSupported, true); + ns = utils.InputBox(0, prompt, caption, libSet.isHtmlDialogSupported, true); } catch (e) { status = 'cancel'; } if (status != 'cancel') { - ppt.isHtmlDialogSupported = ns == 0 ? 0 : 1; + libSet.isHtmlDialogSupported = ns == 0 ? 0 : 1; } } - return ppt.isHtmlDialogSupported; + return libSet.isHtmlDialogSupported; } isIEInstalled() { @@ -124,6 +124,6 @@ Supported-1; unsupported-0`; } window_ok_callback(status, clicked) { - if (clicked) ppt.panelSourceMsg = false; + if (clicked) libSet.panelSourceMsg = false; } } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-properties.js b/profile/georgia-reborn/scripts/Library/scripts/lib-properties.js index eb754856..0d19d34c 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-properties.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-properties.js @@ -1,10 +1,10 @@ 'use strict'; -class PanelPropertyLib { +class LibPanelProperty { constructor(name, default_value) { this.name = name; this.default_value = default_value; - this.value = ppt.get(this.name, default_value); + this.value = libSet.get(this.name, default_value); } // * METHODS * // @@ -15,13 +15,13 @@ class PanelPropertyLib { set(new_value) { if (this.value !== new_value) { - ppt.set(this.name, new_value); + libSet.set(this.name, new_value); this.value = new_value; } } } -class PanelPropertiesLib { +class LibPanelProperties { constructor() { // this.name_list = {}; debug } @@ -62,7 +62,7 @@ class PanelPropertiesLib { add(item) { // this.name_list[item[0]] = 1; debug - this[`${item[2]}_internal`] = new PanelPropertyLib(item[0], item[1]); + this[`${item[2]}_internal`] = new LibPanelProperty(item[0], item[1]); Object.defineProperty(this, item[2], { get() { @@ -87,7 +87,8 @@ class PanelPropertiesLib { } } -let properties = [ +/** @global @type {Array.} */ +let libProperties = [ ['Panel Library - #Show Html Dialog Unsupported-0 Supported-1 Autocheck-2', 2, 'isHtmlDialogSupported'], ['Panel Library - Action Mode', 0, 'actionMode'], ['Panel Library - Alt-Click Action', 1, 'altClickAction'], @@ -324,18 +325,23 @@ let properties = [ ['Panel Library - Zoom Tooltip [Button] (%)', 100, 'zoomTooltipBut'] ]; -const ppt = new PanelPropertiesLib(); -ppt.init('auto', properties); -if (!$Lib.file('C:\\check_local\\1450343922.txt')) ppt.themed = false; - -if (ppt.get('Panel Library - Tree List View')) { - ppt.facetView = ppt.get('Panel Library - Tree List View'); - ppt.set('Panel Library - Tree List View', null); +/** + * The instance of `PanelPropertiesLib` class for library panel property settings. + * @typedef {LibPanelProperties} + * @global + */ +const libSet = new LibPanelProperties(); +libSet.init('auto', libProperties); +if (!$Lib.file('C:\\check_local\\1450343922.txt')) libSet.themed = false; + +if (libSet.get('Panel Library - Tree List View')) { + libSet.facetView = libSet.get('Panel Library - Tree List View'); + libSet.set('Panel Library - Tree List View', null); } -ppt.set('Panel Library - Image Pre-Load Images In Disk Cache', null); -ppt.set('Panel Library - Image Root Collage', null); -ppt.set('Panel Library - Image Show Index Number', null); -ppt.set('Panel Library - Image Show Index Year Auto', null); -ppt.set('Panel Library - Node: Item Show Duration', null); -ppt.set('Panel Library - Node [Squares]: Windows 0 or 1', null); -properties = undefined; +libSet.set('Panel Library - Image Pre-Load Images In Disk Cache', null); +libSet.set('Panel Library - Image Root Collage', null); +libSet.set('Panel Library - Image Show Index Number', null); +libSet.set('Panel Library - Image Show Index Year Auto', null); +libSet.set('Panel Library - Node: Item Show Duration', null); +libSet.set('Panel Library - Node [Squares]: Windows 0 or 1', null); +libProperties = undefined; diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-scrollbar.js b/profile/georgia-reborn/scripts/Library/scripts/lib-scrollbar.js index 890ea22e..af7eac07 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-scrollbar.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-scrollbar.js @@ -1,6 +1,6 @@ 'use strict'; -class Scrollbar { +class LibScrollbar { constructor() { this.active = true; this.alpha = 255; @@ -54,7 +54,7 @@ class Scrollbar { }; this.narrow = { - show: ppt.sbarShow == 1, + show: libSet.sbarShow == 1, x: 0 }; @@ -90,8 +90,8 @@ class Scrollbar { this.duration = { drag: 200, - inertia: ppt.durationTouchFlick, - full: ppt.durationScroll + inertia: libSet.durationTouchFlick, + full: libSet.durationScroll }; this.duration.scroll = Math.round(this.duration.full * 0.8); @@ -107,28 +107,28 @@ class Scrollbar { }, 16); this.hideDebounce = $Lib.debounce(() => { - if ((ppt.countsRight || ppt.itemShowStatistics) && !panel.imgView && !ppt.facetView && (!ppt.rootNode || pop.inlineRoot)) return; + if ((libSet.countsRight || libSet.itemShowStatistics) && !lib.panel.imgView && !libSet.facetView && (!libSet.rootNode || lib.pop.inlineRoot)) return; if (this.scrollbar.zone) return; this.active = false; this.cur_active = this.active; this.hover = false; this.cur_hover = false; this.alpha = this.alpha1; - panel.treePaint(); + lib.panel.treePaint(); }, 5000); this.minimiseDebounce = $Lib.debounce(() => { - if (this.scrollbar.zone) return panel.treePaint(); + if (this.scrollbar.zone) return lib.panel.treePaint(); this.narrow.show = true; - if (ppt.sbarShow == 1) but.setScrollBtnsHide(true, true); + if (libSet.sbarShow == 1) lib.but.setScrollBtnsHide(true, true); this.scrollbar.cur_zone = this.scrollbar.zone; this.hover = false; this.cur_hover = false; this.alpha = this.alpha1; - panel.treePaint(); + lib.panel.treePaint(); }, 1000); - this.updDebounce = $Lib.debounce(() => lib.treeState(false, ppt.rememberTree), 400); + this.updDebounce = $Lib.debounce(() => lib.lib.treeState(false, libSet.rememberTree), 400); this.setCol(); } @@ -148,22 +148,22 @@ class Scrollbar { calcItem_y() { const ix = Math.round(this.delta / this.row.h + 0.4); - panel.tree.x = Math.round(this.row.h * ix - this.delta); - panel.tree.y = Math.round(this.row.h * ix + panel.search.h - this.delta); + lib.panel.tree.x = Math.round(this.row.h * ix - this.delta); + lib.panel.tree.y = Math.round(this.row.h * ix + lib.panel.search.h - this.delta); } checkScroll(new_scroll, type, memory) { const b = $Lib.clamp(new_scroll, 0, this.max_scroll); if (b == this.scroll) return; this.scroll = b; - if (ppt.smooth && !memory) { + if (libSet.smooth && !memory) { this.elap = 16; this.event = type || 'scroll'; - panel.tree.x = 0; - panel.tree.y = panel.search.h; + lib.panel.tree.x = 0; + lib.panel.tree.y = lib.panel.search.h; this.start = this.delta; if (this.event != 'drag') { - if (this.bar.isDragging && Math.abs(this.delta - this.scroll) > (!panel.imgView ? this.scrollbar.height : this.scrollbar.height * 3)) this.event = 'barFast'; + if (this.bar.isDragging && Math.abs(this.delta - this.scroll) > (!lib.panel.imgView ? this.scrollbar.height : this.scrollbar.height * 3)) this.event = 'barFast'; this.clock = Date.now(); if (!this.draw_timer) { this.scrollTimer(); @@ -177,119 +177,119 @@ class Scrollbar { } draw(gr) { - if (!ppt.sbarShow) return; + if (!libSet.sbarShow) return; if (this.drawBar && this.active) { let sbar_x = this.x; let sbar_w = this.w; let sbar_y = this.y; let sbar_h = this.h; - if (ppt.sbarShow === 1) { + if (libSet.sbarShow === 1) { sbar_x = !this.narrow.show ? this.x : this.narrow.x; - sbar_w = !this.narrow.show ? this.w : ui.sbar.narrowWidth; + sbar_w = !this.narrow.show ? this.w : lib.ui.sbar.narrowWidth; sbar_y = !this.narrow.show ? this.y : this.narrow.y; - sbar_h = !this.narrow.show ? this.h : ui.sbar.narrowWidth; + sbar_h = !this.narrow.show ? this.h : lib.ui.sbar.narrowWidth; } // Non-Reborn/Random theme scrollbar colors - ui.col.sbarNormalRGBA = RGBtoRGBA(ui.col.sbarNormal, this.alpha2 + this.alpha); - ui.col.sbarHoveredRGBA = RGBtoRGBA(ui.col.sbarHovered, this.alpha); - ui.col.sbarDragRGBA = RGBtoRGBA(ui.col.sbarDrag, this.alpha2); + lib.ui.col.sbarNormalRGBA = RGBtoRGBA(lib.ui.col.sbarNormal, this.alpha2 + this.alpha); + lib.ui.col.sbarHoveredRGBA = RGBtoRGBA(lib.ui.col.sbarHovered, this.alpha); + lib.ui.col.sbarDragRGBA = RGBtoRGBA(lib.ui.col.sbarDrag, this.alpha2); // Reborn/Random theme scrollbar colors - black text color - ui.col.darkAccentRGBA_75 = RGBtoRGBA(col.darkAccent_75, this.alpha - 50); - ui.col.darkAccentRGBA_50 = this.hover ? RGBtoRGBA(col.lightAccent_50, this.alpha) : RGBtoRGBA(col.darkAccent_75, this.alpha); + lib.ui.col.darkAccentRGBA_75 = RGBtoRGBA(grCol.darkAccent_75, this.alpha - 50); + lib.ui.col.darkAccentRGBA_50 = this.hover ? RGBtoRGBA(grCol.lightAccent_50, this.alpha) : RGBtoRGBA(grCol.darkAccent_75, this.alpha); // Reborn/Random theme scrollbar colors - white text color - ui.col.lightAccentRGBA_80 = RGBtoRGBA(col.lightAccent_80, this.alpha); - ui.col.lightAccentRGBA_50 = RGBtoRGBA(col.lightAccent_50, this.alpha); - ui.col.lightAccentRGBA2_50 = RGBtoRGBA(col.lightAccent_50, this.alpha2); + lib.ui.col.lightAccentRGBA_80 = RGBtoRGBA(grCol.lightAccent_80, this.alpha); + lib.ui.col.lightAccentRGBA_50 = RGBtoRGBA(grCol.lightAccent_50, this.alpha); + lib.ui.col.lightAccentRGBA2_50 = RGBtoRGBA(grCol.lightAccent_50, this.alpha2); // Reborn/Random theme scrollbar colors - for nearly white album art - ui.col.lightAccentRGBA_100 = RGBA(140, 140, 140, this.alpha2 - this.alpha); - ui.col.lightAccentRGBA2_100 = RGBA(255, 255, 255, this.alpha); - ui.col.lightAccentRGBA3_100 = RGBA(255, 255, 255, this.alpha2); + lib.ui.col.lightAccentRGBA_100 = RGBA(140, 140, 140, this.alpha2 - this.alpha); + lib.ui.col.lightAccentRGBA2_100 = RGBA(255, 255, 255, this.alpha); + lib.ui.col.lightAccentRGBA3_100 = RGBA(255, 255, 255, this.alpha2); - let thumbColors = [ui.col.sbarNormalRGBA, ui.col.sbarHoveredRGBA, ui.col.sbarDragRGBA]; + let thumbColors = [lib.ui.col.sbarNormalRGBA, lib.ui.col.sbarHoveredRGBA, lib.ui.col.sbarDragRGBA]; - if (g_pl_colors.bg !== RGB(255, 255, 255) && !pref.styleRebornFusion && !pref.styleRebornFusion2) { - if ((pref.theme === 'reborn' || pref.theme === 'random') && lightBg) { - thumbColors = [ui.col.darkAccentRGBA_75, ui.col.darkAccentRGBA_50, ui.col.lightAccentRGBA2_50]; + if (pl.col.bg !== RGB(255, 255, 255) && !grSet.styleRebornFusion && !grSet.styleRebornFusion2) { + if ((grSet.theme === 'reborn' || grSet.theme === 'random') && grCol.lightBg) { + thumbColors = [lib.ui.col.darkAccentRGBA_75, lib.ui.col.darkAccentRGBA_50, lib.ui.col.lightAccentRGBA2_50]; } - else if ((pref.theme === 'reborn' || pref.theme === 'random') && !lightBg) { - thumbColors = [ui.col.lightAccentRGBA_80, ui.col.lightAccentRGBA_50, ui.col.lightAccentRGBA2_50]; + else if ((grSet.theme === 'reborn' || grSet.theme === 'random') && !grCol.lightBg) { + thumbColors = [lib.ui.col.lightAccentRGBA_80, lib.ui.col.lightAccentRGBA_50, lib.ui.col.lightAccentRGBA2_50]; } - if ((pref.theme === 'reborn' || pref.theme === 'random') && (imgBrightness > 230)) { - thumbColors = [ui.col.lightAccentRGBA_100, ui.col.lightAccentRGBA2_100, ui.col.lightAccentRGBA3_100]; + if ((grSet.theme === 'reborn' || grSet.theme === 'random') && (grCol.imgBrightness > 230)) { + thumbColors = [lib.ui.col.lightAccentRGBA_100, lib.ui.col.lightAccentRGBA2_100, lib.ui.col.lightAccentRGBA3_100]; } } - switch (ui.sbar.type) { + switch (lib.ui.sbar.type) { case 0: - if (ppt.rowStripes && ppt.sbarShow == 2 && !this.vertical) gr.FillSolidRect(this.x, this.y, this.w, this.h, ui.col.rowStripes /*ui.col.bg1*/); + if (libSet.rowStripes && libSet.sbarShow == 2 && !this.vertical) gr.FillSolidRect(this.x, this.y, this.w, this.h, lib.ui.col.rowStripes /*ui.col.bg1*/); if (this.vertical) { - gr.FillSolidRect(sbar_x + (this.narrow.show ? ui.sbar.narrowWidth - SCALE(1) : RES_4K ? 0 : -1), this.y + this.bar.y, sbar_w, this.bar.h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); + gr.FillSolidRect(sbar_x + (this.narrow.show ? lib.ui.sbar.narrowWidth - SCALE(1) : RES._4K ? 0 : -1), this.y + this.bar.y, sbar_w, this.bar.h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); } else { - gr.FillSolidRect(this.x + this.bar.x, sbar_y + (this.narrow.show ? ui.sbar.narrowWidth - SCALE(1) : 0), this.bar.h, sbar_h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); - if (!this.narrow.show || ppt.sbarShow != 1) { + gr.FillSolidRect(this.x + this.bar.x, sbar_y + (this.narrow.show ? lib.ui.sbar.narrowWidth - SCALE(1) : 0), this.bar.h, sbar_h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); + if (!this.narrow.show || libSet.sbarShow != 1) { gr.FillSolidRect(this.x + this.bar.x, sbar_y, this.bar.h, sbar_h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); } } break; case 1: if (this.vertical) { - if (!this.narrow.show || ppt.sbarShow != 1) { + if (!this.narrow.show || libSet.sbarShow != 1) { gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); - if (pref.theme.startsWith('custom')) { - gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, ui.col.sbarHoveredRGBA); + if (grSet.theme.startsWith('custom')) { + gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, lib.ui.col.sbarHoveredRGBA); } } } - else if (!this.narrow.show || ppt.sbarShow != 1) { + else if (!this.narrow.show || libSet.sbarShow != 1) { gr.FillSolidRect(this.x + this.bar.x, sbar_y, this.bar.h, sbar_h, this.bar.isDragging ? thumbColors[2] : this.hover ? thumbColors[1] : thumbColors[0]); - if (pref.theme.startsWith('custom')) { - gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, ui.col.sbarHoveredRGBA); + if (grSet.theme.startsWith('custom')) { + gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, lib.ui.col.sbarHoveredRGBA); } } break; case 2: if (this.vertical) { - ui.theme.SetPartAndStateID(6, 1); - gr.FillSolidRect(sbar_x - SCALE(12), this.y - SCALE(8), this.w + SCALE(50), this.h + SCALE(g_properties.row_h) * 2 - SCALE(6), ui.col.bg); - if (!this.narrow.show || ppt.sbarShow != 1) ui.theme.DrawThemeBackground(gr, sbar_x, this.y, sbar_w, this.h); - ui.theme.SetPartAndStateID(3, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3); - ui.theme.DrawThemeBackground(gr, sbar_x + (this.narrow.show ? ui.sbar.narrowWidth - SCALE(1) : 0), this.y + this.bar.y, sbar_w, this.bar.h); - if (pref.styleBlend && albumArt && blendedImg) { - gr.DrawImage(blendedImg, sbar_x - SCALE(12), this.y - SCALE(8), ww, wh, sbar_x - SCALE(12), this.y - SCALE(6), blendedImg.Width, blendedImg.Height); + lib.ui.theme.SetPartAndStateID(6, 1); + gr.FillSolidRect(sbar_x - SCALE(12), this.y - SCALE(8), this.w + SCALE(50), this.h + SCALE(plSet.row_h) * 2 - SCALE(6), lib.ui.col.bg); + if (!this.narrow.show || libSet.sbarShow != 1) lib.ui.theme.DrawThemeBackground(gr, sbar_x, this.y, sbar_w, this.h); + lib.ui.theme.SetPartAndStateID(3, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3); + lib.ui.theme.DrawThemeBackground(gr, sbar_x + (this.narrow.show ? lib.ui.sbar.narrowWidth - SCALE(1) : 0), this.y + this.bar.y, sbar_w, this.bar.h); + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) { + gr.DrawImage(grCol.imgBlended, sbar_x - SCALE(12), this.y - SCALE(8), grm.ui.ww, grm.ui.wh, sbar_x - SCALE(12), this.y - SCALE(6), grCol.imgBlended.Width, grCol.imgBlended.Height); } } else { - ui.theme.SetPartAndStateID(4, 1); - if (!this.narrow.show || ppt.sbarShow != 1) ui.theme.DrawThemeBackground(gr, this.x, sbar_y, this.w, sbar_h); - ui.theme.SetPartAndStateID(2, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3); - ui.theme.DrawThemeBackground(gr, this.x + this.bar.x, sbar_y + (this.narrow.show ? ui.sbar.narrowWidth - SCALE(1) : 0), this.bar.h, sbar_h); + lib.ui.theme.SetPartAndStateID(4, 1); + if (!this.narrow.show || libSet.sbarShow != 1) lib.ui.theme.DrawThemeBackground(gr, this.x, sbar_y, this.w, sbar_h); + lib.ui.theme.SetPartAndStateID(2, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3); + lib.ui.theme.DrawThemeBackground(gr, this.x + this.bar.x, sbar_y + (this.narrow.show ? lib.ui.sbar.narrowWidth - SCALE(1) : 0), this.bar.h, sbar_h); } break; } - if (!panel.imgView || !img.letter.show || !this.bar.isDragging) return; - const ix = img.style.vertical ? (Math.ceil((panel.m.y + sbar.delta - img.panel.y) / img.row.h) - 1) * (!ppt.albumArtFlowMode ? img.columns : 1) : Math.ceil((panel.m.x + sbar.delta - img.panel.x) / img.columnWidth) - 1; - if (ix < 0 || ix > pop.tree.length - 1) return; - let letter = panel.lines == 1 || !ppt.albumArtFlipLabels ? pop.tree[ix].grp : pop.tree[ix].lot; - if (panel.colMarker) letter = letter.replace(/@!#.*?@!#/g, ''); - if (img.letter.no != 0) { - if (img.letter.albumArtYearAuto) { + if (!lib.panel.imgView || !libImg.letter.show || !this.bar.isDragging) return; + const ix = libImg.style.vertical ? (Math.ceil((lib.panel.m.y + lib.sbar.delta - libImg.panel.y) / libImg.row.h) - 1) * (!libSet.albumArtFlowMode ? libImg.columns : 1) : Math.ceil((lib.panel.m.x + lib.sbar.delta - libImg.panel.x) / libImg.columnWidth) - 1; + if (ix < 0 || ix > lib.pop.tree.length - 1) return; + let letter = lib.panel.lines == 1 || !libSet.albumArtFlipLabels ? lib.pop.tree[ix].grp : lib.pop.tree[ix].lot; + if (lib.panel.colMarker) letter = letter.replace(/@!#.*?@!#/g, ''); + if (libImg.letter.no != 0) { + if (libImg.letter.albumArtYearAuto) { let sub = letter.substring(0, 4); if (/\d{4}/.test(sub)) letter = sub; else { sub = letter.substring(0, 6); - letter = /(\[|\()\d{4}(\]|\))/.test(sub) ? sub : letter.substring(0, img.letter.no); + letter = /(\[|\()\d{4}(\]|\))/.test(sub) ? sub : letter.substring(0, libImg.letter.no); } - } else letter = letter.substring(0, img.letter.no); + } else letter = letter.substring(0, libImg.letter.no); } - const letter_w = gr.CalcTextWidth(letter, ui.font.main) + img.letter.w; - const w1 = Math.min(letter_w, ui.w - img.panel.x - img.letter.w); - const w2 = Math.min(letter_w, ui.w - img.panel.x) + 1; - if (img.style.vertical) gr.FillSolidRect(ui.x, this.y + this.bar.y + this.bar.h / 2 - img.text.h / 2, w2, img.text.h + 2, ui.col.nowPlayingBg); - if (img.style.vertical) gr.FillSolidRect(ui.x, this.y + this.bar.y + this.bar.h / 2 - img.text.h / 2, w2, img.text.h + 2, ui.col.nowPlayingBg); - if (img.style.vertical) gr.GdiDrawText(letter, ui.font.main, ui.col.text_nowp, ui.x + ui.l.w + img.letter.w / 2, this.y + this.bar.y + this.bar.h / 2 - img.text.h / 2, w1, img.text.h, panel.lc); - else gr.GdiDrawText(letter, ui.font.main, ui.col.text_nowp, ui.x + this.x + this.bar.x + this.bar.h / 2 - w1 / 2, sbar_y - img.text.h, w1, img.text.h, panel.cc); + const letter_w = gr.CalcTextWidth(letter, lib.ui.font.main) + libImg.letter.w; + const w1 = Math.min(letter_w, lib.ui.w - libImg.panel.x - libImg.letter.w); + const w2 = Math.min(letter_w, lib.ui.w - libImg.panel.x) + 1; + if (libImg.style.vertical) gr.FillSolidRect(lib.ui.x, this.y + this.bar.y + this.bar.h / 2 - libImg.text.h / 2, w2, libImg.text.h + 2, lib.ui.col.nowPlayingBg); + if (libImg.style.vertical) gr.FillSolidRect(lib.ui.x, this.y + this.bar.y + this.bar.h / 2 - libImg.text.h / 2, w2, libImg.text.h + 2, lib.ui.col.nowPlayingBg); + if (libImg.style.vertical) gr.GdiDrawText(letter, lib.ui.font.main, lib.ui.col.text_nowp, lib.ui.x + lib.ui.l.w + libImg.letter.w / 2, this.y + this.bar.y + this.bar.h / 2 - libImg.text.h / 2, w1, libImg.text.h, lib.panel.lc); + else gr.GdiDrawText(letter, lib.ui.font.main, lib.ui.col.text_nowp, lib.ui.x + this.x + this.bar.x + this.bar.h / 2 - w1 / 2, sbar_y - libImg.text.h, w1, libImg.text.h, lib.panel.cc); } } @@ -316,7 +316,7 @@ class Scrollbar { } lbtn_dn(p_x, p_y) { - if (!ppt.sbarShow && ppt.touchControl) return this.tap(p_x, p_y); + if (!libSet.sbarShow && libSet.touchControl) return this.tap(p_x, p_y); const x = p_x - this.x; const y = p_y - this.y; let dir; @@ -324,7 +324,7 @@ class Scrollbar { case this.vertical: if (x > this.w || y < 0 || y > this.h || this.row.count <= this.rows_drawn) return; if (x < 0) { - if (!ppt.touchControl) return; + if (!libSet.touchControl) return; else return this.tap(p_x, p_y); } if (y < this.but_h || y > this.h - this.but_h) return; @@ -333,7 +333,7 @@ class Scrollbar { if (y < this.bar.y || y > this.bar.y + this.bar.h) this.shiftPage(dir, this.nearestY(y)); else { // on bar this.bar.isDragging = true; - but.Dn = true; + lib.but.Dn = true; window.RepaintRect(this.x, this.y, this.w, this.h); this.initial.drag.y = y - this.bar.y + this.but_h; } @@ -341,7 +341,7 @@ class Scrollbar { case !this.vertical: if (y > this.h || x < 0 || x > this.w || this.row.count <= this.rows_drawn) return; if (y < 0) { - if (!ppt.touchControl) return; + if (!libSet.touchControl) return; else return this.tap(p_x, p_y); } if (x < this.but_h || x > this.w - this.but_h) return; @@ -350,7 +350,7 @@ class Scrollbar { if (x < this.bar.x || x > this.bar.x + this.bar.h) this.shiftPage(dir, this.nearestX(x)); else { // on bar this.bar.isDragging = true; - but.Dn = true; + lib.but.Dn = true; window.RepaintRect(this.x, this.y, this.w, this.h); this.initial.drag.x = x - this.bar.x + this.but_h; } @@ -364,7 +364,7 @@ class Scrollbar { clearInterval(this.touch.ticker); if (!this.touch.counter) this.track(true); if (Math.abs(this.touch.velocity) > this.touch.min && Date.now() - this.touch.startTime < 300) { - this.touch.amplitude = ppt.flickDistance * this.touch.velocity * ppt.touchStep; + this.touch.amplitude = libSet.flickDistance * this.touch.velocity * libSet.touchStep; this.touch.timestamp = Date.now(); this.checkScroll(Math.round((this.scroll + this.touch.amplitude) / this.row.h) * this.row.h, 'inertia'); } @@ -373,8 +373,8 @@ class Scrollbar { else window.RepaintRect(this.x, this.y, this.w, this.h); if (this.bar.isDragging) { this.bar.isDragging = false; - img.loadThrottle(); - but.Dn = false; + libImg.loadThrottle(); + lib.but.Dn = false; } this.initial.drag.x = 0; this.initial.drag.y = 0; @@ -387,8 +387,8 @@ class Scrollbar { leave() { if (this.touch.dn) this.touch.dn = false; - if (!men.r_up) this.scrollbar.zone = false; - if (this.bar.isDragging || ppt.sbarShow == 1) return; + if (!lib.men.r_up) this.scrollbar.zone = false; + if (this.bar.isDragging || libSet.sbarShow == 1) return; this.hover = !this.hover; this.paint(); this.hover = false; @@ -396,7 +396,7 @@ class Scrollbar { } logScroll() { - this.scrollIX = $Lib.clamp(Math.round(sbar.scroll / this.row.h + 0.4), 0, pop.tree.length - 1); + this.scrollIX = $Lib.clamp(Math.round(lib.sbar.scroll / this.row.h + 0.4), 0, lib.pop.tree.length - 1); } metrics(x, y, w, h, rows_drawn, row_h, vertical) { @@ -406,24 +406,24 @@ class Scrollbar { this.y = Math.round(y); this.w = w; this.h = h; - sbar.w = pref.libraryAutoHideScrollbar && ppt.sbarShow === 1 ? 0 : SCALE(12); - ui.sbar.but_w = SCALE(12); + lib.sbar.w = grSet.libraryAutoHideScrollbar && libSet.sbarShow === 1 ? 0 : SCALE(12); + lib.ui.sbar.but_w = SCALE(12); } else { this.x = SCALE(40); - this.y = ui.y + ui.h - SCALE(32); + this.y = lib.ui.y + lib.ui.h - SCALE(32); this.w = w - SCALE(40); this.h = h; - sbar.h = pref.libraryAutoHideScrollbar && ppt.sbarShow === 1 ? 0 : SCALE(12); - ui.sbar.but_w = SCALE(12); + lib.sbar.h = grSet.libraryAutoHideScrollbar && libSet.sbarShow === 1 ? 0 : SCALE(12); + lib.ui.sbar.but_w = SCALE(12); } this.rows_drawn = rows_drawn; this.row.h = row_h; - this.but_h = ui.sbar.but_h; - this.scrollStep = $Lib.clamp(ppt.scrollStep, 0, 10); - if (panel.imgView && this.scrollStep != 0) this.scrollStep = Math.max(Math.round(this.scrollStep /= 3), 1); + this.but_h = lib.ui.sbar.but_h; + this.scrollStep = $Lib.clamp(libSet.scrollStep, 0, 10); + if (lib.panel.imgView && this.scrollStep != 0) this.scrollStep = Math.max(Math.round(this.scrollStep /= 3), 1); // draw info this.scrollbar.height = this.vertical ? Math.round(this.h - this.but_h * 2) : Math.round(this.w - this.but_h * 2); - this.bar.h = Math.max(Math.round(this.scrollbar.height * this.rows_drawn / this.row.count), $Lib.clamp(this.scrollbar.height / 2, 5, ppt.sbarShow == 2 ? ppt.sbarGripHeight : ppt.sbarGripHeight * 2)); + this.bar.h = Math.max(Math.round(this.scrollbar.height * this.rows_drawn / this.row.count), $Lib.clamp(this.scrollbar.height / 2, 5, libSet.sbarShow == 2 ? libSet.sbarGripHeight : libSet.sbarGripHeight * 2)); this.scrollbar.travel = this.scrollbar.height - this.bar.h; // scrolling info this.scrollable_lines = this.rows_drawn > 0 ? this.row.count - this.rows_drawn : 0; @@ -432,69 +432,69 @@ class Scrollbar { this.bar.x = this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); this.drag_distance_per_row = this.scrollbar.travel / this.scrollable_lines; // panel info - if (this.vertical) this.narrow.x = pref.layout === 'artwork' ? this.x - 5 : this.x + this.w - $Lib.clamp(ui.sbar.narrowWidth, 5, this.w); - else this.narrow.y = this.y + this.h - $Lib.clamp(ui.sbar.narrowWidth, 5, this.h); - - // panel.tree.w = ui.w - - // Math.max(ppt.sbarShow && this.scrollable_lines > 0 ? (!ppt.countsRight && !ppt.itemShowStatistics) || ppt.facetView ? ui.sbar.sp + ui.sz.sel : - // ppt.sbarShow == 2 ? ui.sbar.sp + ui.sz.margin : - // ppt.sbarShow == 1 ? (ui.w - this.narrow.x) + ui.sz.marginRight + Math.max(this.w - 11, 0) : ui.sz.sel : ui.sz.sel, ui.sz.margin); - panel.tree.w = // * Controlling this.countsRight from populate - ui.w - Math.max(ppt.sbarShow && this.scrollable_lines > 0 ? (!ppt.countsRight && !ppt.itemShowStatistics) || ppt.facetView ? ui.sbar.sp + ui.sz.sel : ui.sz.sel : - ui.sz.sel, ui.sz.margin); - - pop.id = ui.id.tree + ppt.fullLineSelection + panel.tree.w + panel.imgView + ppt.albumArtLabelType + ppt.albumArtFlipLabels + ppt.albumArtFlowMode; - panel.tree.stripe.w = ppt.sbarShow == 2 && this.scrollable_lines > 0 ? ui.w - ui.sbar.sp - ui.sz.pad : ui.w; - panel.tree.sel.w = sbar.w ? ui.w - SCALE(42) : ui.w; // ppt.sbarShow == 2 && this.scrollable_lines > 0 ? ui.w - ui.sbar.sp - ui.sz.pad * 2 : ui.w - ui.sz.pad * 2; + if (this.vertical) this.narrow.x = grSet.layout === 'artwork' ? this.x - 5 : this.x + this.w - $Lib.clamp(lib.ui.sbar.narrowWidth, 5, this.w); + else this.narrow.y = this.y + this.h - $Lib.clamp(lib.ui.sbar.narrowWidth, 5, this.h); + + // lib.panel.tree.w = lib.ui.w - + // Math.max(libSet.sbarShow && this.scrollable_lines > 0 ? (!libSet.countsRight && !libSet.itemShowStatistics) || libSet.facetView ? lib.ui.sbar.sp + lib.ui.sz.sel : + // libSet.sbarShow == 2 ? lib.ui.sbar.sp + lib.ui.sz.margin : + // libSet.sbarShow == 1 ? (lib.ui.w - this.narrow.x) + lib.ui.sz.marginRight + Math.max(this.w - 11, 0) : lib.ui.sz.sel : lib.ui.sz.sel, lib.ui.sz.margin); + lib.panel.tree.w = // * Controlling this.countsRight from populate + lib.ui.w - Math.max(libSet.sbarShow && this.scrollable_lines > 0 ? (!libSet.countsRight && !libSet.itemShowStatistics) || libSet.facetView ? lib.ui.sbar.sp + lib.ui.sz.sel : lib.ui.sz.sel : + lib.ui.sz.sel, lib.ui.sz.margin); + + lib.pop.id = lib.ui.id.tree + libSet.fullLineSelection + lib.panel.tree.w + lib.panel.imgView + libSet.albumArtLabelType + libSet.albumArtFlipLabels + libSet.albumArtFlowMode; + lib.panel.tree.stripe.w = libSet.sbarShow == 2 && this.scrollable_lines > 0 ? lib.ui.w - lib.ui.sbar.sp - lib.ui.sz.pad : lib.ui.w; + lib.panel.tree.sel.w = lib.sbar.w ? lib.ui.w - SCALE(42) : lib.ui.w; // libSet.sbarShow == 2 && this.scrollable_lines > 0 ? lib.ui.w - lib.ui.sbar.sp - lib.ui.sz.pad * 2 : lib.ui.w - lib.ui.sz.pad * 2; this.max_scroll = this.scrollable_lines * this.row.h; - if (panel.imgView && this.vertical && this.row.h > ui.h - panel.search.h - (ui.style.topBarShow ? 0 : ui.sz.margin)) this.max_scroll -= this.row.h; // if (panel.imgView && this.vertical && this.row.h > ui.h - img.panel.h) this.max_scroll -= this.row.h; - if (panel.imgView && !this.vertical && this.row.h > ui.w) this.max_scroll -= this.row.h; - if (ppt.sbarShow != 1) but.setScrollBtnsHide(); + if (lib.panel.imgView && this.vertical && this.row.h > lib.ui.h - lib.panel.search.h - (lib.ui.style.topBarShow ? 0 : lib.ui.sz.margin)) this.max_scroll -= this.row.h; // if (lib.panel.imgView && this.vertical && this.row.h > lib.ui.h - img.panel.h) this.max_scroll -= this.row.h; + if (lib.panel.imgView && !this.vertical && this.row.h > lib.ui.w) this.max_scroll -= this.row.h; + if (libSet.sbarShow != 1) lib.but.setScrollBtnsHide(); } move(p_x, p_y) { this.active = true; - if (p_x > this.x - SCALE(25) && p_x < this.x + SCALE(25) && (sbar.vertical ? p_y > this.y : p_y > this.y - SCALE(20) && p_y < this.y + SCALE(30))) { + if (p_x > this.x - SCALE(25) && p_x < this.x + SCALE(25) && (lib.sbar.vertical ? p_y > this.y : p_y > this.y - SCALE(20) && p_y < this.y + SCALE(30))) { this.scrollbar.zone = true; this.narrow.show = false; - if (ppt.sbarShow == 1 && this.scrollbar.zone != this.scrollbar.cur_zone) { - but.setScrollBtnsHide(!this.scrollbar.zone || this.scrollable_lines < 1, true); + if (libSet.sbarShow == 1 && this.scrollbar.zone != this.scrollbar.cur_zone) { + lib.but.setScrollBtnsHide(!this.scrollbar.zone || this.scrollable_lines < 1, true); this.scrollbar.cur_zone = this.scrollbar.zone; } // * Automatic Scrollbar Hide - show - if (sbar.vertical && this.scrollable_lines > 0) sbar.w = SCALE(12); - if (!sbar.vertical && this.scrollable_lines > 0) sbar.h = SCALE(12); + if (lib.sbar.vertical && this.scrollable_lines > 0) lib.sbar.w = SCALE(12); + if (!lib.sbar.vertical && this.scrollable_lines > 0) lib.sbar.h = SCALE(12); } - else if (ppt.sbarShow === 1 && !this.bar.isDragging) { // * Automatic Scrollbar Hide - hide - if (pref.libraryAutoHideScrollbar && this.scrollable_lines > 0 && sbar.vertical) sbar.w = 0; - if (pref.libraryAutoHideScrollbar && this.scrollable_lines > 0 && !sbar.vertical) sbar.h = 0; + else if (libSet.sbarShow === 1 && !this.bar.isDragging) { // * Automatic Scrollbar Hide - hide + if (grSet.libraryAutoHideScrollbar && this.scrollable_lines > 0 && lib.sbar.vertical) lib.sbar.w = 0; + if (grSet.libraryAutoHideScrollbar && this.scrollable_lines > 0 && !lib.sbar.vertical) lib.sbar.h = 0; this.resetAuto(); this.scrollbar.zone = false; } - if (ppt.sbarShow == 1) { + if (libSet.sbarShow == 1) { this.minimiseDebounce(); this.hideDebounce(); } - if (ppt.touchControl) { + if (libSet.touchControl) { const delta = this.touch.reference - (this.vertical ? p_y : p_x); if (delta > this.touch.diff || delta < -this.touch.diff) { this.touch.reference = this.vertical ? p_y : p_x; - if (ppt.flickDistance) this.touch.offset = $Lib.clamp(this.touch.offset + delta, 0, this.max_scroll); + if (libSet.flickDistance) this.touch.offset = $Lib.clamp(this.touch.offset + delta, 0, this.max_scroll); if (this.touch.dn) { - ui.id.dragDrop = ui.id.touch_dn = -1; + lib.ui.id.dragDrop = lib.ui.id.touch_dn = -1; } } } - if (this.touch.dn && !vk.k('zoom')) { + if (this.touch.dn && !lib.vk.k('zoom')) { const now = Date.now(); if (now - this.touch.startTime > 300) this.touch.startTime = now; this.touch.lastDn = now; - this.checkScroll(this.initial.scr + (this.vertical ? this.initial.y - p_y : this.initial.x - p_x) * ppt.touchStep, ppt.touchStep == 1 ? 'drag' : 'scroll'); + this.checkScroll(this.initial.scr + (this.vertical ? this.initial.y - p_y : this.initial.x - p_x) * libSet.touchStep, libSet.touchStep == 1 ? 'drag' : 'scroll'); return; } const x = p_x - this.x; const y = p_y - this.y; - this.hover = this.vertical ? !(x < 0 || x > this.w || y > this.bar.y + this.bar.h || y < this.bar.y || but.Dn) : !(y < 0 || y > this.h || x > this.bar.x + this.bar.h || x < this.bar.x || but.Dn); + this.hover = this.vertical ? !(x < 0 || x > this.w || y > this.bar.y + this.bar.h || y < this.bar.y || lib.but.Dn) : !(y < 0 || y > this.h || x > this.bar.x + this.bar.h || x < this.bar.x || lib.but.Dn); if (!this.bar.timer && (this.hover != this.cur_hover || this.active != this.cur_active)) { this.init = false; this.paint(); @@ -536,21 +536,21 @@ class Scrollbar { if (Elapsed > Duration) return End; if (Event == 'drag') return; const n = Elapsed / Duration; - return Start + (End - Start) * ease[Event](n); + return Start + (End - Start) * libEase[Event](n); } reset() { this.delta = this.scroll = 0; this.metrics(this.x, this.y, this.w, this.h, this.rows_drawn, this.row.h, this.vertical); - lib.treeState(false, ppt.rememberTree); + lib.lib.treeState(false, libSet.rememberTree); } resetAuto() { this.minimiseDebounce.cancel(); this.hideDebounce.cancel(); - if (!ppt.sbarShow) but.setScrollBtnsHide(true); - if (ppt.sbarShow == 1) { - but.setScrollBtnsHide(true, true); + if (!libSet.sbarShow) lib.but.setScrollBtnsHide(true); + if (libSet.sbarShow == 1) { + lib.but.setScrollBtnsHide(true, true); this.narrow.show = true; this.scrollbar.cur_zone = false; } @@ -569,7 +569,7 @@ class Scrollbar { if (this.vertical) this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); else this.bar.x = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); - ppt.rememberTree ? lib.treeState(false, ppt.rememberTree) : panel.treePaint(); + libSet.rememberTree ? lib.lib.treeState(false, libSet.rememberTree) : lib.panel.treePaint(); this.calcItem_y(); clearTimeout(this.draw_timer); this.draw_timer = null; @@ -577,18 +577,18 @@ class Scrollbar { scrollRound() { if (this.vertical) { - if (panel.tree.y == panel.search.h) return; - this.checkScroll((panel.tree.y < panel.search.h ? Math.floor(this.scroll / this.row.h) : Math.ceil(this.scroll / this.row.h)) * this.row.h); + if (lib.panel.tree.y == lib.panel.search.h) return; + this.checkScroll((lib.panel.tree.y < lib.panel.search.h ? Math.floor(this.scroll / this.row.h) : Math.ceil(this.scroll / this.row.h)) * this.row.h); } else { - if (panel.tree.x == 0) return; - this.checkScroll((panel.tree.x < 0 ? Math.floor(this.scroll / this.row.h) : Math.ceil(this.scroll / this.row.h)) * this.row.h); + if (lib.panel.tree.x == 0) return; + this.checkScroll((lib.panel.tree.x < 0 ? Math.floor(this.scroll / this.row.h) : Math.ceil(this.scroll / this.row.h)) * this.row.h); } } setRows(row_count) { if (!row_count) { - panel.tree.x = 0; - panel.tree.y = panel.search.h; + lib.panel.tree.x = 0; + lib.panel.tree.y = lib.panel.search.h; } this.row.count = row_count; this.metrics(this.x, this.y, this.w, this.h, this.rows_drawn, this.row.h, this.vertical); @@ -596,8 +596,8 @@ class Scrollbar { scrollMemory(h, j) { let scroll = !h ? j * this.row.h : (j - 3) * this.row.h; - if (panel.imgView && img.style.vertical) { - scroll /= img.columns; + if (lib.panel.imgView && libImg.style.vertical) { + scroll /= libImg.columns; scroll = scroll - scroll % this.row.h; } this.checkScroll(scroll, 'full', true); @@ -607,8 +607,8 @@ class Scrollbar { const b = $Lib.clamp(this.scrollIX * this.row.h, 0, this.max_scroll); if (b == this.scroll) return; this.scroll = b; - panel.tree.x = 0; - panel.tree.y = panel.search.h; + lib.panel.tree.x = 0; + lib.panel.tree.y = lib.panel.search.h; this.scrollThrottle(); this.updDebounce(); } @@ -622,7 +622,7 @@ class Scrollbar { scrollTo() { if (this.vertical) this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); else this.bar.x = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h); - panel.treePaint(); + lib.panel.treePaint(); } scrollToEnd() { @@ -630,42 +630,42 @@ class Scrollbar { } setCol() { - const opaque = ui.getOpaque(); + const opaque = lib.ui.getOpaque(); this.alpha = - !ui.sbar.col ? 75 : - pref.theme.startsWith('custom') ? 0 : - ['nblue', 'ngreen', 'nred', 'ngold'].includes(pref.theme) ? 220 : - pref.styleBlackAndWhite ? 175 : - pref.styleBlackAndWhite2 ? 152 : - pref.styleBlend ? 175 : + !lib.ui.sbar.col ? 75 : + grSet.theme.startsWith('custom') ? 0 : + ['nblue', 'ngreen', 'nred', 'ngold'].includes(grSet.theme) ? 220 : + grSet.styleBlackAndWhite ? 175 : + grSet.styleBlackAndWhite2 ? 152 : + grSet.styleBlend ? 175 : 100; this.alpha1 = this.alpha; - this.alpha2 = !ui.sbar.col ? 128 : 255 - this.inStep = ui.sbar.type && ui.sbar.col ? 12 : 18; - switch (ui.sbar.type) { + this.alpha2 = !lib.ui.sbar.col ? 128 : 255 + this.inStep = lib.ui.sbar.type && lib.ui.sbar.col ? 12 : 18; + switch (lib.ui.sbar.type) { case 0: - switch (ui.sbar.col) { + switch (lib.ui.sbar.col) { case 0: - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i); - this.col.max = opaque ? $Lib.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, 192), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, 192); + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, this.alpha + i), lib.ui.col.bg) : RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, this.alpha + i); + this.col.max = opaque ? $Lib.RGBAtoRGB(RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 192), lib.ui.col.bg) : RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 192); break; case 1: - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(ui.col.text & RGBA(255, 255, 255, this.alpha + i), ui.col.bg) : ui.col.text & RGBA(255, 255, 255, this.alpha + i); - this.col.max = opaque ? $Lib.RGBAtoRGB(ui.col.text & 0x99ffffff, ui.col.bg) : ui.col.text & 0x99ffffff; + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(lib.ui.col.text & RGBA(255, 255, 255, this.alpha + i), lib.ui.col.bg) : lib.ui.col.text & RGBA(255, 255, 255, this.alpha + i); + this.col.max = opaque ? $Lib.RGBAtoRGB(lib.ui.col.text & 0x99ffffff, lib.ui.col.bg) : lib.ui.col.text & 0x99ffffff; break; } break; case 1: - switch (ui.sbar.col) { + switch (lib.ui.sbar.col) { case 0: - this.col.bg = opaque ? $Lib.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, 15), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, 15); - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i); - this.col.max = opaque ? $Lib.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, 192), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, 192); + this.col.bg = opaque ? $Lib.RGBAtoRGB(RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 15), lib.ui.col.bg) : RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 15); + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, this.alpha + i), lib.ui.col.bg) : RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, this.alpha + i); + this.col.max = opaque ? $Lib.RGBAtoRGB(RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 192), lib.ui.col.bg) : RGBA(lib.ui.col.t, lib.ui.col.t, lib.ui.col.t, 192); break; case 1: - this.col.bg = opaque ? $Lib.RGBAtoRGB(ui.col.text & 0x15ffffff, ui.col.bg) : ui.col.text & 0x15ffffff; - for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(ui.col.text & RGBA(255, 255, 255, this.alpha + i), ui.col.bg) : ui.col.text & RGBA(255, 255, 255, this.alpha + i); - this.col.max = opaque ? $Lib.RGBAtoRGB(ui.col.text & 0x99ffffff, ui.col.bg) : ui.col.text & 0x99ffffff; + this.col.bg = opaque ? $Lib.RGBAtoRGB(lib.ui.col.text & 0x15ffffff, lib.ui.col.bg) : lib.ui.col.text & 0x15ffffff; + for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $Lib.RGBAtoRGB(lib.ui.col.text & RGBA(255, 255, 255, this.alpha + i), lib.ui.col.bg) : lib.ui.col.text & RGBA(255, 255, 255, this.alpha + i); + this.col.max = opaque ? $Lib.RGBAtoRGB(lib.ui.col.text & 0x99ffffff, lib.ui.col.bg) : lib.ui.col.text & 0x99ffffff; break; } break; @@ -711,7 +711,7 @@ class Scrollbar { if (!this.touch.offset) this.touch.offset = p_x; } this.touch.velocity = this.touch.amplitude = 0; - if (!ppt.flickDistance) return; + if (!libSet.flickDistance) return; this.touch.frame = this.touch.offset; this.touch.startTime = this.touch.timestamp = Date.now(); clearInterval(this.touch.ticker); @@ -723,8 +723,8 @@ class Scrollbar { this.touch.counter++; const now = Date.now(); if (now - this.touch.lastDn < 10000 && this.touch.counter == 4) { - ui.id.touch_dn = -1; - panel.last_pressed_coord = { + lib.ui.id.touch_dn = -1; + lib.panel.last_pressed_coord = { x: -1, y: -1 }; diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-search.js b/profile/georgia-reborn/scripts/Library/scripts/lib-search.js index fdb01956..9343a03e 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-search.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-search.js @@ -1,13 +1,13 @@ 'use strict'; -class Search { +class LibSearch { constructor() { this.cx = 0; this.end = 0; this.lbtnDn = false; this.lg = []; this.log = []; - this.menu = $Lib.jsonParse(ppt.searchHistory, []); + this.menu = $Lib.jsonParse(libSet.searchHistory, []); this.offset = 0; this.paste = false; this.start = 0; @@ -19,81 +19,81 @@ class Search { let item = -1; const itemPresent = this.menu.some((v, i) => { item = i; - return v.search == panel.search.txt; + return v.search == lib.panel.search.txt; }); if (itemPresent) { this.menu[item].accessed = Date.now(); return; } - if (!panel.search.txt) return; - this.menu.push({ search: panel.search.txt, accessed: Date.now() }); + if (!lib.panel.search.txt) return; + this.menu.push({ search: lib.panel.search.txt, accessed: Date.now() }); if (this.menu.length > 25) { this.menu.sort((a, b) => b.accessed - a.accessed); this.menu.length = 25; } - this.menu.sort((a, b) => pop.collator.compare(a.search, b.search)); - ppt.searchHistory = JSON.stringify(this.menu); + this.menu.sort((a, b) => lib.pop.collator.compare(a.search, b.search)); + libSet.searchHistory = JSON.stringify(this.menu); }, 3000); } // * METHODS * // calcText() { - $Lib.gr(1, 1, false, g => this.txt_w = g.CalcTextWidth(panel.search.txt.substr(this.offset), ui.font.main, true)); + $Lib.gr(1, 1, false, g => this.txt_w = g.CalcTextWidth(lib.panel.search.txt.substr(this.offset), lib.ui.font.main, true)); } clear() { - if (!panel.search.txt) return; - lib.time.Reset(); - pop.cache.search = {}; + if (!lib.panel.search.txt) return; + lib.lib.time.Reset(); + lib.pop.cache.search = {}; this.offset = this.start = this.end = this.cx = 0; - panel.search.txt = ''; - but.setSearchBtnsHide(); - panel.searchPaint(); - lib.setNodes(); // comment out to always stop child panels clearing [if memory on & item selected that's used & so won't clear] - pop.checkAutoHeight(); - pop.notifySelection(); + lib.panel.search.txt = ''; + lib.but.setSearchBtnsHide(); + lib.panel.searchPaint(); + lib.lib.setNodes(); // comment out to always stop child panels clearing [if memory on & item selected that's used & so won't clear] + lib.pop.checkAutoHeight(); + lib.pop.notifySelection(); } draw(gr) { - if (!ppt.searchShow) return; - this.start = $Lib.clamp(this.start, 0, panel.search.txt.length); - this.end = $Lib.clamp(this.end, 0, panel.search.txt.length); - this.cx = $Lib.clamp(this.cx, 0, panel.search.txt.length); - if (ui.style.fill) gr.FillSolidRect(0, 1, ui.w, ui.row.h - 4, 0x60000000); - if (ui.style.pen == 2) gr.DrawRoundRect(0, 2, ui.w - 1, ui.row.h - 4, 4, 4, 1, ui.style.pen_c); - if (panel.search.txt) { + if (!libSet.searchShow) return; + this.start = $Lib.clamp(this.start, 0, lib.panel.search.txt.length); + this.end = $Lib.clamp(this.end, 0, lib.panel.search.txt.length); + this.cx = $Lib.clamp(this.cx, 0, lib.panel.search.txt.length); + if (lib.ui.style.fill) gr.FillSolidRect(0, 1, lib.ui.w, lib.ui.row.h - 4, 0x60000000); + if (lib.ui.style.pen == 2) gr.DrawRoundRect(0, 2, lib.ui.w - 1, lib.ui.row.h - 4, 4, 4, 1, lib.ui.style.pen_c); + if (lib.panel.search.txt) { this.drawSel(gr); this.getOffset(gr); - gr.GdiDrawText(panel.search.txt.substr(this.offset), ui.font.main, ui.col.search, ui.x + panel.search.x, ui.y, panel.search.w, panel.search.sp, panel.l); + gr.GdiDrawText(lib.panel.search.txt.substr(this.offset), lib.ui.font.main, lib.ui.col.search, lib.ui.x + lib.panel.search.x, lib.ui.y, lib.panel.search.w, lib.panel.search.sp, lib.panel.l); } - else if (!ui.img.blurDark) { - gr.GdiDrawText('Search', ui.font.search, ui.col.txt_box, ui.x + panel.search.x, ui.y, panel.search.w, panel.search.sp, panel.l); + else if (!lib.ui.img.blurDark) { + gr.GdiDrawText('Search', lib.ui.font.search, lib.ui.col.txt_box, lib.ui.x + lib.panel.search.x, lib.ui.y, lib.panel.search.w, lib.panel.search.sp, lib.panel.l); } else { gr.SetTextRenderingHint(5); - gr.DrawString('Search', ui.font.search, ui.col.txt_box, ui.x + panel.search.x, ui.y - -1, panel.search.w, panel.search.sp, panel.s_lc); + gr.DrawString('Search', lib.ui.font.search, lib.ui.col.txt_box, lib.ui.x + lib.panel.search.x, lib.ui.y - -1, lib.panel.search.w, lib.panel.search.sp, lib.panel.s_lc); } this.drawCursor(gr); } drawCursor(gr) { - if (panel.search.active && panel.search.cursor && this.start == this.end && this.cx >= this.offset) { - const lx = panel.search.x + this.get_cursor_x(this.cx); - gr.DrawLine(ui.x + lx, ui.y + panel.search.sp * 0.3, ui.x + lx, ui.y + panel.search.sp * 0.6, ui.l.w, ui.col.text); + if (lib.panel.search.active && lib.panel.search.cursor && this.start == this.end && this.cx >= this.offset) { + const lx = lib.panel.search.x + this.get_cursor_x(this.cx); + gr.DrawLine(lib.ui.x + lx, lib.ui.y + lib.panel.search.sp * 0.3, lib.ui.x + lx, lib.ui.y + lib.panel.search.sp * 0.6, lib.ui.l.w, lib.ui.col.text); } } drawSel(gr) { if (this.start == this.end) return; - const clamp = panel.search.x + panel.search.w; - gr.DrawLine(Math.min(panel.search.x + this.get_cursor_x(this.start), clamp), ui.y + (panel.search.sp / 2), Math.min(panel.search.x + this.get_cursor_x(this.end), clamp), ui.y + (panel.search.sp / 2), ui.row.h - 3, ui.col.searchSel); + const clamp = lib.panel.search.x + lib.panel.search.w; + gr.DrawLine(Math.min(lib.panel.search.x + this.get_cursor_x(this.start), clamp), lib.ui.y + (lib.panel.search.sp / 2), Math.min(lib.panel.search.x + this.get_cursor_x(this.end), clamp), lib.ui.y + (lib.panel.search.sp / 2), lib.ui.row.h - 3, lib.ui.col.searchSel); } get_cursor_x(pos) { let x = 0; $Lib.gr(1, 1, false, g => { - if (pos >= this.offset) x = g.CalcTextWidth(panel.search.txt.substr(this.offset, pos - this.offset), ui.font.main, true); + if (pos >= this.offset) x = g.CalcTextWidth(lib.panel.search.txt.substr(this.offset, pos - this.offset), lib.ui.font.main, true); }); return x; } @@ -101,10 +101,10 @@ class Search { getCursorChrPos(x) { let i = 0; $Lib.gr(1, 1, false, g => { - const nx = x - panel.search.x; + const nx = x - lib.panel.search.x; let pos = 0; - for (i = this.offset; i < panel.search.txt.length; i++) { - pos += g.CalcTextWidth(panel.search.txt.substr(i, 1), ui.font.main, true); + for (i = this.offset; i < lib.panel.search.txt.length; i++) { + pos += g.CalcTextWidth(lib.panel.search.txt.substr(i, 1), lib.ui.font.main, true); if (pos >= nx + 3) break; } }); @@ -113,29 +113,29 @@ class Search { getOffset(gr) { let j = 0; - let tx = gr.CalcTextWidth(panel.search.txt.substr(this.offset, this.cx - this.offset), ui.font.main, true); - while (tx >= panel.search.w && panel.search.w > 0 && j < 500) { + let tx = gr.CalcTextWidth(lib.panel.search.txt.substr(this.offset, this.cx - this.offset), lib.ui.font.main, true); + while (tx >= lib.panel.search.w && lib.panel.search.w > 0 && j < 500) { j++; this.offset++; - tx = gr.CalcTextWidth(panel.search.txt.substr(this.offset, this.cx - this.offset), ui.font.main, true); + tx = gr.CalcTextWidth(lib.panel.search.txt.substr(this.offset, this.cx - this.offset), lib.ui.font.main, true); } } lbtn_dblclk(x, y) { - if (y < ui.y + panel.search.h && x > but.q.h + but.margin && x < panel.search.x + panel.search.w && panel.search.txt.length) { - panel.search.cursor = false; + if (y < lib.ui.y + lib.panel.search.h && x > lib.but.q.h + lib.but.margin && x < lib.panel.search.x + lib.panel.search.w && lib.panel.search.txt.length) { + lib.panel.search.cursor = false; this.start = 0; - this.end = panel.search.txt.length; - panel.searchPaint(); + this.end = lib.panel.search.txt.length; + lib.panel.searchPaint(); } } lbtn_dn(x, y) { - panel.searchPaint(); - this.lbtnDn = panel.search.active = (y < ui.y + panel.search.h && x > but.q.x - but.margin / 2 + but.q.h + but.margin && x < panel.search.x + panel.search.w); + lib.panel.searchPaint(); + this.lbtnDn = lib.panel.search.active = (y < lib.ui.y + lib.panel.search.h && x > lib.but.q.x - lib.but.margin / 2 + lib.but.q.h + lib.but.margin && x < lib.panel.search.x + lib.panel.search.w); if (!this.lbtnDn) { this.offset = this.start = this.end = this.cx = 0; - timer.clear(timer.cursor); + lib.timer.clear(lib.timer.cursor); return; } else { if (this.shift) { @@ -145,101 +145,101 @@ class Search { this.cx = this.getCursorChrPos(x); this.start = this.end = this.cx; } - timer.searchCursor(true); + lib.timer.searchCursor(true); } - panel.searchPaint(); + lib.panel.searchPaint(); } lbtn_up() { - if (this.start != this.end) timer.clear(timer.cursor); + if (this.start != this.end) lib.timer.clear(lib.timer.cursor); this.lbtnDn = false; } move(x, y) { - if (y > panel.search.h || !this.lbtnDn) return; + if (y > lib.panel.search.h || !this.lbtnDn) return; const cursorChrPos = this.getCursorChrPos(x); const c_x = this.get_cursor_x(cursorChrPos); let l; this.calcText(); if (cursorChrPos < this.start) { if (cursorChrPos < this.end) { - if (c_x < panel.search.x && this.offset > 0) this.offset--; + if (c_x < lib.panel.search.x && this.offset > 0) this.offset--; } - else if (cursorChrPos > this.end && c_x + panel.search.x > panel.search.x + panel.search.w) { - l = (this.txt_w > panel.search.w) ? this.txt_w - panel.search.w : 0; + else if (cursorChrPos > this.end && c_x + lib.panel.search.x > lib.panel.search.x + lib.panel.search.w) { + l = (this.txt_w > lib.panel.search.w) ? this.txt_w - lib.panel.search.w : 0; if (l > 0) this.offset++; } this.end = cursorChrPos; } else if (cursorChrPos > this.start) { - if (c_x + panel.search.x > panel.search.x + panel.search.w) { - l = (this.txt_w > panel.search.w) ? this.txt_w - panel.search.w : 0; + if (c_x + lib.panel.search.x > lib.panel.search.x + lib.panel.search.w) { + l = (this.txt_w > lib.panel.search.w) ? this.txt_w - lib.panel.search.w : 0; if (l > 0) this.offset++; } this.end = cursorChrPos; } this.cx = cursorChrPos; - panel.searchPaint(); + lib.panel.searchPaint(); } on_char(code, force) { let searchDone = false; let text = String.fromCharCode(code) || ''; - if (force) panel.search.active = true; - if (!panel.search.active || code == 5 || code == 9 || code == 12) return; - panel.search.cursor = false; - panel.pos = -1; + if (force) lib.panel.search.active = true; + if (!lib.panel.search.active || code == 5 || code == 9 || code == 12) return; + lib.panel.search.cursor = false; + lib.panel.pos = -1; switch (code) { - case vk.enter: - if (ppt.searchEnter || ppt.searchSend == 1) { - lib.upd_search = true; - lib.time.Reset(); - pop.cache.search = {}; - lib.setNodes(); - panel.setHeight(true); - if (panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.list.Count ? lib.list : panel.list); - else if (!panel.search.txt.length) pop.notifySelection(); - lib.search.cancel(); + case lib.vk.enter: + if (libSet.searchEnter || libSet.searchSend == 1) { + lib.lib.upd_search = true; + lib.lib.time.Reset(); + lib.pop.cache.search = {}; + lib.lib.setNodes(); + lib.panel.setHeight(true); + if (lib.panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.lib.list.Count ? lib.lib.list : lib.panel.list); + else if (!lib.panel.search.txt.length) lib.pop.notifySelection(); + lib.lib.search.cancel(); this.logHistory(); searchDone = true; } - if (ppt.searchSend == 1 || ppt.searchEnter && ppt.searchSend == 2) pop.load(panel.list, false, false, pop.autoPlay.send, !ppt.sendToCur, false); + if (libSet.searchSend == 1 || libSet.searchEnter && libSet.searchSend == 2) lib.pop.load(lib.panel.list, false, false, lib.pop.autoPlay.send, !libSet.sendToCur, false); break; - case vk.escape: + case lib.vk.escape: this.clear(); return; - case vk.redo: - this.lg.push(panel.search.txt); + case lib.vk.redo: + this.lg.push(lib.panel.search.txt); if (this.lg.length > 30) this.lg.shift(); if (this.log.length > 0) { - panel.search.txt = `${this.log.pop()}`; + lib.panel.search.txt = `${this.log.pop()}`; this.cx++; } break; - case vk.undo: - this.log.push(panel.search.txt); + case lib.vk.undo: + this.log.push(lib.panel.search.txt); if (this.log.length > 30) this.lg.shift(); - if (this.lg.length > 0) panel.search.txt = `${this.lg.pop()}`; + if (this.lg.length > 0) lib.panel.search.txt = `${this.lg.pop()}`; break; - case vk.selAll: + case lib.vk.selAll: this.start = 0; - this.end = panel.search.txt.length; + this.end = lib.panel.search.txt.length; break; - case vk.copy: - if (this.start != this.end) $Lib.setClipboardData(panel.search.txt.substring(this.start, this.end)); + case lib.vk.copy: + if (this.start != this.end) $Lib.setClipboardData(lib.panel.search.txt.substring(this.start, this.end)); break; - case vk.cut: - if (this.start != this.end) $Lib.setClipboardData(panel.search.txt.substring(this.start, this.end)); // fall through - case vk.back: + case lib.vk.cut: + if (this.start != this.end) $Lib.setClipboardData(lib.panel.search.txt.substring(this.start, this.end)); // fall through + case lib.vk.back: this.record(); if (this.start == this.end) { if (this.cx > 0) { - panel.search.txt = panel.search.txt.substr(0, this.cx - 1) + panel.search.txt.substr(this.cx, panel.search.txt.length - this.cx); + lib.panel.search.txt = lib.panel.search.txt.substr(0, this.cx - 1) + lib.panel.search.txt.substr(this.cx, lib.panel.search.txt.length - this.cx); if (this.offset > 0) this.offset--; this.cx--; } } - else if (this.end - this.start == panel.search.txt.length) { - panel.search.txt = ''; + else if (this.end - this.start == lib.panel.search.txt.length) { + lib.panel.search.txt = ''; this.cx = 0; } else if (this.start > 0) { @@ -247,11 +247,11 @@ class Search { const en = this.end; this.start = Math.min(st, en); this.end = Math.max(st, en); - panel.search.txt = panel.search.txt.substring(0, this.start) + panel.search.txt.substring(this.end, panel.search.txt.length); + lib.panel.search.txt = lib.panel.search.txt.substring(0, this.start) + lib.panel.search.txt.substring(this.end, lib.panel.search.txt.length); this.cx = this.start; } else { - panel.search.txt = panel.search.txt.substring(this.end, panel.search.txt.length); + lib.panel.search.txt = lib.panel.search.txt.substring(this.end, lib.panel.search.txt.length); this.cx = this.start; } this.calcText(); @@ -259,21 +259,21 @@ class Search { this.start = this.cx; this.end = this.start; break; - case vk.ctrlBackspace: + case lib.vk.ctrlBackspace: this.record(); if (this.start != this.end) this.cx = this.end = this.start; if (this.cx > 0) { - const initial = panel.search.txt.length; - const leftSide = panel.search.txt.slice(0, this.cx).trimEnd(); + const initial = lib.panel.search.txt.length; + const leftSide = lib.panel.search.txt.slice(0, this.cx).trimEnd(); let boundary = 0; for (let k = 0; k < leftSide.length; k++) { - if (panel.search.txt[k] == ' ' && panel.search.txt[k + 1] != ' ') boundary = k + 1; + if (lib.panel.search.txt[k] == ' ' && lib.panel.search.txt[k + 1] != ' ') boundary = k + 1; } - panel.search.txt = leftSide.slice(0, boundary) + panel.search.txt.slice(this.cx).trimStart() + lib.panel.search.txt = leftSide.slice(0, boundary) + lib.panel.search.txt.slice(this.cx).trimStart() this.cx = boundary; if (this.offset > 0) { - this.offset -= initial - panel.search.txt.length; + this.offset -= initial - lib.panel.search.txt.length; } } this.calcText(); @@ -284,12 +284,12 @@ class Search { case 'delete': this.record(); if (this.start == this.end) { - if (this.cx < panel.search.txt.length) { - panel.search.txt = panel.search.txt.substr(0, this.cx) + panel.search.txt.substr(this.cx + 1, panel.search.txt.length - this.cx - 1); + if (this.cx < lib.panel.search.txt.length) { + lib.panel.search.txt = lib.panel.search.txt.substr(0, this.cx) + lib.panel.search.txt.substr(this.cx + 1, lib.panel.search.txt.length - this.cx - 1); } } - else if (this.end - this.start == panel.search.txt.length) { - panel.search.txt = ''; + else if (this.end - this.start == lib.panel.search.txt.length) { + lib.panel.search.txt = ''; this.cx = 0; } else if (this.start > 0) { @@ -297,11 +297,11 @@ class Search { const en = this.end; this.start = Math.min(st, en); this.end = Math.max(st, en); - panel.search.txt = panel.search.txt.substring(0, this.start) + panel.search.txt.substring(this.end, panel.search.txt.length); + lib.panel.search.txt = lib.panel.search.txt.substring(0, this.start) + lib.panel.search.txt.substring(this.end, lib.panel.search.txt.length); this.cx = this.start; } else { - panel.search.txt = panel.search.txt.substring(this.end, panel.search.txt.length); + lib.panel.search.txt = lib.panel.search.txt.substring(this.end, lib.panel.search.txt.length); this.cx = this.start; } this.calcText(); @@ -309,25 +309,25 @@ class Search { this.start = this.cx; this.end = this.start; break; - case vk.paste: + case lib.vk.paste: text = $Lib.getClipboardData() || ''; text = text.replace(/(\r\n|\n|\r)/gm, ' '); // fall through default: this.record(); if (this.start == this.end) { - panel.search.txt = panel.search.txt.substring(0, this.cx) + text + panel.search.txt.substring(this.cx); + lib.panel.search.txt = lib.panel.search.txt.substring(0, this.cx) + text + lib.panel.search.txt.substring(this.cx); this.cx += text.length; this.end = this.start = this.cx; } else if (this.end > this.start) { - panel.search.txt = panel.search.txt.substring(0, this.start) + text + panel.search.txt.substring(this.end); + lib.panel.search.txt = lib.panel.search.txt.substring(0, this.start) + text + lib.panel.search.txt.substring(this.end); this.calcText(); this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0; this.cx = this.start + text.length; this.end = this.start = this.cx; } else { - panel.search.txt = panel.search.txt.substring(0, this.end) + text + panel.search.txt.substring(this.start); + lib.panel.search.txt = lib.panel.search.txt.substring(0, this.end) + text + lib.panel.search.txt.substring(this.start); this.calcText(); this.offset = this.offset < this.end - this.start ? this.offset - this.end + this.start : 0; this.cx = this.end + text.length; @@ -335,32 +335,32 @@ class Search { } break; } - if (code == vk.copy || code == vk.selAll) return; - if (!timer.cursor.id) timer.searchCursor(); - but.setSearchBtnsHide(); - panel.searchPaint(); - if (ppt.searchEnter || searchDone) return; - if ((ppt.searchSend == 2 || lib.list.Count > 200000) && panel.search.txt && panel.search.txt.length < 4) { - lib.upd_search = true; - lib.search500(); + if (code == lib.vk.copy || code == lib.vk.selAll) return; + if (!lib.timer.cursor.id) lib.timer.searchCursor(); + lib.but.setSearchBtnsHide(); + lib.panel.searchPaint(); + if (libSet.searchEnter || searchDone) return; + if ((libSet.searchSend == 2 || lib.lib.list.Count > 200000) && lib.panel.search.txt && lib.panel.search.txt.length < 4) { + lib.lib.upd_search = true; + lib.lib.search500(); this.logHistory(); } else { - lib.search500.cancel(); - lib.upd_search = true; - lib.search(); + lib.lib.search500.cancel(); + lib.lib.upd_search = true; + lib.lib.search(); this.logHistory(); } } on_key_down(vkey) { - if (!panel.search.active) return; + if (!lib.panel.search.active) return; switch (vkey) { - case vk.ctrl: + case lib.vk.ctrl: this.ctrl = true; break; - case vk.left: - case vk.right: - if (vkey == vk.left) { + case lib.vk.left: + case lib.vk.right: + if (vkey == lib.vk.left) { if (!this.ctrl) { if (this.offset > 0) { if (this.cx <= this.offset) { @@ -371,7 +371,7 @@ class Search { } else { let boundary = 0; for (let k = this.cx - 1; k > 0; k--) { - if (panel.search.txt[k] != ' ' && panel.search.txt[k - 1] == ' ') { + if (lib.panel.search.txt[k] != ' ' && lib.panel.search.txt[k - 1] == ' ') { boundary = k; break; } @@ -383,12 +383,12 @@ class Search { this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0; } } - if (vkey == vk.right && this.cx < panel.search.txt.length) { + if (vkey == lib.vk.right && this.cx < lib.panel.search.txt.length) { if (!this.ctrl) this.cx++; else { - let boundary = panel.search.txt.length; - for (let k = this.cx; k < panel.search.txt.length; k++) { - if (panel.search.txt[k] == ' ' && panel.search.txt[k + 1] != ' ') { + let boundary = lib.panel.search.txt.length; + for (let k = this.cx; k < lib.panel.search.txt.length; k++) { + if (lib.panel.search.txt[k] == ' ' && lib.panel.search.txt[k + 1] != ' ') { boundary = k + 1; break; } @@ -401,38 +401,38 @@ class Search { this.start = Math.min(this.cx, this.shift_x); this.end = Math.max(this.cx, this.shift_x); } - panel.search.cursor = true; - timer.searchCursor(true); + lib.panel.search.cursor = true; + lib.timer.searchCursor(true); break; - case vk.home: - case vk.end: - if (vkey == vk.home) this.offset = this.start = this.end = this.cx = 0; - else this.start = this.end = this.cx = panel.search.txt.length; + case lib.vk.home: + case lib.vk.end: + if (vkey == lib.vk.home) this.offset = this.start = this.end = this.cx = 0; + else this.start = this.end = this.cx = lib.panel.search.txt.length; if (this.shift) { this.start = Math.min(this.cx, this.shift_x); this.end = Math.max(this.cx, this.shift_x); } - panel.search.cursor = true; - timer.searchCursor(true); + lib.panel.search.cursor = true; + lib.timer.searchCursor(true); break; - case vk.shift: + case lib.vk.shift: this.shift = true; this.shift_x = this.cx; break; - case vk.del: + case lib.vk.del: if (this.ctrl && !this.shift && this.start == this.end) { // ctrl + delete: delete next word this.record(); - const initial = panel.search.txt.length; - const leftSide = panel.search.txt.slice(0, this.cx); - const rightSide = panel.search.txt.slice(this.cx, panel.search.txt.length).trimStart(); + const initial = lib.panel.search.txt.length; + const leftSide = lib.panel.search.txt.slice(0, this.cx); + const rightSide = lib.panel.search.txt.slice(this.cx, lib.panel.search.txt.length).trimStart(); const idx = rightSide.search(/ \b/); const boundary = idx == -1 ? rightSide.length : idx + 1; let newRightSide = rightSide.slice(boundary); if (newRightSide.length && !/\s$/.test(leftSide) && !/^\s/.test(newRightSide)) newRightSide = ` ${newRightSide}`; - panel.search.txt = leftSide + newRightSide; + lib.panel.search.txt = leftSide + newRightSide; this.cx = !/\s$/.test(leftSide) ? leftSide.length + 1 : leftSide.length; if (this.offset > 0) { - this.offset -= initial - panel.search.txt.length; + this.offset -= initial - lib.panel.search.txt.length; } this.calcText(); this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0; @@ -440,15 +440,15 @@ class Search { } else this.on_char('delete'); break; } - panel.searchPaint(); + lib.panel.searchPaint(); } on_key_up(vkey) { - if (!panel.search.active) return; - if (vkey == vk.ctrl) { + if (!lib.panel.search.active) return; + if (vkey == lib.vk.ctrl) { this.ctrl = false; } - if (vkey == vk.shift) { + if (vkey == lib.vk.shift) { this.shift = false; this.shift_x = this.cx; } @@ -456,27 +456,27 @@ class Search { rbtn_up(x, y) { this.paste = !!$Lib.getClipboardData(); - searchMenu.load(x, y); + libSearchMenu.load(x, y); } record() { - this.lg.push(panel.search.txt); + this.lg.push(lib.panel.search.txt); this.log = []; if (this.lg.length > 30) this.lg.shift(); } focus() { - panel.searchPaint(); - panel.search.active = true; + lib.panel.searchPaint(); + lib.panel.search.active = true; this.shift = false; - this.start = this.end = this.cx = panel.search.x; - panel.search.cursor = true; - timer.searchCursor(true); - panel.searchPaint(); + this.start = this.end = this.cx = lib.panel.search.x; + lib.panel.search.cursor = true; + lib.timer.searchCursor(true); + lib.panel.searchPaint(); } } -class Find { +class LibFind { constructor() { this.arc1 = 5; this.arc2 = 4; @@ -496,12 +496,12 @@ class Find { draw(gr) { if (this.jSearch) { gr.SetSmoothingMode(4); - this.j.w = gr.CalcTextWidth(this.jSearch, /*ui.font.find*/ ft.notification) + 25; - gr.FillRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, col.popupBg); + this.j.w = gr.CalcTextWidth(this.jSearch, /*ui.font.find*/ grFont.notification) + 25; + gr.FillRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, grCol.popupBg); gr.DrawRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, 1, 0x64000000); gr.DrawRoundRect(this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w - 2, this.j.h - 2, this.arc2, this.arc2, 1, 0x28ffffff); - // gr.GdiDrawText(this.jSearch, ui.font.find, RGB(0, 0, 0), this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w, this.j.h, panel.cc); // Drop shadow not needed - gr.GdiDrawText(this.jSearch, /*ui.font.find*/ ft.notification, this.jump_search ? col.popupText : 0xffff4646, this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, panel.cc); + // gr.GdiDrawText(this.jSearch, lib.ui.font.find, RGB(0, 0, 0), this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w, this.j.h, lib.panel.cc); // Drop shadow not needed + gr.GdiDrawText(this.jSearch, /* lib.ui.font.find */ grFont.notification, this.jump_search ? grCol.popupText : 0xffff4646, this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, lib.panel.cc); gr.SetSmoothingMode(0); } } @@ -511,13 +511,13 @@ class Find { } on_char(code) { - if (panel.search.active || utils.IsKeyPressed(0x11)) return; + if (lib.panel.search.active || utils.IsKeyPressed(0x11)) return; const text = String.fromCharCode(code); switch (code) { - case vk.back: + case lib.vk.back: this.jSearch = this.jSearch.substr(0, this.jSearch.length - 1); break; - case vk.enter: + case lib.vk.enter: this.jSearch = ''; return; default: @@ -525,22 +525,22 @@ class Find { break; } const playlistItems = plman.GetPlaylistItems(plman.ActivePlaylist); - const search = fb.TitleFormat(pref.jumpSearchComposerOnly ? '%composer%' : '$if2(%album artist%, %artist%)').EvalWithMetadbs(playlistItems); + const search = fb.TitleFormat(grSet.jumpSearchComposerOnly ? '%composer%' : '$if2(%album artist%, %artist%)').EvalWithMetadbs(playlistItems); let focusIndex = plman.GetPlaylistFocusItemIndex(plman.ActivePlaylist); let advance = false; let foundInPlaylist = false; let foundInLibrary = false; // * Library advance - if (panel.pos >= 0 && panel.pos < pop.tree.length) { - const char = pop.tree[panel.pos].name.replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase(); - if (pop.tree[panel.pos].sel && char == text && this.allEqual(this.jSearch)) { + if (lib.panel.pos >= 0 && lib.panel.pos < lib.pop.tree.length) { + const char = lib.pop.tree[lib.panel.pos].name.replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase(); + if (lib.pop.tree[lib.panel.pos].sel && char == text && this.allEqual(this.jSearch)) { this.jSearch = this.jSearch.slice(0, 1); advance = true; } } // * Playlist advance - else if (focusIndex >= 0 && focusIndex < search.length && (displayPlaylist || displayLibrarySplit(true))) { + else if (focusIndex >= 0 && focusIndex < search.length && (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true))) { const char = search[focusIndex].replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase(); if (char === text && this.allEqual(this.jSearch)) { this.jSearch = this.jSearch.slice(0, 1); @@ -556,8 +556,8 @@ class Find { if (!this.initials) { // reset in buildTree this.initials = {} // * Library advance - if (!displayPlaylist || !displayLibrarySplit(true)) { - pop.tree.forEach((v, i) => { + if (!grm.ui.displayPlaylist || !grm.ui.displayLibrarySplit(true)) { + lib.pop.tree.forEach((v, i) => { if (!v.root) { const nm = v.name.replace(/@!#.*?@!#/g, ''); init = nm.charAt().toLowerCase(); @@ -589,16 +589,16 @@ class Find { this.jump_search = false; // * Library advance - if (panel.pos >= 0 && panel.pos < pop.tree.length) { + if (lib.panel.pos >= 0 && lib.panel.pos < lib.pop.tree.length) { this.matches = this.initials[text]; - this.ix = this.matches.indexOf(panel.pos); + this.ix = this.matches.indexOf(lib.panel.pos); this.ix++; if (this.ix >= this.matches.length) this.ix = 0; - panel.pos = this.matches[this.ix]; + lib.panel.pos = this.matches[this.ix]; this.jump_search = true; } // * Playlist advance - else if (focusIndex >= 0 && focusIndex < search.length && (displayPlaylist || displayLibrarySplit(true))) { + else if (focusIndex >= 0 && focusIndex < search.length && (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true))) { this.matches = this.initials[text]; console.log('Playlist advance results', this.matches); // Debug this.ix = this.matches.indexOf(focusIndex); @@ -609,75 +609,75 @@ class Find { } // * Library advance - if (this.jump_search && !displayLibrarySplit(true)) { - pop.clearSelected(); - pop.sel_items = []; - pop.tree[panel.pos].sel = true; - pop.setPos(panel.pos); - pop.getTreeSel(); - lib.treeState(false, ppt.rememberTree); - panel.treePaint(); - if (panel.imgView) pop.showItem(panel.pos, 'focus'); + if (this.jump_search && !grm.ui.displayLibrarySplit(true)) { + lib.pop.clearSelected(); + lib.pop.sel_items = []; + lib.pop.tree[lib.panel.pos].sel = true; + lib.pop.setPos(lib.panel.pos); + lib.pop.getTreeSel(); + lib.lib.treeState(false, libSet.rememberTree); + lib.panel.treePaint(); + if (lib.panel.imgView) lib.pop.showItem(lib.panel.pos, 'focus'); else { - const row = (panel.pos * ui.row.h - sbar.scroll) / ui.row.h; - if (sbar.rows_drawn - row < 3 || row < 0) sbar.checkScroll((panel.pos + 3) * ui.row.h - sbar.rows_drawn * ui.row.h); + const row = (lib.panel.pos * lib.ui.row.h - lib.sbar.scroll) / lib.ui.row.h; + if (lib.sbar.rows_drawn - row < 3 || row < 0) lib.sbar.checkScroll((lib.panel.pos + 3) * lib.ui.row.h - lib.sbar.rows_drawn * lib.ui.row.h); } - if (ppt.libSource) { - if (pop.autoFill.key) pop.load(pop.sel_items, true, false, false, !ppt.sendToCur, false); - pop.track(pop.autoFill.key); - } else if (panel.pos >= 0 && panel.pos < pop.tree.length) pop.setPlaylistSelection(panel.pos, pop.tree[panel.pos]); + if (libSet.libSource) { + if (lib.pop.autoFill.key) lib.pop.load(lib.pop.sel_items, true, false, false, !libSet.sendToCur, false); + lib.pop.track(lib.pop.autoFill.key); + } else if (lib.panel.pos >= 0 && lib.panel.pos < lib.pop.tree.length) lib.pop.setPlaylistSelection(lib.panel.pos, lib.pop.tree[lib.panel.pos]); } // * Playlist advance - else if (this.jump_search && (displayPlaylist || displayLibrarySplit(true))) { + else if (this.jump_search && (grm.ui.displayPlaylist || grm.ui.displayLibrarySplit(true))) { plman.ClearPlaylistSelection(plman.ActivePlaylist); plman.SetPlaylistFocusItem(plman.ActivePlaylist, focusIndex); plman.SetPlaylistSelectionSingle(plman.ActivePlaylist, focusIndex, true); window.Repaint(); } else { - panel.treePaint(); + lib.panel.treePaint(); } - timer.clear(timer.jsearch2); - timer.jsearch2.id = setTimeout(() => { + lib.timer.clear(lib.timer.jsearch2); + lib.timer.jsearch2.id = setTimeout(() => { this.jSearch = ''; - panel.treePaint(); - timer.jsearch2.id = null; + lib.panel.treePaint(); + lib.timer.jsearch2.id = null; }, 2200); } break; case !advance: if (utils.IsKeyPressed(0x09) || utils.IsKeyPressed(0x11) || utils.IsKeyPressed(0x1B) || utils.IsKeyPressed(0x6A) || utils.IsKeyPressed(0x6D)) return; - if (!panel.search.active) { + if (!lib.panel.search.active) { let pos = -1; - pop.clearSelected(); + lib.pop.clearSelected(); if (!this.jSearch) return; - pop.sel_items = []; + lib.pop.sel_items = []; this.jump_search = true; - panel.treePaint(); - timer.clear(timer.jsearch1); + lib.panel.treePaint(); + lib.timer.clear(lib.timer.jsearch1); - timer.jsearch1.id = setTimeout(() => { + lib.timer.jsearch1.id = setTimeout(() => { // * First search in the Library - pop.tree.some((v, i) => { + lib.pop.tree.some((v, i) => { const name = v.name.replace(/@!#.*?@!#/g, ''); - if (name !== panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() === this.jSearch.toLowerCase()) { + if (name !== lib.panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() === this.jSearch.toLowerCase()) { foundInLibrary = true; pos = i; v.sel = true; - pop.setPos(pos); - if (pop.autoFill.key) pop.getTreeSel(); - lib.treeState(false, ppt.rememberTree); + lib.pop.setPos(pos); + if (lib.pop.autoFill.key) lib.pop.getTreeSel(); + lib.lib.treeState(false, libSet.rememberTree); console.log(`Jumpsearch: "${name}" found in Library`); // Debug, can remove this soon return true; } return false; }); // * If no Library results found, try search query in the Playlist - if (!foundInLibrary && pref.jumpSearchIncludePlaylist) { + if (!foundInLibrary && grSet.jumpSearchIncludePlaylist) { playlistItems.Convert().some((v, i) => { const name = search[i].replace(/@!#.*?@!#/g, ''); - if (name !== panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() === this.jSearch.toLowerCase()) { + if (name !== lib.panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() === this.jSearch.toLowerCase()) { foundInPlaylist = true; foundInLibrary = false; pos = i; @@ -696,49 +696,49 @@ class Find { console.log('Jumpsearch: No results were found'); // Debug, can remove this soon } - panel.treePaint(); + lib.panel.treePaint(); if (foundInLibrary) { - displayPlaylist = pref.libraryLayout === 'split' && displayPlaylist; - displayLibrary = true; - displayBiography = false; - pref.displayLyrics = false; - pop.showItem(pos, 'focus'); - initButtonState(); + grm.ui.displayPlaylist = grSet.libraryLayout === 'split' && grm.ui.displayPlaylist; + grm.ui.displayLibrary = true; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; + lib.pop.showItem(pos, 'focus'); + grm.button.initButtonState(); } - else if (foundInPlaylist && pref.jumpSearchIncludePlaylist) { - displayPlaylist = true; - displayLibrary = pref.libraryLayout === 'split' && displayPlaylist; - displayBiography = false; - pref.displayLyrics = false; + else if (foundInPlaylist && grSet.jumpSearchIncludePlaylist) { + grm.ui.displayPlaylist = true; + grm.ui.displayLibrary = grSet.libraryLayout === 'split' && grm.ui.displayPlaylist; + grm.ui.displayBiography = false; + grm.ui.displayLyrics = false; this.jSearch = ''; // Reset to avoid conflict with other query - initButtonState(); + grm.button.initButtonState(); } - timer.jsearch1.id = null; + lib.timer.jsearch1.id = null; }, 500); - timer.clear(timer.jsearch2); + lib.timer.clear(lib.timer.jsearch2); - timer.jsearch2.id = setTimeout(() => { + lib.timer.jsearch2.id = setTimeout(() => { if (foundInLibrary) { - if (ppt.libSource) { - if (pop.autoFill.key) pop.load(pop.sel_items, true, false, false, !ppt.sendToCur, false); - pop.track(pop.autoFill.key); - } else if (pos >= 0 && pos < pop.tree.length) pop.setPlaylistSelection(pos, pop.tree[pos]); + if (libSet.libSource) { + if (lib.pop.autoFill.key) lib.pop.load(lib.pop.sel_items, true, false, false, !libSet.sendToCur, false); + lib.pop.track(lib.pop.autoFill.key); + } else if (pos >= 0 && pos < lib.pop.tree.length) lib.pop.setPlaylistSelection(pos, lib.pop.tree[pos]); } this.jSearch = ''; - panel.treePaint(); - timer.jsearch2.id = null; + lib.panel.treePaint(); + lib.timer.jsearch2.id = null; }, 1200); } } } on_size() { - this.j.x = Math.round(ui.x + ui.w / 2); - this.j.h = Math.round(ui.row.h * 1.5); - this.j.y = Math.round(ui.y + (ui.h - this.j.h) / 2); + this.j.x = Math.round(lib.ui.x + lib.ui.w / 2); + this.j.h = Math.round(lib.ui.row.h * 1.5); + this.j.y = Math.round(lib.ui.y + (lib.ui.h - this.j.h) / 2); this.arc1 = Math.min(5, this.j.h / 2); this.arc2 = Math.min(4, (this.j.h - 2) / 2); } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-timers.js b/profile/georgia-reborn/scripts/Library/scripts/lib-timers.js index 7d547014..5be03803 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-timers.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-timers.js @@ -1,6 +1,6 @@ 'use strict'; -class Timers { +class LibTimers { constructor() { ['cursor', 'jsearch1', 'jsearch2', 'tt'].forEach(v => this[v] = { id: null @@ -16,17 +16,17 @@ class Timers { searchCursor(clear) { if (clear) this.clear(this.cursor); - if (!panel.search.cursor) panel.search.cursor = true; + if (!lib.panel.search.cursor) lib.panel.search.cursor = true; this.cursor.id = setInterval(() => { - panel.search.cursor = !panel.search.cursor; - panel.searchPaint(); + lib.panel.search.cursor = !lib.panel.search.cursor; + lib.panel.searchPaint(); }, 530); } tooltipLib() { this.clear(this.tt); this.tt.id = setTimeout(() => { - pop.deactivateTooltip(); + lib.pop.deactivateTooltip(); this.tt.id = null; }, 5000); } diff --git a/profile/georgia-reborn/scripts/Library/scripts/lib-utils.js b/profile/georgia-reborn/scripts/Library/scripts/lib-utils.js index b43b064e..8c777d4b 100644 --- a/profile/georgia-reborn/scripts/Library/scripts/lib-utils.js +++ b/profile/georgia-reborn/scripts/Library/scripts/lib-utils.js @@ -1,11 +1,12 @@ 'use strict'; -const my_utilsLib = {}; +/** @global @type {object} */ +const lib_my_utils = {}; -my_utilsLib.scriptInfo = window.ScriptInfo; -// my_utilsLib.packageInfo = utils.GetPackageInfo(my_utilsLib.scriptInfo.PackageId); -my_utilsLib.packagePath = `${basePath}scripts\\library/`; +lib_my_utils.scriptInfo = window.ScriptInfo; +// lib_my_utils.packageInfo = utils.GetPackageInfo(lib_my_utils.scriptInfo.PackageId); +lib_my_utils.packagePath = `${grPath.base}scripts\\library\\`; -my_utilsLib.getAsset = assetFile => utils.ReadTextFile(`${basePath}scripts\\library\\assets/${assetFile}`); -my_utilsLib.getImageAssets = assetFolder => utils.Glob(`${basePath}scripts\\library\\assets\\images/${assetFolder}/*`); -my_utilsLib.getScriptPath = `${basePath}scripts\\library\\scripts\\`; +lib_my_utils.getAsset = assetFile => utils.ReadTextFile(`${grPath.base}scripts\\library\\assets\\${assetFile}`); +lib_my_utils.getImageAssets = assetFolder => utils.Glob(`${grPath.base}scripts\\library\\assets\\images\\${assetFolder}\\*`); +lib_my_utils.getScriptPath = `${grPath.base}scripts\\library\\scripts\\`; diff --git a/profile/georgia-reborn/scripts/Playlist/pl-control-list.js b/profile/georgia-reborn/scripts/Playlist/pl-control-list.js deleted file mode 100644 index dc037cec..00000000 --- a/profile/georgia-reborn/scripts/Playlist/pl-control-list.js +++ /dev/null @@ -1,743 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN PlayList Control * // -// * Author: TT * // -// * Org. Author: TheQwertiest * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2023-09-25 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -///////////////////////////// -// * PLAYLIST PROPERTIES * // -///////////////////////////// -/** - * Adds additional system playlist panel properties to the SMP properties. - */ -g_properties.add_properties( - { - list_left_pad: ['Panel Playlist - Padding left', 0], - list_top_pad: ['Panel Playlist - Padding top', 0], - list_right_pad: ['Panel Playlist - Padding right', 0], - list_bottom_pad: ['Panel Playlist - Padding bottom', 15], - show_scrollbar: ['Panel Playlist - User: Scrollbar.show', true], - scrollbar_right_pad: ['Panel Playlist - User: scrollbar.pad.right', 0], - scrollbar_top_pad: ['Panel Playlist - User: scrollbar.pad.top', 0], - scrollbar_bottom_pad: ['Panel Playlist - User: scrollbar.pad.bottom', 3], - scrollbar_w: ['Panel Playlist - User: scrollbar.width', ''], - row_h: ['Panel Playlist - User: Row.height', 20], - scroll_pos: ['Panel Playlist - System: Scrollbar.position', 0] - } -); - -// * Fixup properties -g_properties.row_h = Math.max(10, g_properties.row_h); - - -//////////////////// -// * BASIC LIST * // -//////////////////// -/** - * A basic list with items to display and a scrollbar. - * Also handles mouse and keyboard events. - */ -class List { - /** - * By default each item is a row of a fixed size. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {ListContent} content The content container. - * @class - */ - constructor(x, y, w, h, content) { - // public: - - /** @type {number} */ - this.x = x; - /** @type {number} */ - this.y = y; - /** @type {number} */ - this.w = w; - /** @type {number} */ - this.h = h; - - /** @const {number}*/ - this.row_h = SCALE(g_properties.row_h); // Also see playlist.reinitialize - - /** @protected {number} */ - this.list_x = this.x + g_properties.list_left_pad; - /** @protected {number} */ - this.list_y = 0; - /** @protected {number} */ - this.list_w = 0; - /** @protected {number} */ - this.list_h = 0; - - /** @protected {number} */ - this.rows_to_draw_precise = 0; - - /** @protected {Array} */ - this.items_to_draw = []; - - // * Mouse and key state - /** @protected {boolean} */ - this.mouse_in = false; - /** @protected {boolean} */ - this.mouse_down = false; - /** @protected {boolean} */ - this.mouse_double_clicked = false; - - // * Scrollbar props - /** - * Row shift is always non-negative. - * @protected {number} - */ - this.row_shift = 0; - /** - * Pixel shift is always non-positive. - * @protected {number} - */ - this.pixel_shift = 0; - /** @protected {boolean} */ - this.is_scrollbar_visible = g_properties.show_scrollbar; - /** @protected {boolean} */ - this.is_scrollbar_available = false; - /** @protected {boolean} */ - this.needs_scrollbar_update = false; - - // * Objects - /** @protected {?ScrollBar} */ - this.scrollbar = undefined; - /** @protected {ListContent} */ - this.cnt = content; - - /** - * @private - * @function - */ - this.throttled_repaint = Throttle(() => { - window.RepaintRect(this.x - 1, playlist.y, this.w + 1, playlist.h); - }, 1000 / 60); - } - - /** - * Draws the list items with a scrollbar if necessary. - * @param {GdiGraphics} gr - */ - on_paint(gr) { - gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); - - if (this.items_to_draw.length) { - for (let i = this.items_to_draw.length - 1; i >= 0; --i) { - this.items_to_draw[i].draw(gr); - } - } - else { - const text_format = g_string_format.align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - gr.DrawString('No rows to display', gdi.Font('Segoe UI Semibold', 24), RGB(70, 70, 70), this.x, this.y, this.w, this.h, text_format); - } - - if (this.is_scrollbar_visible) { - this.scrollbar.paint(gr); - } - } - - /** - * Adjusts the size and position of an element based on the provided parameters. - * Also handles hiding the scrollbar if auto-hide is enabled. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - on_size(w, h, x, y) { - const w_changed = this.w !== w || this.x !== x; - const h_changed = this.h !== h || this.y !== y; - - if (h_changed) { - this.y = y; - this.on_h_size(h); - } - - if (w_changed) { - this.x = x; - this.list_x = this.x + g_properties.list_left_pad; - this.on_w_size(w); - } - - // * Makes sure to always hide scrollbar when auto-hide is enabled - if (pref.playlistAutoHideScrollbar && g_properties.show_scrollbar) { - g_properties.show_scrollbar = false; - } - } - - /** - * Handles mouse movement events and updates the scrollbar position, - * hides the scrollbar automatically, and updates the playlist row hover state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_move(x, y, m) { - if (this.is_scrollbar_visible) { - this.scrollbar.move(x, y); - - if (this.scrollbar.b_is_dragging || this.scrollbar.trace(x, y)) { - return true; - } - } - - // * Automatic scrollbar hide - if (pref.playlistAutoHideScrollbar) { - if (this.scrollbar.trace(x, y)) { - g_properties.show_scrollbar = true; - this.update_scrollbar(); - this.needs_scrollbar_update = true; - } else if (!this.mouse_in || !this.scrollbar.trace(x, y)) { - g_properties.show_scrollbar = false; - if (this.needs_scrollbar_update) { - this.update_scrollbar(); - this.needs_scrollbar_update = false; - } - } - } - // * Update playlist row hover state or automatic scrollbar hide - if (pref.playlistRowHover || pref.playlistAutoHideScrollbar) { - this.repaint(); - } - - this.mouse_in = this.trace(x, y); - - return false; - } - - /** - * Handles left mouse button down events and performs different actions based on the mouse position and other conditions. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_down(x, y, m) { - this.mouse_down = true; - - if (this.mouse_double_clicked) { - return true; - } - - if (this.is_scrollbar_visible && this.scrollbar.trace(x, y)) { - this.scrollbar.lbtn_dn(x, y); - return true; - } - - return false; - } - - /** - * Handles mouse double click events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {string} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_dblclk(x, y, m) { - this.mouse_down = true; - this.mouse_double_clicked = true; - - if (this.is_scrollbar_visible && this.scrollbar.trace(x, y)) { - this.scrollbar.lbtn_dn(x, y); - return true; - } - - return false; - } - - /** - * Handles left mouse button up events and checks if the scrollbar is visible and if it was being dragged. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_up(x, y, m) { - if (!this.mouse_down) { - return true; - } - - this.mouse_double_clicked = false; - this.mouse_down = false; - - if (this.is_scrollbar_visible) { - const wasDragging = this.scrollbar.b_is_dragging; - this.scrollbar.lbtn_up(x, y); - if (wasDragging) { - return true; - } - } - - return false; - } - - /** - * Handles right mouse button up events and shows the context menu on scrollbar if available. - * Also handles the case when mouse is out of the list. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_rbtn_up(x, y, m) { - if (!this.trace(x, y)) { - return true; - } - - if (!this.is_scrollbar_available - || !this.is_scrollbar_visible - || !this.scrollbar.trace(x, y)) { - return false; - } - - const cmm = new ContextMainMenu(); - - this.append_scrollbar_visibility_context_menu_to(cmm); - - if (utils.IsKeyPressed(VK_SHIFT)) { - qwr_utils.append_default_context_menu_to(cmm); - } - - activeMenu = true; - cmm.execute(x, y); - activeMenu = false; - - this.repaint(); - - return true; - } - - /** - * Handles mouse wheel events and checks if a scrollbar is available. - * @param {number} step The amount of scrolling that occurred on the mouse wheel. - */ - on_mouse_wheel(step) { - if (this.is_scrollbar_available) { - this.scrollbar.wheel(step); - } - } - - /** - * Handles mouse leave events and performs actions related to scrollbar and mouse state. - */ - on_mouse_leave() { - if (this.is_scrollbar_available) { - this.scrollbar.leave(); - } - - this.mouse_in = false; - } - - /** - * Checks if a given point (x, y) is within the boundaries of an list item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True if the given x and y coordinates are within the boundaries. - */ - trace(x, y) { - return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; - } - - /** - * Checks if a given point (x, y) is within the boundaries of the list. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True if the given x and y coordinates are within the boundaries. - */ - trace_list(x, y) { - return x >= this.list_x && x < this.list_x + this.list_w && y >= this.list_y && y < this.list_y + this.list_h; - } - - /** - * Invokes a throttled repaint. - */ - repaint() { - this.throttled_repaint(); - } - - /** - * Appends a context menu item to a parent menu that toggles the visibility of a scrollbar in the playlist. - * @param {string} parent_menu The menu to which the scrollbar visibility context menu item will be appended. - */ - append_scrollbar_visibility_context_menu_to(parent_menu) { - parent_menu.append_item('Scrollbar auto-hide', () => { - pref.playlistAutoHideScrollbar = !pref.playlistAutoHideScrollbar; - g_properties.show_scrollbar = !pref.playlistAutoHideScrollbar; - updatePlaylist(); - }, { is_checked: pref.playlistAutoHideScrollbar }); - } - - /** - * Updates the height size of the list. - * @param {number} h The height. - * @protected - */ - on_h_size(h) { - this.h = h; - this.update_list_h_size(); - } - - /** - * Updates the width size of the list. - * @param {number} w The width. - * @protected - */ - on_w_size(w) { - this.w = w; - this.update_list_w_size(); - } - - /** - * Called when playlist data in content is changed and updates the scrollbar. - * @protected - */ - on_list_items_change() { - this.update_scrollbar(); - this.on_content_to_draw_change(); - } - - /** - * Gets the item from an array that intersects with the given mouse coordinates. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {ListItem} The item that is found under the mouse coordinates. - * @protected - */ - get_item_under_mouse(x, y) { - return this.items_to_draw.find(item => item.trace(x, y)); - } - - /** - * Calculates shift parameters and generates a list of items to draw. - * @protected - */ - on_content_to_draw_change() { - this.calculate_shift_params(); - this.items_to_draw = this.cnt.generate_items_to_draw(this.list_y, this.list_h, this.row_shift, this.pixel_shift, this.row_h); - } - - /** - * The scrollbar callback to update the scroll position and content. - * @protected - */ - scrollbar_redraw_callback() { - const invalidPos = (g_properties.scroll_pos || this.scrollbar.scroll) > this.scrollbar.scrollable_lines; // Prevent scroll crash - g_properties.scroll_pos = invalidPos ? 0 : this.scrollbar.scroll; - this.on_content_to_draw_change(); - this.repaint(); - } - - /** - * Initializes the scrollbar with specific dimensions and a callback for redrawing. - * @private - */ - initialize_scrollbar() { - this.is_scrollbar_available = false; - - const scrollbar_x = this.x - SCALE(32) + this.w - playlist_geo.scrollbar_w - playlist_geo.scrollbar_right_pad; - const scrollbar_y = this.y + playlist_geo.scrollbar_top_pad - (RES_4K ? 12 : 5); - const scrollbar_w = SCALE(28); - const scrollbar_h = this.h - (playlist_geo.scrollbar_bottom_pad + playlist_geo.scrollbar_top_pad) + (RES_4K ? 14 : 5); - - if (this.scrollbar) { - this.scrollbar.reset(); - } - - this.scrollbar = new ScrollBar(scrollbar_x, scrollbar_y, scrollbar_w, scrollbar_h, this.row_h, this.scrollbar_redraw_callback.bind(this)); - } - - /** - * Checks if the scrollbar should be displayed based on the total height of the content and updates the scrollbar accordingly. - * @private - */ - update_scrollbar() { - const total_height_in_rows = this.cnt.calculate_total_h_in_rows(); - - if (total_height_in_rows <= this.rows_to_draw_precise) { - this.is_scrollbar_available = false; - g_properties.scroll_pos = 0; - this.on_scrollbar_visibility_change(false); - } - else if (this.scrollbar) { - this.scrollbar.set_window_param(this.rows_to_draw_precise, total_height_in_rows); - this.scrollbar.scroll_to(g_properties.scroll_pos, true); - - const invalidPos = (g_properties.scroll_pos || this.scrollbar.scroll) > this.scrollbar.scrollable_lines; // Prevent scroll crash - g_properties.scroll_pos = invalidPos ? 0 : this.scrollbar.scroll; - - this.is_scrollbar_available = true; - this.on_scrollbar_visibility_change(g_properties.show_scrollbar); - } - } - - /** - * Updates the size of the list when visibility of the scrollbar changes. - * @param {boolean} is_visible The state if the scrollbar is currently visible or not. - * @private - */ - on_scrollbar_visibility_change(is_visible) { - if (this.is_scrollbar_visible !== is_visible) { - this.is_scrollbar_visible = is_visible; - this.update_list_w_size(); - } - } - - /** - * Updates the size and position of the list and also the scrollbar. - * @private - */ - update_list_h_size() { - this.list_y = this.y + g_properties.list_top_pad; - this.list_h = this.h - (g_properties.list_bottom_pad + g_properties.list_top_pad); - - this.rows_to_draw_precise = this.list_h / this.row_h; - - this.initialize_scrollbar(); - this.update_scrollbar(); - this.on_content_to_draw_change(); - } - - /** - * Updates the width of the list and its scrollbar. - * @private - */ - update_list_w_size() { - this.list_w = this.w - g_properties.list_left_pad - g_properties.list_right_pad; - - if (this.is_scrollbar_available) { - if (this.is_scrollbar_visible) { - this.list_w -= this.scrollbar.w + 2; - } - this.scrollbar.set_x(this.w - playlist_geo.scrollbar_w - playlist_geo.scrollbar_right_pad); - } - - // TODO: Mordred - override this elsewhere - this.initialize_scrollbar(); - this.update_scrollbar(); - this.on_content_to_draw_change(); - - this.cnt.update_items_w_size(this.list_w); - } - - /** - * Calculates the shift parameters for scrolling. - * @private - */ - calculate_shift_params() { - this.row_shift = Math.floor(g_properties.scroll_pos); - this.pixel_shift = -Math.round((g_properties.scroll_pos - this.row_shift) * this.row_h); - } -} - - -//////////////////////// -// * ITEM CONTAINER * // -//////////////////////// -/** - * The item container represents a rectangular item with position and size properties, - * and provides methods for drawing, repainting, and tracing. - */ -class ListItem { - /** - * Initializes the size and position of the object. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @class - */ - constructor(x, y, w, h) { - /** - * @private - * @function - */ - this.throttled_repaint = Throttle(() => { - window.RepaintRect(this.x, this.y, this.w, this.h); - }, 1000 / 60); - - this.x = x; - this.y = y; - this.w = w; - this.h = h; - } - - /** - * Nothing to draw here. - * @param {GdiGraphics} gr - * @abstract - */ - draw(gr) { - throw new LogicError('draw not implemented'); - } - - /** - * Invokes a throttled repaint. - */ - repaint() { - this.throttled_repaint(); - } - - /** - * Checks if a given point (x, y) is within the boundaries of an list item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - trace(x, y) { - return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; - } - - /** - * Sets the x-coordinate for the list item. - * @param {number} x The x-coordinate. - */ - set_x(x) { - this.x = x; - } - - /** - * Sets the y-coordinate for the list item. - * @param {number} y The y-coordinate. - */ - set_y(y) { - this.y = y; - } - - /** - * Sets the width for the list item. - * @param {number} w The width. - */ - set_w(w) { - this.w = w; - } -} - - -/////////////////////////// -// * CONTENT CONTAINER * // -/////////////////////////// -/** - * A content container that provides methods for generating and updating item lists, - * as well as calculating the total height of the list in rows. - * @class - */ -class ListContent { - /** - * Generates the item list to draw. - * Called in three cases: - * - 1. Window vertical size changed. - * - 2. Scroll position changed. - * - 3. List cnt changed. - * @param {number} wy The y-coordinate of the list. - * @param {number} wh The height of the list. - * @param {number} row_shift The shift in rows (shift_in_pixels/row_h) in the list. - * @param {number} pixel_shift The shift in pixels (shift_in_pixels - row_shift) in the list. - * @param {number} row_h The row height in the list. - * @returns {Array} - * @abstract - */ - generate_items_to_draw(wy, wh, row_shift, pixel_shift, row_h) { - throw new LogicError('generate_items_to_draw not implemented'); - } - - /** - * Sets a new width for the items. - * @param {number} w The width. - * @abstract - */ - update_items_w_size(w) { - throw new LogicError('update_items_w_size not implemented'); - } - - /** - * Calculates the total height in rows. - * @returns {number} Total cnt height in rows, i.e. total_h/row_h. - * @abstract - */ - calculate_total_h_in_rows() { - throw new LogicError('calculate_total_h_in_rows not implemented'); - } -} - - -/** - * A Basic content container and a subclass of ListContent that represents a list of rows and provides methods - * for generating and updating the items to draw, as well as calculating the total height in rows. - * May contain only Items with height of row_h. - */ -class ListRowContent extends ListContent { - /** - * @extends {ListContent} - * @class - */ - constructor() { - super(); - - /** @type {Array} */ - this.rows = []; - } - - /** - * Generates a list of items to draw based on the given parameters. - * @param {number} wy The starting y-coordinate of the drawing area. - * @param {number} wh The height of the list where the items will be drawn. - * @param {number} row_shift The index of the first row to start drawing from. - * @param {number} pixel_shift The number of pixels to shift the starting position of the items to draw vertically. - * @param {number} row_h The height of each row in pixels. - * @returns {Array} An array called `items_to_draw`. - */ - generate_items_to_draw(wy, wh, row_shift, pixel_shift, row_h) { - if (!this.rows.length) { - return []; - } - - const items_to_draw = []; - let cur_y = wy + pixel_shift; - - for (let i = row_shift; i < this.rows.length; ++i) { - this.rows[i].x = pref.layout === 'default' && (pref.playlistLayout === 'normal' || pref.playlistLayoutNormal && (displayBiography || pref.displayLyrics)) ? ww * 0.5 : 0; - this.rows[i].y = cur_y; - items_to_draw.push(this.rows[i]); - cur_y += row_h; - - if (cur_y >= wy + wh) { - break; - } - } - - return items_to_draw; - } - - /** - * Updates the width of each item in a list of rows. - * @param {number} w The width. - */ - update_items_w_size(w) { - this.rows.forEach(item => { - item.set_w(w); - }); - } - - /** - * Calculates the total number of rows in the list. - * @returns {number} The number of rows in the list. - */ - calculate_total_h_in_rows() { - return this.rows.length; - } -} diff --git a/profile/georgia-reborn/scripts/Playlist/pl-control-scrollbar.js b/profile/georgia-reborn/scripts/Playlist/pl-control-scrollbar.js deleted file mode 100644 index 4d610744..00000000 --- a/profile/georgia-reborn/scripts/Playlist/pl-control-scrollbar.js +++ /dev/null @@ -1,898 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Playlist Scrollbar Control * // -// * Author: TT * // -// * Org. Author: TheQwertiest * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2023-09-25 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -///////////////////////////// -// * PLAYLIST PROPERTIES * // -///////////////////////////// -/** - * Adds additional system playlist panel properties to the SMP properties. - */ -g_properties.add_properties( - { - wheel_scroll_page: ['Panel Playlist - User: Scrollbar.wheel_whole_page', false] - } -); - - -//////////////////////////// -// * PLAYLIST SCROLLBAR * // -//////////////////////////// -/** - * Creates the scrollbar with the ScrollBarPart object and handles scrollbar events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} row_h The height of the row that this scrollbar occupies. - * @param {boolean} fn_redraw Called to redraw the list on the next draw. - * @returns {ScrollBar} An instance of the ScrollBar. - * @class - */ -function ScrollBar(x, y, w, h, row_h, fn_redraw) { - // * CONSTRUCTOR * // - // Public: - this.x = x; - this.y = y; - this.w = w; - this.h = h; - - this.row_h = row_h; - this.rows_drawn = 0; // Visible list size in rows (might be float) - this.row_count = 0; // All rows in associated list - - this.fn_redraw = fn_redraw; // Callback for list redraw - this.draw_timer = false; - this.sb_parts = {}; - - // Buttons - this.btn_h = 0; - - // Thumb - this.thumb_h = 0; - this.thumb_y = 0; // Upper y - - this.in_sbar = false; - - this.b_is_dragging = false; - this.is_scrolled_down = false; - this.is_scrolled_up = true; - this.drag_distance_per_row = 0; // How far should the thumb move, when the list shifts by one row - this.initial_drag_y = 0; // Dragging - - this.scroll = 0; // Lines shifted in list (float) - /** @type {number} */ this.desiredScrollPosition = undefined; - /** @type {number} */ this.lastScrollPosition = undefined; - - this.wheel_scroll_page = g_properties.wheel_scroll_page; - - this.scrollbar_h = 0; // space between sb_parts (arrows) - this.scrollable_lines = 0; // not visible rows (row_count - rows_drawn) - this.scrollbar_travel = 0; // space for thumb to travel (scrollbar_h - thumb_h) - - // private: - const that = this; - - let scrollbar_images = {}; - - let cur_part_key = null; - - // Timers - let throttled_scroll_y = 0; - let timer_shift; - let timer_shift_count; - let timer_stop_y = -1; - /** @type {number} */ - let smoothScrollTimer = null; - - // Helpers - - /** - * Applies an easing effect to a given value. - * @param {number} x The absolute progress of the animation in the bounds of 0 (beginning of the animation) and 1 (end of animation). - * @returns {number} The interpolated value. - */ - const easeOut = (x) => 1 - Math.pow(1 - x, 3); - - /** - * Scrolls to the specified scroll position throttled. - */ - const throttled_scroll_to = Throttle(() => { - this.smooth_scroll_to((throttled_scroll_y - this.btn_h) / this.drag_distance_per_row); - }, 1000 / 60); - - /** - * The alpha timer is used to animate the alpha values for hover effects of the scrollbar parts. - */ - const alpha_timer = new function () { - /** - * Starts the alpha timer. - */ - this.start = function () { - const hoverInStep = 50; - const hoverOutStep = 15; - const downOutStep = 50; - - if (!alpha_timer_internal) { - alpha_timer_internal = setInterval(() => { - for (const part in that.sb_parts) { - const item = that.sb_parts[part]; - switch (item.state) { - case 'normal': - item.hover_alpha = Math.max(0, item.hover_alpha -= hoverOutStep); - item.hot_alpha = Math.max(0, item.hot_alpha -= hoverOutStep); - item.pressed_alpha = part === 'thumb' ? Math.max(0, item.pressed_alpha -= hoverOutStep) : Math.max(0, item.pressed_alpha -= downOutStep); - break; - case 'hover': - item.hover_alpha = Math.min(255, item.hover_alpha += hoverInStep); - item.hot_alpha = Math.max(0, item.hot_alpha -= hoverOutStep); - item.pressed_alpha = Math.max(0, item.pressed_alpha -= downOutStep); - break; - case 'pressed': - item.hover_alpha = 0; - item.hot_alpha = 0; - item.pressed_alpha = 255; - break; - case 'hot': - item.hover_alpha = Math.max(0, item.hover_alpha -= hoverOutStep); - item.hot_alpha = Math.min(255, item.hot_alpha += hoverInStep); - item.pressed_alpha = Math.max(0, item.pressed_alpha -= downOutStep); - break; - } - // console.log(i, item.state, item.hover_alpha , item.pressed_alpha , item.hot_alpha); - // item.repaint(); - } - - that.repaint(); - - const alpha_in_progress = Object.values(that.sb_parts).some((item) => - (item.hover_alpha > 0 && item.hover_alpha < 255) - || (item.pressed_alpha > 0 && item.pressed_alpha < 255) - || (item.hot_alpha > 0 && item.hot_alpha < 255)); - - if (!alpha_in_progress) { - this.stop(); - } - }, 25); - } - }; - - /** - * Stops and clears the alpha timer. - */ - this.stop = () => { - if (alpha_timer_internal) { - clearInterval(alpha_timer_internal); - alpha_timer_internal = null; - } - }; - - let alpha_timer_internal = null; - }(); - - // * METHODS * // - - /** - * Draws the scrollbar. - * @param {GdiGraphics} gr - */ - this.paint = function (gr) { - gr.SetSmoothingMode(SmoothingMode.None); // Disable anti-aliasing, otherwise there will be an ugly 1px outline in style blending - - for (const part in this.sb_parts) { - const item = this.sb_parts[part]; - const { x, y, w, h } = item; - - gr.DrawImage(item.img_normal, x, y, w, h, 0, 0, w, h, 0, 255); - switch (part) { - case 'lineUp': - case 'lineDown': - gr.DrawImage(item.img_hot, x, y, w, h, 0, 0, w, h, 0, item.hot_alpha); - gr.DrawImage(item.img_hover, x, y, w, h, 0, 0, w, h, 0, item.hover_alpha); - gr.DrawImage(item.img_pressed, x, y, w, h, 0, 0, w, h, 0, item.pressed_alpha); - break; - - case 'thumb': - gr.DrawImage(item.img_hover, x, y, w, h, 0, 0, w, h, 0, item.hover_alpha); - gr.DrawImage(item.img_pressed, x, y, w, h, 0, 0, w, h, 0, item.pressed_alpha); - break; - } - } - }; - - /** - * Updates the scrollbar via repaint. - */ - this.repaint = function () { - window.RepaintRect(this.x - (RES_4K ? 13 : 6), this.y, this.w, this.h); - }; - - /** - * Flushes the scrollbar position. - */ - this.flush = () => { - if (this.desiredScrollPosition !== undefined) { - this.scroll_to(this.desiredScrollPosition); - this.desiredScrollPosition = undefined; - } - }; - - /** - * Resets the current scroll of scrollbar. - */ - this.reset = () => { - this.flush(); // throttled_scroll_to.flush(); - alpha_timer.stop(); - this.stop_shift_timer(); - - this.scroll = 0; - this.calc_params(); - }; - - /** - * Checks if the mouse is within the boundaries of the scrollbar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @return {boolean} True or false. - */ - this.trace = function (x, y) { - return x + SCALE(10) > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; - }; - - /** - * Sets the window parameters for the scrollbar. - * @param {number} rows_drawn The number of rows drawn. - * @param {number} row_count The total number of rows. - */ - this.set_window_param = (rows_drawn, row_count) => { - this.rows_drawn = rows_drawn; - this.row_count = row_count; - this.calc_params(); - this.create_parts(); - }; - - /** - * Calculates the scrollbar parameters. - */ - this.calc_params = () => { - this.btn_h = this.w; - // * Draw info - this.scrollbar_h = this.h - this.btn_h * 2; - this.thumb_h = Math.max(Math.round(this.scrollbar_h * this.rows_drawn / this.row_count), RES_4K ? 45 : 30); - this.scrollbar_travel = this.scrollbar_h - this.thumb_h; - // * Scrolling info - this.scrollable_lines = this.row_count - this.rows_drawn; - this.thumb_y = this.btn_h + this.scroll * this.scrollbar_travel / this.scrollable_lines; - this.drag_distance_per_row = this.scrollbar_travel / this.scrollable_lines; - }; - - /** - * Creates the button and thumb scrollbar parts. - */ - this.create_parts = () => { - create_dynamic_scrollbar_images(this.w, this.thumb_h); - - const { x, y, w, h } = this; - - this.sb_parts = { - lineUp: new ScrollBarPart(x - (RES_4K ? 13 : 6), y, w, this.btn_h, scrollbar_images.lineUp), - thumb: new ScrollBarPart(x, y + this.thumb_y, w - SCALE(14), this.thumb_h, scrollbar_images.thumb), - lineDown: new ScrollBarPart(x - (RES_4K ? 13 : 6), y + h - this.btn_h, w, this.btn_h, scrollbar_images.lineDown) - }; - }; - - /** - * Handles mouse wheel scrolling events. - * @param {number} wheel_direction The up or down wheel direction. - */ - this.wheel = (wheel_direction) => { - const direction = -wheel_direction; - - if (this.wheel_scroll_page) { - this.shift_page(direction); - } else { - const newScroll = this.nearestScroll(direction); - if (!pref.playlistSmoothScrolling) { - this.scroll_to(newScroll + direction * pref.playlistWheelScrollSteps); - } else { - if (this.desiredScrollPosition === undefined) { - this.desiredScrollPosition = newScroll + direction * pref.playlistWheelScrollSteps; - } else { - this.desiredScrollPosition += (direction * pref.playlistWheelScrollSteps); - } - if (direction === -1 && this.desiredScrollPosition < 0) { - this.desiredScrollPosition = 0; - } else if (direction === 1 && this.desiredScrollPosition > this.scrollable_lines) { - this.desiredScrollPosition = this.scrollable_lines; - } - this.smooth_scroll_to(this.desiredScrollPosition); - } - } - }; - - /** - * Handles mouse leaving events over each scrollbar part. - */ - this.parts_leave = () => { - this.in_sbar = false; - cur_part_key = null; - - for (const part in this.sb_parts) { - this.sb_parts[part].cs('normal'); - } - alpha_timer.start(); - }; - - /** - * Handles mouse leaving events of the scrollbar. - */ - this.leave = function () { - if (this.b_is_dragging) { - return; - } - - this.parts_leave(); - }; - - /** - * Handles mouse movement of the scrollbar parts. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @return {string} - */ - this.parts_move = (x, y) => { - const hover_part_key = FindKey(this.sb_parts, (item) => item.trace(x, y)); - - const changeHotStatus = this.trace(x, y) !== this.in_sbar; - if (changeHotStatus) { - this.in_sbar = !this.in_sbar; - if (this.in_sbar) { - if (hover_part_key !== 'lineUp' && cur_part_key !== 'lineUp') { - this.sb_parts.lineUp.cs('hot'); - } - if (hover_part_key !== 'lineDown' && cur_part_key !== 'lineDown') { - this.sb_parts.lineDown.cs('hot'); - } - } - else { - if (cur_part_key !== 'lineUp') { - this.sb_parts.lineUp.cs('normal'); - } - if (cur_part_key !== 'lineDown') { - this.sb_parts.lineDown.cs('normal'); - } - } - alpha_timer.start(); - } - - if (cur_part_key === hover_part_key) { // Nothing to do: same button - return cur_part_key; - } - - if (cur_part_key) { - if (cur_part_key === 'thumb') { - this.sb_parts[cur_part_key].cs('normal'); - } - else { - if (this.sb_parts[cur_part_key].state === 'pressed') { - // Stop btn fast scroll - this.stop_shift_timer(); - } - - // Return prev button to normal or hot state - this.sb_parts[cur_part_key].cs(this.in_sbar ? 'hot' : 'normal'); - } - alpha_timer.start(); - } - - if (hover_part_key) { // Select current button - this.sb_parts[hover_part_key].cs('hover'); - alpha_timer.start(); - } - - cur_part_key = hover_part_key; - return cur_part_key; - }; - - /** - * Handles mouse moving events over the scrollbar. - * @param {number} p_x The x-coordinate. - * @param {number} p_y The y-coordinate. - * @return {string} - */ - this.move = function (p_x, p_y) { - if (this.b_is_dragging) { - throttled_scroll_y = p_y - this.y - this.initial_drag_y; - throttled_scroll_to(); - // this.scroll_to( (p_y - this.y - this.initial_drag_y - this.btn_h) / this.drag_distance_per_row); - return; - } - - this.parts_move(p_x, p_y); - }; - - /** - * Handles left mouse button down events on the scrollbar. - */ - this.parts_lbtn_down = function () { - if (cur_part_key) { - this.sb_parts[cur_part_key].cs('pressed'); - alpha_timer.start(); - } - }; - - /** - * Handles left mouse button up events on the scrollbar. - * @param {number} p_x The x-coordinate. - * @param {number} p_y The y-coordinate. - */ - this.lbtn_dn = (p_x, p_y) => { - if (!this.trace(p_x, p_y) || this.row_count <= this.rows_drawn) { - return; - } - - this.parts_lbtn_down(); - - const y = p_y - this.y; - - if (y < this.btn_h) { - this.shift_line(-1); - this.start_shift_timer(-1); - } - else if (y > this.h - this.btn_h) { - this.shift_line(1); - this.start_shift_timer(1); - } - else if (y < this.thumb_y) { - this.shift_page(-1); - timer_stop_y = y; - this.start_shift_timer(-this.rows_drawn); - } - else if (y > this.thumb_y + this.thumb_h) { - this.shift_page(1); - timer_stop_y = y; - this.start_shift_timer(this.rows_drawn); - } - else { // On bar - this.b_is_dragging = true; - this.initial_drag_y = y - this.thumb_y; - } - }; - - /** - * Handles left mouse button down events on the scrollbar parts. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - this.parts_lbtn_up = function (x, y) { - if (!cur_part_key || this.sb_parts[cur_part_key].state !== 'pressed') { - return false; - } - - const new_state = this.sb_parts[cur_part_key].trace(x, y) ? 'hover' : 'normal'; - - this.sb_parts[cur_part_key].cs(new_state); - alpha_timer.start(); - - return true; - }; - - /** - * Handles left mouse button up events on the scrollbar. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - this.lbtn_up = (x, y) => { - this.parts_lbtn_up(x, y); - if (this.b_is_dragging) { - this.b_is_dragging = false; - this.desiredScrollPosition = undefined; - } - this.initial_drag_y = 0; - - this.stop_shift_timer(); - }; - - /** - * Scrolls to the start of the list. - */ - this.scroll_to_start = function () { - this.smooth_scroll_to(0); - }; - - /** - * Scrolls one line up or down. - * @param {number} direction The up or down scroll direction. - */ - this.shift_line = function (direction) { - const newScroll = this.nearestScroll(direction); - this.smooth_scroll_to(newScroll); - }; - - /** - * Scrolls one page up or down. - * @param {number} direction The up or down scroll direction. - */ - this.shift_page = function (direction) { - const newScroll = this.nearestScroll(direction); - this.smooth_scroll_to(newScroll + direction * Math.floor(Math.max(this.rows_drawn - 1, 1))); - }; - - /** - * Scrolls to the end of the list. - */ - this.scroll_to_end = function () { - this.smooth_scroll_to(this.scrollable_lines); - }; - - /** - * Starts a timer to shift the scrollbar. This method inserts a delay (8x45ms) when holding - * the mouse btn down before scrolling starts, after the first scroll event happens. - * @param {number} shift_amount The number of rows to shift. - */ - this.start_shift_timer = (shift_amount) => { - if (timer_shift == null) { - timer_shift_count = 0; - timer_shift = setInterval(() => { - if (this.thumb_y <= this.btn_h || this.thumb_y + this.thumb_h >= this.h - this.btn_h) { - this.stop_shift_timer(); - return; - } - if (timer_stop_y !== -1) { - const new_thumb_y = this.btn_h + (this.scroll + shift_amount) * this.scrollbar_travel / this.scrollable_lines; - - if ((shift_amount > 0 && new_thumb_y >= timer_stop_y) - || (shift_amount < 0 && new_thumb_y + this.thumb_h <= timer_stop_y)) { - this.stop_shift_timer(); - return; - } - } - - if (timer_shift_count > 8) { - if (this.desiredScrollPosition === undefined) { - this.desiredScrollPosition = this.scroll + shift_amount; - } else { - this.desiredScrollPosition += shift_amount; - } - this.smooth_scroll_to(this.desiredScrollPosition); - } else { - timer_shift_count++; - } - }, 45); - } - }; - - /** - * Stops the timer that is shifting the scrollbar. - */ - this.stop_shift_timer = () => { - if (timer_shift != null) { - clearInterval(timer_shift); - timer_shift = undefined; - } - timer_stop_y = -1; - }; - - /** - * Calculates the nearest scroll position to the current position. - * @param {number} direction The direction of the scroll. - * @returns {number} The nearest scroll position. - */ - this.nearestScroll = function (direction) { - const scrollShift = this.scroll - Math.floor(this.scroll); - const drawnShift = 1 - (this.rows_drawn - Math.floor(this.rows_drawn)); - let newScroll = 0; - - if (direction < 0 && scrollShift !== 0) { - newScroll = Math.floor(this.scroll); - } else if (direction > 0 && Math.abs(drawnShift - scrollShift) > 0.0001) { - newScroll = drawnShift > scrollShift ? Math.floor(this.scroll) + drawnShift : Math.ceil(this.scroll) + drawnShift; - } else { - newScroll = this.scroll + direction; - } - - // console.log('current:', this.scroll, 'new:', newScroll, 'dir:', direction, Math.round(this.desiredScrollPosition)); - return newScroll; - }; - - /** - * Stops the scrollbar scroll and clears the timer. - */ - this.stopScrolling = () => { - clearInterval(smoothScrollTimer); - smoothScrollTimer = null; - }; - - /** - * Scrolls to desired row over 400ms. Can be called repeatedly (during wheel or holding down arrows) to update the desired position. - * @param {number} newPosition The new row position to scroll to. - * @returns {number} The new scroll position. - */ - this.smooth_scroll_to = (newPosition) => { - if (!pref.playlistSmoothScrolling) { - this.scroll_to(newPosition, false); - } - const end = Math.max(0, Math.min(newPosition, this.scrollable_lines)); - if (end === this.scroll) { - return; - } - clearInterval(smoothScrollTimer); - const start = this.scroll; - const direction = start - end > 0 ? -1 : 1; - let animationProgress = 0; // Percent of animation completion: 0 (start) - 100 (end). Use 100 scale to avoid .009999 issues - const scrollFunc = () => { - animationProgress += 8; // Slow things down slightly from 10 - let newVal = start + easeOut(animationProgress / 100) * (end - start); - if ((Math.abs(newPosition - newVal) < 0.1) || - (direction === 1 && newVal > newPosition) || - (direction === -1 && newVal < newPosition)) { - newVal = newPosition; - animationProgress = 100; // Clear interval - } else if (newPosition <= 0) { // Fix crash for auto-hide scrollbar when removing almost everything in playlist and some tracks in top remain - animationProgress = 100; - } - newVal = Math.round(newVal * 100) / 100; - // console.log(`${start} + easeOut(${animationProgress}/100) * (${end} - ${start}) = `, newVal) - this.scroll_to(newVal, false); - if (animationProgress >= 100 && newPosition > 0) { - this.desiredScrollPosition = undefined; - this.stopScrolling(); - } - }; - smoothScrollTimer = setInterval(() => { - scrollFunc(); - }, pref.playlistWheelScrollDuration / 10); - scrollFunc(); // Want to immediately start scroll - }; - - /** - * Scrolls to the specified scroll position. - * @param {number} new_position The new row position to scroll to. - * @param {boolean} scroll_wo_redraw Calls a redraw to update the scrollbar. - */ - this.scroll_to = (new_position, scroll_wo_redraw = false) => { - const s = Math.max(0, Math.min(new_position, this.scrollable_lines)); - const invalidPos = (g_properties.scroll_pos || s) > this.scrollable_lines; // Prevent scroll crash - if (s === this.scroll) return; - this.scroll = invalidPos ? 0 : s; - this.thumb_y = this.btn_h + this.scroll * this.scrollbar_travel / this.scrollable_lines; - this.sb_parts.thumb.y = this.y + this.thumb_y; - - this.is_scrolled_up = (this.scroll === 0); - this.is_scrolled_down = Math.abs(this.scroll - this.scrollable_lines) < 0.0001; - - if (!scroll_wo_redraw) { - this.fn_redraw(); - } - }; - - /** - * Sets the x-coordinate of the scrollbar. - * @param {number} x The x-coordinate. - */ - this.set_x = (x) => { - this.x = x; - for (const part in this.sb_parts) { - this.sb_parts[part].x = x; - } - }; - - // private: - - /** - * Creates images for the scrollbar up and down buttons. - */ - function create_scrollbar_images() { - if (scrollbar_images.length > 0) { - return; - } - - const fontSegoeUi = g_pl_fonts.scrollbar; - - const ico_back_colors = - [ - g_pl_colors.bg, - g_pl_colors.bg, - g_pl_colors.bg, - g_pl_colors.bg - ]; - - const ico_fore_colors = - [ - g_pl_colors.sbar_btn_normal, - g_pl_colors.sbar_btn_hovered, - g_pl_colors.sbar_btn_hovered, - g_pl_colors.sbar_btn_normal - ]; - - const btn = - { - lineUp: { - ico: '\uE010', - font: fontSegoeUi, - w: that.w, - h: that.w - }, - lineDown: { - ico: '\uE011', - font: fontSegoeUi, - w: that.w, - h: that.w - } - }; - - scrollbar_images = []; - - for (const i in btn) { - const item = btn[i]; - const { w, h } = item; - const m = 2; - const stateImages = []; // 0=normal, 1=hover, 2=down, 3=hot; - - for (let s = 0; s < 4; s++) { - const img = gdi.CreateImage(w, h); - const grClip = img.GetGraphics(); - - const icoColor = ico_fore_colors[s]; - const backColor = ico_back_colors[s]; - - // Don't really need this button backgrounds - // if (i === 'lineUp') { - // grClip.FillSolidRect(m, 0, w - m * 2, h - 1, backColor); - // } - // else if (i === 'lineDown') { - // grClip.FillSolidRect(m, 1, w - m * 2, h - 1, backColor); - // } - - grClip.SetSmoothingMode(SmoothingMode.HighQuality); - grClip.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); - - const btn_format = g_string_format.h_align_center | g_string_format.v_align_far; - if (i === 'lineDown') { - grClip.DrawString(item.ico, item.font, icoColor, 0, RES_4K ? -25 : -12, w, h, btn_format); - } - else if (i === 'lineUp') { - grClip.DrawString(item.ico, item.font, icoColor, 0, 0, w, h, btn_format); - } - - img.ReleaseGraphics(grClip); - stateImages[s] = img; - } - - scrollbar_images[i] = - { - normal: stateImages[0], - hover: stateImages[1], - pressed: stateImages[2], - hot: stateImages[3] - }; - } - } - - /** - * Creates images for the scrollbar thumb. - * @param {number} thumb_w The width of the scrollbar thumb. - * @param {number} thumb_h The height of the scrollbar thumb. - */ - function create_dynamic_scrollbar_images(thumb_w, thumb_h) { - const thumb_colors = - [ - g_pl_colors.sbar_thumb_normal, - g_pl_colors.sbar_thumb_hovered, - g_pl_colors.sbar_thumb_drag - ]; - - const w = thumb_w; - const h = thumb_h; - const m = 2; - const stateImages = []; // 0=normal, 1=hover, 2=down; - - for (let s = 0; s <= 2; s++) { - const img = gdi.CreateImage(w, h); - const grClip = img.GetGraphics(); - - const color = thumb_colors[s]; - grClip.FillSolidRect(m, 0, w - m * 2, h, color); - - img.ReleaseGraphics(grClip); - stateImages[s] = img; - } - - scrollbar_images.thumb = - { - normal: stateImages[0], - hover: stateImages[1], - pressed: stateImages[2] - }; - } - - create_scrollbar_images(); -} - - -///////////////////////////////// -// * PLAYLIST SCROLLBAR PART * // -///////////////////////////////// -/** - * Creates scrollbar parts with specified dimensions and images. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {GdiBitmap} img_src The image sources for different states of the scrollbar part. - * @returns {ScrollBarPart} The part of the scrollbar that is about to be drawn. - * @class - */ -function ScrollBarPart(x, y, w, h, img_src) { - // * CONSTRUCTOR * // - this.x = x; - this.y = y; - this.w = w; - this.h = h; - this.img_normal = undefined; - this.img_hover = undefined; - this.img_pressed = undefined; - this.img_hot = undefined; - this.hover_alpha = 0; - this.hot_alpha = 0; - this.pressed_alpha = 0; - this.state = 'normal'; - - // * METHODS * // - - /** - * Updates the scrollbar part via repaint. - */ - this.repaint = function () { - window.RepaintRect(this.x, this.y, this.w, this.h); - }; - - /** - * Checks if the mouse is within the boundaries of the scrollbar part. - * @param {number} x The x coordinate. - * @param {number} y The y coordinate. - * @returns {boolean} True if the coordinates are inside the scrollbar part. - */ - this.trace = function (x, y) { - return x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; - }; - - /** - * Sets the state of the scrollbar part. - * @param {string} s The state. - */ - this.cs = (s) => { - this.state = s; - this.repaint(); - }; - - /** - * Assigns the images to the scrollbar part. - * @param {Object} imgs The images. - */ - this.assign_imgs = function (imgs) { - this.img_normal = this.img_hover = this.img_hover = this.img_hover = null; - - if (imgs === undefined) { - return; - } - - this.img_normal = imgs.normal; - this.img_hover = imgs.hover ? imgs.hover : this.img_normal; - this.img_pressed = imgs.pressed ? imgs.pressed : this.img_normal; - this.img_hot = imgs.hot ? imgs.hot : this.img_normal; - }; - - this.assign_imgs(img_src); -} diff --git a/profile/georgia-reborn/scripts/Playlist/pl-linked-list.js b/profile/georgia-reborn/scripts/Playlist/pl-linked-list.js deleted file mode 100644 index a5203e17..00000000 --- a/profile/georgia-reborn/scripts/Playlist/pl-linked-list.js +++ /dev/null @@ -1,272 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Utility Linked List * // -// * Author: TT * // -// * Org. Author: TheQwertiest * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2023-09-25 * // -///////////////////////////////////////////////////////////////////////////// - - -'use strict'; - - -///////////////////////////// -// * UTILITY LINKED LIST * // -///////////////////////////// -/** - * Constructs a LinkedList instance. - * @template T - * @class - * @returns {LinkedList} A LinkedList instance to manage the list of elements. - */ -function LinkedList() { - // * CONSTRUCTOR * // - /** @type {?Node} */ - let back = null; - /** @type {?Node} */ - let front = null; - /** @type {number} */ - let size = 0; - - /** - * @const {Node} - */ - this.end_node = new Node(null, null, null); - - /** - * Defines a Node object with properties for value, previous node, and next node. - * @param {T} value The value of the node. It can be any data type, such as a number, string, object, or even another node. - * @param {?Node} prev The previous node in a linked list. - * @param {?Node} next The next node in a linked list. - * @template T - * @struct - * @class - */ - function Node(value, prev, next) { - this.value = value; - this.prev = prev; - this.next = next; - } - - /** - * Adds a node to a linked list and updates the previous and next pointers accordingly. - * @param {Node} node A node in a linked list. - */ - function add_node(node) { - if (node.prev) { - node.prev.next = node; - } - else { - front = node; - } - - if (node.next) { - node.next.prev = node; - } - else { - back = node; - } - - ++size; - } - - /** - * Removes a node from a linked list and updates the previous and next pointers accordingly. - * @param {?Node} node A node in a linked list. - */ - function remove_node(node) { - if (!node) { - return; - } - - if (node.prev) { - node.prev.next = node.next; - } - else { - front = node.next; - } - - if (node.next) { - node.next.prev = node.prev; - } - else { - back = node.prev; - } - - --size; - } - - // * METHODS * // - - /** - * Clears all elements from the linked list. - * @returns {void} - */ - this.clear = () => { - back = null; - front = null; - size = 0; - }; - - /** - * Pushes a new value to the back of the queue. - * @param {T} value The value to push. - */ - this.push_back = (value) => { - add_node(new Node(value, back, null)); - }; - - /** - * Pushes a new value to the front of the queue. - * @param {T} value The value to push. - */ - this.push_front = (value) => { - add_node(new Node(value, null, front)); - }; - - /** - * Removes the value at the front of the queue. - * @returns {T} The value that was removed. - */ - this.pop_front = () => { - remove_node(front); - }; - - /** - * Removes the value at the back of the queue. - * @returns {T} The value that was removed. - */ - this.pop_back = () => { - remove_node(back); - }; - - /** - * Removes the value at the given iterator. - * @param {LinkedList.Iterator} iterator The iterator to remove. - * @returns {T} The value that was removed. - */ - this.remove = function (iterator) { - if (!(iterator instanceof LinkedList.Iterator)) { - throw new InvalidTypeError(iterator, typeof iterator, 'Iterator'); - } - - if (iterator.parent !== this) { - throw new LogicError('Using iterator from a different list'); - } - - if (iterator.compare(this.end())) { - throw new LogicError('Removing invalid iterator'); - } - - remove_node(iterator.cur_node); - - iterator.cur_node = this.end_node; - }; - - /** - * Gets the value at the front of the queue. - * @returns {T} The value at the front of the queue. - */ - this.front = () => front.value; - - /** - * Gets the value at the back of the queue. - * @returns {T} The value at the back of the queue. - */ - this.back = () => back.value; - - /** - * Gets the number of elements in the queue. - * @returns {number} The number of elements in the queue. - */ - this.length = () => size; - - /** - * Gets an iterator for the beginning of the queue. - * @returns {LinkedList.Iterator} An iterator for the beginning of the queue. - */ - this.begin = function () { - return new LinkedList.Iterator(this, front || this.end_node); - }; - - /** - * Gets an iterator for the end of the queue. - * @returns {LinkedList.Iterator} An iterator for the end of the queue. - */ - this.end = function () { - return new LinkedList.Iterator(this, this.end_node); - }; -} - - -/** - * Encapsulates a linked list iterator and is used to iterate through the list. - * @param {LinkedList} parent The parent of the node. - * @param {Node} node The node that is pointed to by the iterator. - * @template T - * @class - */ -LinkedList.Iterator = function (parent, node) { - // * CONSTRUCTOR * // - /** @const {LinkedList} */ - this.parent = parent; - - /** @type {Node} */ - this.cur_node = node; - - // * METHODS * // - - /** - * Increments the iterator to the next element. - * @returns {void} - */ - this.increment = function () { - if (this.cur_node === parent.end_node) { - throw new LogicError('Iterator is out of bounds'); - } - - this.cur_node = this.cur_node.next; - if (!this.cur_node) { - this.cur_node = parent.end_node; - } - }; - - /** - * Decrements the iterator to the previous element. - * @returns {void} - */ - this.decrement = function () { - if (this.cur_node === front) { - throw new LogicError('Iterator is out of bounds'); - } - - this.cur_node = this.cur_node === parent.end_node ? back : this.cur_node.prev; - }; - - /** - * Gets the value of the current element. - * @returns {T} The value of the current element. - */ - this.value = function () { - if (this.cur_node === parent.end_node) { - throw new LogicError('Accessing end node'); - } - - return this.cur_node.value; - }; - - /** - * Compares this iterator to another iterator. - * @param {LinkedList.Iterator} iterator The other iterator to compare to. - * @returns {boolean} True or false. - */ - this.compare = function (iterator) { - if (iterator.parent !== this.parent) { - throw new LogicError('Comparing iterators from different lists'); - } - return iterator.cur_node === this.cur_node; - }; -}; diff --git a/profile/georgia-reborn/scripts/Playlist/pl-main.js b/profile/georgia-reborn/scripts/Playlist/pl-main.js index f63d4afe..b5738596 100644 --- a/profile/georgia-reborn/scripts/Playlist/pl-main.js +++ b/profile/georgia-reborn/scripts/Playlist/pl-main.js @@ -1,9462 +1,58 @@ -///////////////////////////////////////////////////////////////////////////// -// * Georgia-ReBORN: A Clean, Full Dynamic Color Reborn foobar2000 Theme * // -// * Description: Georgia-ReBORN Playlist * // -// * Author: TT * // -// * Org. Author: extremeHunter, TheQwertiest * // -// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // -// * Version: 3.0-DEV * // -// * Dev. started: 2017-12-22 * // -// * Last change: 2024-01-13 * // -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Main * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// 'use strict'; -/////////////////// -// * VARIABLES * // -/////////////////// -/** @type {PlaylistPanel} The playlist object. */ -let playlist; -/** @type {number} The playlist drop index used for drag and drop in the split layout. */ -let playlistDropIndex; -/** @type {boolean} The playlist fonts creation state. */ -let playlistFontsCreated = false; -/** @type {number} The playlist thumbnail size defaults are 64 pixels in HD 128 in 4K. */ -let playlistThumbSize = SCALE(64); -/** @type {FbMetadbHandle[]} The list of handles that we are loading artwork for. */ -const playlistThumbList = new Set(); -/** @type {Map} The playlist track ratings cached. */ -const playlistTrackRatings = new Map(); -/** @type {Map} The playlist album ratings cached. */ -let playlistAlbumRatings = new Map(); - -/** @type {Object} The playlist geometry object. */ -const playlist_geo = {}; -/** @type {Object} The playlist colors object. */ -const g_pl_colors = {}; -/** @type {Object} The playlist fonts object. */ -let g_pl_fonts = {}; - -/** - * A set of drag and drop action settings. - * @enum {number} - */ -const g_drop_effect = { - none: 0, - copy: 1, - move: 2, - link: 4, - scroll: 0x80000000 -}; - -/** - * A set of playlist history state settings. - * @enum {string} - */ -const PlaylistMutation = { - Added: 'Playlist added', - Init: 'Playlist initializing history', - Removed: 'Playlist removed', - Reordered: 'Playlist reordered', - Switch: 'Playlist switch' -} - -/** - * A set of playlist row state settings. - * @enum {number} - */ -const rowState = { - normal: 0, - hovered: 1, - pressed: 2 -}; - -/** - * A set of playlist item visibility state settings. - * @enum {number} - */ -const visibility_state = { - none: 0, - partial_top: 1, - partial_bottom: 2, - full: 3 -}; - - -//////////////////// -// * PROPERTIES * // -//////////////////// -/** - * Adds main playlist panel properties to the SMP properties. - * Default values for grouping data are set in it's class constructor. - */ -g_properties.add_properties( - { - show_playlist_info: ['Panel Playlist - User: Playlist_info.show', true], - - rows_in_header: ['Panel Playlist - User: Header.normal.row_count', 4], - rows_in_compact_header: ['Panel Playlist - User: Header.compact.row_count', 3], - show_header: ['Panel Playlist - User: Header.show', true], - use_compact_header: ['Panel Playlist - User: Header.use_compact', false], - show_album_art: ['Panel Playlist - User: Header.this.art.show', true], - auto_album_art: ['Panel Playlist - User: Header.this.art.auto', false], - show_group_info: ['Panel Playlist - User: Header.info.show', true], - show_disc_header: ['Panel Playlist - User: Header.disc_header.show', true], - show_rating_header: ['Panel Playlist - User: Header.rating.show', true], - show_PLR_header: ['Panel Playlist - User: Header.peak_loudness_ratio.show', false], - auto_collapse: ['Panel Playlist - User: Header.collapse.auto', false], - collapse_on_playlist_switch: ['Panel Playlist - User: Header.collapse.on_playlist_switch', false], - collapse_on_start: ['Panel Playlist - User: Header.collapse.on_start', false], - - show_row_stripes: ['Panel Playlist - User: Row.stripes.show', false], - show_playcount: ['Panel Playlist - User: Row.play_count.show', true], - show_PLR: ['Panel Playlist - User: Row.peak_loudness_ratio.show', false], - show_rating: ['Panel Playlist - User: Row.rating.show', true], - use_rating_from_tags: ['Panel Playlist - User: Row.rating.from_tags', false], - show_queue_position: ['Panel Playlist - User: Row.queue_position.show', true], - - playlist_stats_include_artist: ['Panel Playlist - User: Misc.playlist_stats_include_artist', true], - playlist_stats_include_album: ['Panel Playlist - User: Misc.playlist_stats_include_album', true], - playlist_stats_include_track: ['Panel Playlist - User: Misc.playlist_stats_include_track', true], - playlist_stats_include_year: ['Panel Playlist - User: Misc.playlist_stats_include_year', false], - playlist_stats_include_genre: ['Panel Playlist - User: Misc.playlist_stats_include_genre', false], - playlist_stats_include_label: ['Panel Playlist - User: Misc.playlist_stats_include_label', false], - playlist_stats_include_country: ['Panel Playlist - User: Misc.playlist_stats_include_country', false], - playlist_stats_include_stats: ['Panel Playlist - User: Misc.playlist_stats_include_stats', true], - playlist_stats_sort_by: ['Panel Playlist - User: Misc.playlist_stats_sort_by', ''], - playlist_stats_sort_direction: ['Panel Playlist - User: Misc.playlist_stats_sort_direction', '_dsc'], - - playlist_group_data: ['Panel Playlist - System: Playlist.grouping.data_list', ''], - playlist_custom_group_data: ['Panel Playlist - System: Playlist.grouping.custom_data_list', ''], - default_group_name: ['Panel Playlist - System: Playlist.grouping.default_preset_name', ''], - group_presets: ['Panel Playlist - System: Playlist.grouping.presets', ''] - } -); - -// * Fixup properties -g_properties.rows_in_header = Math.max(0, g_properties.rows_in_header); -g_properties.rows_in_compact_header = Math.max(0, g_properties.rows_in_compact_header); -if (!pref.playlistAutoHideScrollbar && !g_properties.show_scrollbar) { - g_properties.show_scrollbar = true; -} - -// * Fix playlist panel state at startup -if (pref.libraryLayoutSplitPreset || pref.libraryLayoutSplitPreset3 || pref.libraryLayoutSplitPreset4) { - g_properties.auto_collapse = pref.showPanelOnStartup === 'library'; -} else if (pref.libraryLayoutSplitPreset2) { - g_properties.show_header = pref.showPanelOnStartup === 'playlist'; -} - - -/////////////// -// * FONTS * // -/////////////// -const headerFontSize = pref[`playlistHeaderFontSize_${pref.layout}`]; -const rowFontSize = pref[`playlistFontSize_${pref.layout}`]; - -const titleNormalFont = pref.customThemeFonts ? customFont.playlistTitleNormal : 'Segoe UI'; -const titleSelectedFont = pref.customThemeFonts ? customFont.playlistTitleSelected : 'Segoe UI'; -const titlePlayingFont = pref.customThemeFonts ? customFont.playlistTitlePlaying : 'Segoe UI'; - -const artistNormalFont = pref.customThemeFonts ? customFont.playlistArtistNormal : 'Segoe UI Semibold'; -const artistPlayingFont = pref.customThemeFonts ? customFont.playlistArtistPlaying : 'Segoe UI Semibold'; -const artistNormalCompactFont = pref.customThemeFonts ? customFont.playlistArtistNormalCompact : 'Segoe UI Semibold'; -const artistPlayingCompactFont = pref.customThemeFonts ? customFont.playlistArtistPlayingCompact : 'Segoe UI Semibold'; - -const albumFont = pref.customThemeFonts ? customFont.playlistAlbum : 'Segoe UI Semibold'; -const dateFont = pref.customThemeFonts ? customFont.playlistDate : 'Segoe UI Semibold'; -const dateCompactFont = pref.customThemeFonts ? customFont.playlistDateCompact : 'Segoe UI Semibold'; -const infoFont = pref.customThemeFonts ? customFont.playlistInfo : 'Segoe UI'; -const coverFont = pref.customThemeFonts ? customFont.playlistCover : 'Segoe UI Semibold'; - -const playcountFont = pref.customThemeFonts ? customFont.playlistPlaycount : 'Segoe UI'; - -/** - * Creates and assigns playlist fonts. - */ -function createPlaylistFonts() { - const headerFontSize = pref[`playlistHeaderFontSize_${pref.layout}`]; - const rowFontSize = pref[`playlistFontSize_${pref.layout}`]; - - g_pl_fonts = { - title_normal: Font(titleNormalFont, rowFontSize), - title_selected: Font(titleSelectedFont, rowFontSize), - title_playing: Font(titlePlayingFont, rowFontSize), - - artist_normal: Font(artistNormalFont, headerFontSize + 3, pref.customThemeFonts ? g_font_style.bold : 0), - artist_playing: Font(artistPlayingFont, headerFontSize + 3, pref.customThemeFonts ? g_font_style.bold : 0), - artist_normal_compact: Font(artistNormalCompactFont, headerFontSize), - artist_playing_compact: Font(artistPlayingCompactFont, headerFontSize, g_font_style.underline), - - album: Font(albumFont, headerFontSize), - date: Font(dateFont, headerFontSize + 3), - date_compact: Font(dateCompactFont, headerFontSize), - info: Font(infoFont, rowFontSize - 1), - cover: Font(coverFont, rowFontSize - 1), - - playcount: Font(playcountFont, rowFontSize - 3), - plr_track: Font(playcountFont, rowFontSize - 3), - rating_not_set: Font('Segoe UI Symbol', rowFontSize + 2), - rating_set: Font('Segoe UI Symbol', rowFontSize + 4), - scrollbar: Font('Segoe UI Symbol', headerFontSize), - - font_awesome: Font('FontAwesome', rowFontSize + 2), - dummy_text: Font(fontDefault, rowFontSize + 1) - }; - playlistFontsCreated = true; -} - - -////////////////// -// * GEOMETRY * // -////////////////// -/** - * Rescales the playlist based on the font size settings and creates playlist fonts if they haven't been created already. - * @param {boolean=} forceRescale Whether the playlist should be rescaled even if the playlist fonts have already been created. - * @returns - */ -function rescalePlaylist(forceRescale) { - if (playlistFontsCreated && !forceRescale) { - return; // Don't redo fonts - } - createPlaylistFonts(); - g_properties.row_h = Math.round(pref[`playlistFontSize_${pref.layout}`] * 1.667); - playlist_geo.row_h = SCALE(g_properties.row_h); - playlist_geo.scrollbar_w = g_properties.scrollbar_w; // Don't use SCALE() - playlist_geo.scrollbar_right_pad = SCALE(g_properties.scrollbar_right_pad); - playlist_geo.scrollbar_top_pad = SCALE(g_properties.scrollbar_top_pad); - playlist_geo.scrollbar_bottom_pad = SCALE(g_properties.scrollbar_bottom_pad); - playlist_geo.list_bottom_pad = SCALE(g_properties.list_bottom_pad); -} - - -////////////////// -// * POSITION * // -////////////////// -/** - * Sets and updates the playlist x-coordinate when resizing or changing the playlist layout. - * @returns {number} The playlist x-coordinate. - */ -function setPlaylistX() { - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; - - if (pref.panelWidthAuto && noAlbumArtStub) initNoAlbumArtSize(); - - return pref.layout === 'default' && (pref.playlistLayout === 'normal' || - pref.playlistLayoutNormal && (displayBiography || pref.displayLyrics)) ? - pref.panelWidthAuto ? displayLibrarySplit() ? noAlbumArtSize : !fb.IsPlaying ? 0 : albumArtSize.x + albumArtSize.w : - ww * 0.5 : - 0; -} - - -/////////////////////////////// -// * DRAG & DROP CALLBACKS * // -/////////////////////////////// -/** - * Called when mouse with content enters another window and determines if that window is a valid drop target. - * 1. Called first. - */ -function on_drag_enter(action, x, y, mask) { - trace_call && console.log('Playlist => on_drag_enter'); - playlist && playlist.on_drag_enter(action, x, y, mask); -} - - -/** - * Called when content with mouse moves but stays within the same window. - * 2. called after on_drag_enter. - */ -function on_drag_over(action, x, y, mask) { - trace_call && console.log('Playlist => on_drag_over'); - playlist && playlist.on_drag_over(action, x, y, mask); -} - - -/** - * Called when mouse with content is being dragged outside of window. - * 3. called after on_drag_over. - */ -function on_drag_leave() { - trace_call && console.log('Playlist => on_drag_leave'); - playlist && playlist.on_drag_leave(); -} - - -/** - * Called when mouse with content is being dropped. - * 4. called after on_drag_over. - */ -function on_drag_drop(action, x, y, mask) { - trace_call && console.log('Playlist => on_drag_drop'); - playlist && playlist.on_drag_drop(action, x, y, mask); -} - - -//////////////////// -// * MAIN PANEL * // -//////////////////// -/** - * Creates the playlist panel container and passes callbacks and methods to the playlist. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @class - */ -function PlaylistPanel(x, y) { - this.x = x; - this.y = y; - this.w = 0; - this.h = 0; - - const that = this; - - /** - * @const - * @type {number} - */ - let playlist_info_h = SCALE(g_properties.row_h); - - /** - * @const - * @type {number} - */ - const playlist_info_and_gap_h = playlist_info_h + SCALE(4); - - let is_activated = window.IsVisible; - - const key_handler = new KeyActionHandler(); - - // Panel parts - const playlist_info = new PlaylistManager(that.x, that.y, 0, playlist_info_h); - const playlist = new Playlist(that.x, that.y + (g_properties.show_playlist_info ? playlist_info_and_gap_h : 0)); - - /** - * Adjusts the size and position of a playlist based on whether or not the playlist info is being shown. - * @param {boolean} show_playlist_info Whether to show the playlist information or not. - */ - const toggle_playlist_info = (show_playlist_info) => { - playlist.y = that.y + (show_playlist_info ? (playlist_info_and_gap_h) : 0); - const new_playlist_h = show_playlist_info ? (playlist.h - playlist_info_and_gap_h) : (playlist.h + playlist_info_and_gap_h); - playlist.on_size(playlist.w, new_playlist_h, playlist.x, playlist.y); - // Easier to repaint everything - window.Repaint(); - }; - - // #region Callback Implementation - - /** - * Draws the playlist and playlist manager. - * @param {GdiGraphics} gr - */ - this.on_paint = function (gr) { - gr.FillSolidRect(this.x, this.y, this.w, this.h, g_pl_colors.bg); // Main bg - if (!is_activated) { - is_activated = true; - - if (g_properties.show_playlist_info) { - playlist_info.reinitialize(); - } - playlist.reinitialize(); - } - - if (pref.styleBlend && albumArt && blendedImg && (displayPlaylist || displayPlaylistArtwork)) { - gr.DrawImage(blendedImg, displayLibrarySplit() ? pref.panelWidthAuto ? this.x : ww * 0.5 : 0, 0, ww, wh, displayLibrarySplit() ? pref.panelWidthAuto ? albumArtSize.x + albumArtSize.w : ww * 0.5 : 0, 0, blendedImg.Width, blendedImg.Height); - } - - playlist.on_paint(gr); - - // * Hide rows that shouldn't be visible - gr.SetSmoothingMode(SmoothingMode.None); - gr.FillSolidRect(this.x, 0, this.w, geo.topMenuHeight, col.bg); // Hide alpha overlapping at the top - gr.FillSolidRect(this.x, geo.topMenuHeight, this.w, playlist_info_h, g_pl_colors.bg); // Hide alpha overlapping at the top - gr.FillSolidRect(this.x, this.y + this.h - playlist_geo.row_h, this.w, playlist_geo.row_h + geo.lowerBarHeight, g_pl_colors.bg); // Hide alpha overlapping at the bottom - gr.FillSolidRect(this.x, this.y + this.h, this.w, geo.lowerBarHeight, col.bg); // Hide alpha overlapping at the bottom - - if (UIHacks.Aero.Effect === 2) gr.DrawLine(this.x, 0, ww, 0, 1, col.bg); // UIHacks aero glass shadow frame fix - needed for style Blend - - if (pref.styleBlend && albumArt && blendedImg) { - gr.DrawImage(blendedImg, this.x, this.y - this.h - geo.topMenuHeight - geo.lowerBarHeight + playlist_info_h, ww, wh, this.x, this.y - this.h - geo.topMenuHeight - geo.lowerBarHeight + playlist_info_h, blendedImg.Width, blendedImg.Height); - gr.DrawImage(blendedImg, this.x, this.y + this.h - playlist_geo.row_h, ww, wh, this.x, this.y + this.h - playlist_geo.row_h, blendedImg.Width, blendedImg.Height); - } - - if (g_properties.show_playlist_info) { - playlist_info.on_paint(gr); - } - }; - - /** - * Callback to update size and position of the playlist when window is resized. - * @param {number} w The width. - * @param {number} h The height. - */ - this.on_size = function (w, h) { - rescalePlaylist(); - - const x = setPlaylistX(); - const y = geo.topMenuHeight; - const playlist_w = w - x; - const playlist_h = Math.max(0, h - geo.lowerBarHeight - y); - const showPlaylistManager = pref[`showPlaylistManager_${pref.layout}`]; - - this.h = playlist_h; - this.w = playlist_w; - this.x = x; - this.y = y; - - playlist_info_h = SCALE(g_properties.row_h); - playlist.on_size(playlist_w, playlist_h - (playlist_info_h * 2), x, y + playlist_info_h + SCALE(4)); - playlist_info.set_xywh(x, y, showPlaylistManager ? this.w : 0); // Hide Playlist manager - - is_activated = window.IsVisible; - }; - - /** - * Callback for mouse move events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {object} m The mouse mask. - */ - this.on_mouse_move = (x, y, m) => { - playlist.on_mouse_move(x, y, m); - - if (g_properties.show_playlist_info) { - playlist_info.on_mouse_move(x, y, m); - } - }; - - /** - * Callback for left mouse button down events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {object} m The mouse mask. - */ - this.on_mouse_lbtn_down = (x, y, m) => { - playlist.on_mouse_lbtn_down(x, y, m); - - if (g_properties.show_playlist_info) { - playlist_info.on_mouse_lbtn_down(x, y, m); - } - }; - - /** - * Callback for left mouse button double click events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {object} m The mouse mask. - */ - this.on_mouse_lbtn_dblclk = (x, y, m) => { - playlist.on_mouse_lbtn_dblclk(x, y, m); - }; - - /** - * Callback for left mouse button up events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {object} m The mouse mask. - */ - this.on_mouse_lbtn_up = (x, y, m) => { - playlist.on_mouse_lbtn_up(x, y, m); - - if (g_properties.show_playlist_info) { - playlist_info.on_mouse_lbtn_up(x, y, m); - } - }; - - /** - * Callback for right mouse button down events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {object} m The mouse mask. - */ - this.on_mouse_rbtn_down = (x, y, m) => { - playlist.on_mouse_rbtn_down(x, y, m); - - if (g_properties.show_playlist_info) { - playlist_info.on_mouse_rbtn_down(x, y, m); - } - }; - - /** - * Callback for right mouse button up events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {object} m The mouse mask. - */ - this.on_mouse_rbtn_up = (x, y, m) => { - const was_playlist_info_displayed = g_properties.show_playlist_info; - - if (playlist.trace(x, y)) { - playlist.on_mouse_rbtn_up(x, y, m); - } - - if (g_properties.show_playlist_info) { - playlist_info.on_mouse_rbtn_up(x, y, m); - } - - if (was_playlist_info_displayed !== g_properties.show_playlist_info) { - toggle_playlist_info(g_properties.show_playlist_info); - } - - return true; - }; - - /** - * Callback for mouse wheel events. - * @param {number} step The amount of scrolling that occurred on the mouse wheel. - */ - this.on_mouse_wheel = (step) => { - playlist.on_mouse_wheel(step); - }; - - /** - * Callback for mouse leave events. - */ - this.on_mouse_leave = () => { - playlist.on_mouse_leave(); - if (g_properties.show_playlist_info) { - playlist_info.on_mouse_leave(); - } - }; - - /** - * Callback for drag enter events. - * @param {string} action The drag action. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. - */ - this.on_drag_enter = (action, x, y, mask) => { - playlist.on_drag_enter(action, x, y, mask); - }; - - /** - * Callback for drag leave events. - */ - this.on_drag_leave = () => { - playlist.on_drag_leave(); - }; - - /** - * Callback for drag over events. - * @param {string} action The drag action. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. - */ - this.on_drag_over = (action, x, y, mask) => { - playlist.on_drag_over(action, x, y, mask); - }; - - /** - * Callback for drag drop events. - * @param {string} action The drag action. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. - */ - this.on_drag_drop = (action, x, y, m) => { - playlist.on_drag_drop(action, x, y, m); - }; - - /** - * Callback for key down events. - * @param {number} vkey The virtual key code. - */ - this.on_key_down = (vkey) => { - playlist.on_key_down(vkey); - - const modifiers = { - ctrl: utils.IsKeyPressed(VK_CONTROL), - alt: utils.IsKeyPressed(VK_MENU), - shift: utils.IsKeyPressed(VK_SHIFT) - }; - key_handler.invoke_key_action(vkey, modifiers); - }; - - /** - * Callback for key up events. - * @param {number} vkey The virtual key code. - */ - this.on_key_up = (vkey) => { - playlist.on_key_up(vkey); - }; - - /** - * Callback for item focus change events. - * @param {number} playlist_idx The index of the current playlist. - * @param {number} from_idx The index of the item that lost focus. - * @param {number} to_idx The index of the item that gained focus. - */ - this.on_item_focus_change = (playlist_idx, from_idx, to_idx) => { - if (!is_activated) { - return; - } - - playlist.on_item_focus_change(playlist_idx, from_idx, to_idx); - }; - - /** - * Callback for scrolling the playlist to the currently focused item. - */ - this.scroll_to_focused = () => { - playlist.scroll_to_focused(); - }; - - /** - * Callback for playlist change events. - */ - this.on_playlists_changed = () => { - if (!is_activated) { - return; - } - - if (g_properties.show_playlist_info) { - playlist_info.on_playlist_modified(); - } - playlist.on_playlists_changed(); - }; - - /** - * Callback for playlist switch events. - */ - this.on_playlist_switch = () => { - if (!is_activated) { - return; - } - - if (g_properties.show_playlist_info) { - playlist_info.on_playlist_modified(); - } - playlist.on_playlist_switch(); - }; - - /** - * Callback for playlist item ensure visible events. - * @param {number} playlistIndex The index of the playlist. - * @param {number} playlistItemIndex The index of the item. - */ - this.on_playlist_item_ensure_visible = (playlistIndex, playlistItemIndex) => { - if (!is_activated) { - return; - } - - playlist.on_playlist_item_ensure_visible(playlistIndex, playlistItemIndex); - }; - - /** - * Callback for playlist items added events. - * @param {number} playlist_idx The index of the playlist. - */ - this.on_playlist_items_added = (playlist_idx) => { - if (!is_activated) { - return; - } - - if (g_properties.show_playlist_info) { - playlist_info.on_playlist_modified(); - } - playlist.on_playlist_items_added(playlist_idx); - }; - - /** - * Callback for playlist items reordered events. - * @param {number} playlist_idx The index of the playlist. - */ - this.on_playlist_items_reordered = (playlist_idx) => { - if (!is_activated) { - return; - } - - playlist.on_playlist_items_reordered(playlist_idx); - }; - - /** - * Callback for playlist items removed events. - * @param {number} playlist_idx The index of the playlist. - */ - this.on_playlist_items_removed = (playlist_idx) => { - if (!is_activated) { - return; - } - - if (g_properties.show_playlist_info) { - playlist_info.on_playlist_modified(); - } - playlist.on_playlist_items_removed(playlist_idx); - }; - - /** - * Callback for playlist items selection change events. - */ - this.on_playlist_items_selection_change = () => { - if (!is_activated) { - return; - } - - playlist.on_playlist_items_selection_change(); - if (g_properties.show_playlist_info) { - playlist_info.on_playlist_modified(); - } - }; - - /** - * Callback for playback dynamic info track events. - */ - this.on_playback_dynamic_info_track = () => { - if (!is_activated) { - return; - } - - playlist.on_playback_dynamic_info_track(); - }; - - /** - * Callback for playback new track events. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ - this.on_playback_new_track = (metadb) => { - if (!is_activated) { - return; - } - - playlist_info.hover_alpha = 0; // * Prevent Playlist manager bg flashing when bg color changes - - playlist.on_playback_new_track(metadb); - }; - - /** - * Callback for playback pause events. - * @param {boolean} state The new playback state. - */ - this.on_playback_pause = (state) => { - if (!is_activated) { - return; - } - - playlist.on_playback_pause(state); - }; - - /** - * Callback for playback queue change events. - * @param {string} origin The origin of the change. - */ - this.on_playback_queue_changed = (origin) => { - if (!is_activated) { - return; - } - - playlist.on_playback_queue_changed(origin); - }; - - /** - * Callback for playback stop events. - * @param {string} reason The reason for the stop. - */ - this.on_playback_stop = (reason) => { - if (!is_activated) { - return; - } - - playlist.on_playback_stop(reason); - }; - - /** - * Callback for focus events. - * @param {boolean} is_focused Whether the playlist is focused. - */ - this.on_focus = (is_focused) => { - if (!is_activated) { - return; - } - - playlist.on_focus(is_focused); - }; - - /** - * Callback for metadb change events. - * @param {Array} handles The handles of the metadbs that changed. - * @param {boolean} fromhook Whether the change was triggered by a hook. - */ - this.on_metadb_changed = (handles, fromhook) => { - if (!is_activated) { - return; - } - - if (g_properties.show_playlist_info) { - playlist_info.on_playlist_modified(); - } - playlist.on_metadb_changed(handles, fromhook); - }; - - /** - * Callback for get album art done events. - * @param {FbMetadbHandleList} metadb The metadb of the tracks. - * @param {number} art_id The id of the album art. - * @param {GdiBitmap} image The album art image. - * @param {string} image_path The path to the album art image. - */ - this.on_get_album_art_done = (metadb, art_id, image, image_path) => { - if (!is_activated) { - return; - } - - playlist.on_get_album_art_done(metadb, art_id, image, image_path); - }; - - /** - * Callback for notify data events. - * @param {string} name The name of the data. - * @param {Object} info The data. - */ - this.on_notify_data = (name, info) => { - playlist.on_notify_data(name, info); - }; - - // #endregion - - /** - * Callback for playlist header auto-collapse events. - */ - this.auto_collapse_header = () => { - playlist.auto_collapse_header(); - }; - - /** - * Callback for playlist header collapse events. - */ - this.collapse_header = () => { - playlist.collapse_header(); - }; - - /** - * Callback for playlist header expand events. - */ - this.expand_header = () => { - playlist.expand_header(); - }; - - /** - * Callback for playlist init events. - */ - this.initialize = () => { - playlist.register_key_actions(key_handler); - playlist_info.register_key_actions(key_handler); - - playlist.initialize_list(); - }; - - /** - * Callback for playlist scrollbar init events. - */ - this.initScrollbar = () => { - playlist.initScrollbar(); - }; - - /** - * Callback for setting now playing hyperlinks events. - */ - this.set_now_playing_hyperlink = () => { - playlist.set_now_playing_hyperlink(); - }; - - /** - * Callback for show now playing scroll events. - */ - this.show_now_playing = () => { - playlist.show_now_playing(); - }; - - /** - * Callback for stop dragging scroll events. - */ - this.stop_drag_scroll = () => { - playlist.stop_drag_scroll(); - }; - - /** - * Callback for update playlist title color events. - */ - this.title_color_change = () => { - playlist.title_color_change(); - }; -} - - -////////////////// -// * PLAYLIST * // -////////////////// -/** - * Draws the playlist rows and manages them. - */ -class Playlist extends List { - /** - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @extends {List} - * @class - */ - constructor(x, y) { - super(x, y, 0, 0, new PlaylistContent()); - - // * Constants - /** @type {number} */ - this.header_h_in_rows = this.calcHeaderRows(); - - // * Window state - this.was_on_size_called = false; - - this.is_in_focus = false; - - // * Playback state - /** @type {number} */ - this.cur_playlist_idx = undefined; - /** @type {?Row} */ - this.playing_item = undefined; - /** @type {?Row} */ - this.focused_item = undefined; - - // * Mouse and key state - this.mouse_on_item = false; - this.key_down = false; - this.drag_event_invoked = false; - - // * Item events - /** @type {?Row|?BaseHeader|?ListItem} */ - this.last_hover_item = undefined; - /** @type {{x: ?number, y: ?number}} */ - this.last_pressed_coord = { - x: undefined, - y: undefined - }; - - // * Timers - this.drag_scroll_in_progress = false; - this.drag_scroll_timeout_timer = 0; - this.drag_scroll_repeat_timer = 0; - - // * Scrollbar props - /** @type {Array} float */ - this.scroll_pos_list = []; - - // * Objects - /** @type {?SelectionHandler} */ - this.selection_handler = undefined; - /** @type {?QueueHandler} */ - this.queue_handler = undefined; - /** @type {?CollapseHandler} */ - this.collapse_handler = undefined; - /** @type {MetaHandler} */ - this.meta_handler = new MetaHandler(); - /** - * @const - * @type {ContentNavigationHelper} - */ - this.cnt_helper = this.cnt.helper; - - this.debounced_initialize_and_repaint_list = Debounce((refocus) => { - // Debouncing this because when swapping out playlist content, initialize_and_repaint_list will be called - // three times, once for each add/remove/changed callback - this.initialize_and_repaint_list(refocus); - }, 10, { - leading: false, - trailing: true - }); - } - - // #region Callback Implementation - - /** - * Draws the items in the playlist and a scrollbar if necessary. - * @param {GdiGraphics} gr - */ - on_paint(gr) { - gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); - - if (this.items_to_draw.length) { - // Mordred - Passing top, bottom for clipping purposes - for (let i = this.items_to_draw.length - 1; i >= 0; --i) { - this.items_to_draw[i].draw(gr, this.y, this.y + this.h); - } - } - else { - const empty = plman.PlaylistCount <= 1; - const name = plman.GetPlaylistName(this.cur_playlist_idx); - const text = empty ? 'Drop some tracks here\nor play from the library' : `Playlist: ${name}\nEmpty`; - gr.DrawString(text, g_pl_fonts.title_normal, g_pl_colors.row_title_normal, this.x, this.y, this.w, this.h, g_string_format.align_center); - } - - if (this.is_scrollbar_visible) { - this.scrollbar.paint(gr); - } - } - - /** - * Handles resizing of the playlist and updating its size and position. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - on_size(w, h, x, y) { - List.prototype.on_size.apply(this, [w, h, x, y]); - - this.x = x; - this.y = y; - this.h = h; - this.w = w; - this.was_on_size_called = true; - - if (g_properties.show_header && (g_properties.auto_collapse || g_properties.collapse_on_start)) { - this.collapse_handler.collapse_all_but_now_playing(); - } - - if (fb.IsPlaying && (pref.playlistAutoScrollNowPlaying || fb.PlaybackFollowCursor || fb.CursorFollowPlayback)) { - this.on_playback_new_track(); - } - } - - /** - * Handles mouse movement events and performs various actions based on the position of the mouse. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_move(x, y, m) { - if (List.prototype.on_mouse_move.apply(this, [x, y, m])) { - return true; - } - - const right_spacing = SCALE(20); - const item = this.get_item_under_mouse(x - right_spacing, y); - - if (item instanceof Header) { - item.headerTooltip(x, y); - if (item.on_mouse_move(x, y, m)) return true; - } - else if (item instanceof Row) { - item.titleTooltip(x, y); - if (item.on_mouse_move(x, y, m)) return true; - } - else styledTooltipReady = false; - - if (!this.mouse_down) { - return true; - } - - if (!this.selection_handler.is_dragging() && this.last_hover_item) { - const drag_diff = Math.sqrt((Math.pow(this.last_pressed_coord.x - x, 2) + Math.pow(this.last_pressed_coord.y - y, 2))); - if (drag_diff >= 7) { - this.last_pressed_coord = { - x: undefined, - y: undefined - }; - this.last_hover_item = this.get_item_under_mouse(x, y); - - this.selection_handler.perform_internal_drag_n_drop(); - } - } - - return true; - } - - /** - * Handles left mouse button down events and performs various actions based on the mouse position and key presses. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_down(x, y, m) { - if (List.prototype.on_mouse_lbtn_down.apply(this, [x, y, m])) { - return true; - } - - const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); - const shift_pressed = utils.IsKeyPressed(VK_SHIFT); - - /** @type {BaseHeader|Row} */ - // @ts-ignore - const item = this.trace_list(x, y) ? this.get_item_under_mouse(x, y) : undefined; - this.last_hover_item = item; - this.last_pressed_coord.x = x; - this.last_pressed_coord.y = y; - - if (item) { - if ((!pref.hyperlinksCtrlClick || ctrl_pressed) && item instanceof Header) { - if (item.on_mouse_lbtn_down(x, y, m)) { - return true; // Was handled by hyperlinks - } - } - if (ctrl_pressed && shift_pressed && item instanceof BaseHeader) { - this.collapse_handler.toggle_collapse(item); - this.mouse_down = false; - } - else if (shift_pressed - || (item instanceof Row && !item.is_selected() - || item instanceof BaseHeader && !item.is_completely_selected())) { - this.selection_handler.update_selection(item, ctrl_pressed, shift_pressed); - } - else { - // Indicates the need to update selection on on_mouse_lbtn_up - this.mouse_on_item = true; - } - } - else { - this.selection_handler.clear_selection(); - } - - this.repaint(); - - return true; - } - - /** - * Handles mouse double click events. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_dblclk(x, y, m) { - if (List.prototype.on_mouse_lbtn_dblclk.apply(this, [x, y, m])) { - return true; - } - - const item = this.get_item_under_mouse(x, y); - if (!item) { - return true; - } - - if (item instanceof BaseHeader) { - if (item instanceof DiscHeader) { - item.on_mouse_lbtn_dblclk(this.collapse_handler); - } else { - item.on_mouse_lbtn_dblclk(x, y, m); - } - this.repaint(); - } else if (item instanceof Row) { - if (g_properties.show_rating && item.rating_trace(x, y)) { - item.rating_click(x, y); - item.repaint(); - } - else { - plman.ExecutePlaylistDefaultAction(this.cur_playlist_idx, item.idx); - } - } - - return true; - } - - /** - * Handles mouse left button up events and updates the selection of an item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_up(x, y, m) { - const was_double_clicked = this.mouse_double_clicked; - - if (List.prototype.on_mouse_lbtn_up.apply(this, [x, y, m])) { - return true; - } - - this.last_pressed_coord = { - x: undefined, - y: undefined - }; - - if (was_double_clicked) { - return true; - } - - this.last_hover_item = undefined; - - // Drag is handled in on_drag_drop - if (!this.selection_handler.is_dragging() && this.mouse_on_item) { - const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); - const shift_pressed = utils.IsKeyPressed(VK_SHIFT); - /** @type {Row|BaseHeader} */ - // @ts-ignore - const item = this.get_item_under_mouse(x, y); - if (item) { - this.selection_handler.update_selection(item, ctrl_pressed, shift_pressed); - } - } - - this.mouse_on_item = false; - this.repaint(); - - return true; - } - - /** - * Handles right mouse button down events and updates the selection. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_rbtn_down(x, y, m) { - if (!this.cnt.rows.length) { - return; - } - - if (this.is_scrollbar_visible && this.scrollbar.trace(x, y)) { - return; - } - - const item = this.trace_list(x, y) ? this.get_item_under_mouse(x, y) : undefined; - if (!item) { - this.selection_handler.clear_selection(); - } - else if (item instanceof Row && !item.is_selected() - || item instanceof BaseHeader && !item.is_completely_selected()) { - this.selection_handler.update_selection(item); - } - - this.repaint(); - } - - /** - * Handles right mouse button up events and displays a context menu with various options. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_rbtn_up(x, y, m) { - if (List.prototype.on_mouse_rbtn_up.apply(this, [x, y, m])) { - return true; - } - - const metadb = utils.IsKeyPressed(VK_CONTROL) ? (fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem()) : fb.GetFocusItem(); - const has_selected_item = this.selection_handler.has_selected_items(); - const is_cur_playlist_empty = !this.cnt.rows.length; - const cmm = new ContextMainMenu(); - - // * Top menu options Playlist submenu - cmm.append_item('Playlist options menu', () => { - if (displayPlaylist || displayPlaylistArtwork) { - topMenuOptions(state.mouse_x, state.mouse_y, true, true); - } - }); - cmm.append_separator(); - - if (pref.layout === 'default' && pref.theme.startsWith('custom')) { - cmm.append_item('Edit custom theme', () => { - displayCustomThemeMenu = true; - displayPanel('playlist'); - initCustomThemeMenu('pl_bg'); - window.Repaint(); - }); - cmm.append_separator(); - } - - if (pref.layout === 'default' && !displayLibrarySplit()) { - if (displayPlaylist && !displayBiography && !pref.displayLyrics) { - cmm.append_item(displayPlaylist && pref.playlistLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { - pref.playlistLayout = pref.playlistLayout === 'normal' ? 'full' : 'normal'; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - playlist.on_size(ww, wh); - jumpSearch.on_size(); - window.Repaint(); - }); - cmm.append_separator(); - } - else if (displayBiography && displayPlaylist) { - cmm.append_item(displayPlaylist && displayBiography && pref.biographyLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { - if (pref.biographyLayout === 'normal') { - pref.biographyLayout = 'full'; - displayPlaylist = false; - } else { - pref.biographyLayout = 'normal'; - displayPlaylist = true; - } - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - initBiographyLayout(); - }); - cmm.append_separator(); - } - else if (pref.displayLyrics) { - cmm.append_item(pref.displayLyrics && pref.lyricsLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { - pref.lyricsLayout = pref.lyricsLayout === 'normal' ? 'full' : 'normal'; - displayPlaylist = !displayPlaylist; - if (pref.panelWidthAuto) { - initPanelWidthAuto(); - } - resizeArtwork(true); - window.Repaint(); - }); - cmm.append_separator(); - } - } - - if (fb.IsPlaying) { - cmm.append_item('Show now playing', () => { - this.show_now_playing(); - }); - } - - if (playlistHistory.canBack()) { - cmm.append_item('Previous playlist state', () => { - playlistHistory.back(); - }); - } - if (playlistHistory.canForward()) { - cmm.append_item('Next playlist state', () => { - playlistHistory.forward(); - }); - } - - if (!is_cur_playlist_empty) { - cmm.append_item('Refresh playlist \tF5', () => { - Header.art_cache.clear(); - this.initialize_list(); - this.scroll_to_focused(); - }); - - if (this.queue_handler && this.queue_handler.has_items()) { - cmm.append_item('Flush playback queue \tCtrl+Shift+Q', () => { - this.queue_handler.flush(); - }); - } - cmm.append_separator(); - } - - this.append_pltools_menu_to(cmm); - - this.append_edit_menu_to(cmm); - - if (!is_cur_playlist_empty) { - if (!cmm.is_empty()) { - cmm.append_separator(); - } - - if (this.collapse_handler) { - this.append_collapse_menu_to(cmm); - } - - this.append_appearance_menu_to(cmm); - - if (g_properties.show_header) { - Header.grouping_handler.append_menu_to(cmm, () => { - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - this.repaint(); - }); - } - - this.append_sort_menu_to(cmm); - - if (pref.showWeblinks) { - this.append_weblinks_menu_to(cmm, metadb); - } - - if (has_selected_item) { - this.append_send_items_menu_to(cmm); - - cmm.append_separator(); - cmm.append_item('Write theme to tags', () => { - WriteThemeTags(); - }); - cmm.append_item('Write album statistics to tags', () => { - this.meta_handler.write_album_stats_to_tags(); - }); - this.append_write_playlist_stats_list_menu_to(cmm); - } - } - else { - // Empty playlist - - if (!cmm.is_empty()) { - cmm.append_separator(); - } - - const appear = new ContextMenu('Appearance'); - cmm.append(appear); - - appear.append_item('Show playlist info', () => { - g_properties.show_playlist_info = !g_properties.show_playlist_info; - }, { is_checked: g_properties.show_playlist_info }); - - this.append_scrollbar_visibility_context_menu_to(appear); - } - - // -------------------------------------------------------------- // - // * Context Menu Manager - - if (has_selected_item) { - if (!cmm.is_empty()) { - cmm.append_separator(); - } - - const ccmm = new ContextFoobarMenu(plman.GetPlaylistSelectedItems(this.cur_playlist_idx)); - cmm.append(ccmm); - } - - // -------------------------------------------------------------- // - // * System - - if (utils.IsKeyPressed(VK_SHIFT)) { - qwr_utils.append_default_context_menu_to(cmm); - } - - activeMenu = true; - cmm.execute(x, y); - activeMenu = false; - - this.repaint(); - return true; - } - - /** - * Handles mouse leave events and checks if an internal drag and drop operation is active. - * @override - */ - on_mouse_leave() { - if (this.selection_handler.is_internal_drag_n_drop_active() - && this.selection_handler.is_dragging() - && !this.drag_event_invoked) { - // Workaround for the following issues: - // #1 if you move too fast out of the panel, then drag_enter is not invoked (thus we need to clear mouse state here). - // #2 on_mouse_leave sometimes generated during internal drag_over (so we need to ignore it). - this.selection_handler.disable_drag(); - this.drag_event_invoked = false; - this.mouse_in = false; - this.mouse_down = false; - this.repaint(); - } - List.prototype.on_mouse_leave.apply(this); - } - - /** - * Handles drag enter events and checks if the mouse is inside the element, sets the mouse down state, - * enables dragging based on the action type, and determines the drop effect based on the trace list and selection handler. - * @param {DropTargetAction} action The drag action that is being performed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. - */ - on_drag_enter(action, x, y, mask) { - this.mouse_in = true; - this.mouse_down = true; - this.drag_event_invoked = true; - - if (!this.selection_handler.is_dragging()) { - if (action.IsInternal) { - this.selection_handler.enable_drag(); - } - else { - this.selection_handler.enable_external_drag(); - } - } - - if (!this.trace_list(x, y) || !this.selection_handler.can_drop()) { - action.Effect = g_drop_effect.none; - } - else { - action.Effect = (action.Effect & g_drop_effect.move) - || (action.Effect & g_drop_effect.copy) - || (action.Effect & g_drop_effect.link); - } - } - - /** - * Handles drag leave events when the mouse leaves a draggable item. - */ - on_drag_leave() { - if (this.selection_handler.is_dragging()) { - this.stop_drag_scroll(); - this.selection_handler.disable_drag(); - } - - this.drag_event_invoked = false; - this.mouse_in = false; - this.mouse_down = false; - - this.repaint(); - } - - /** - * Handles drag over events and allows for dragging and dropping of rows. - * @param {DropTargetAction} action The drag action that is being performed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. - * @returns {number} The value of the `action.Effect` property. - */ - on_drag_over(action, x, y, mask) { - if (!this.selection_handler.can_drop()) { - action.Effect = g_drop_effect.none; - return; - } - - const drop_info = this.get_drop_row_info(x, y); - const row = drop_info.row; - - if (this.drag_scroll_in_progress) { - if (!row || (y >= (this.list_y + this.row_h * 2) && y <= (this.list_y + this.list_h - this.row_h * 2))) { - this.stop_drag_scroll(); - } - } - else if (row) { - if (this.collapse_handler) { - this.collapse_handler.expand(row.parent); - if (this.collapse_handler.changed) { - // * Fix to restore drag scroll to last row item at bottom when header is collapsed - this.scrollbar.is_scrolled_down = false; - this.repaint(); - } - } - - this.selection_handler.drag(row, drop_info.is_above); - - if (this.is_scrollbar_available) { - if (y < (this.list_y + this.row_h * 2) && !this.scrollbar.is_scrolled_up) { - this.selection_handler.drag(null, false); // To clear last hover row - this.start_drag_scroll('up'); - } - if (y > (this.list_y + this.list_h - this.row_h * 2) && !this.scrollbar.is_scrolled_down) { - this.selection_handler.drag(null, false); // To clear last hover row - this.start_drag_scroll('down'); - } - } - } - - this.last_hover_item = - /** @type {?BaseHeader|?Row} */ this.get_item_under_mouse(x, y); - - if (!this.trace_list(x, y)) { - action.Effect = g_drop_effect.none; - } - else { - action.Effect = g_drop_effect.copy; // * Wine/Linux drag n drop fix // action.Effect = this.filter_effect_by_modifiers(action.Effect); - } - } - - /** - * Handles drag and drop events and checks if dragging is allowed. Also handles internal and external drops. - * @param {DropTargetAction} action The drag action that is being performed. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} mask The mouse mask. - */ - on_drag_drop(action, x, y, m) { - this.mouse_down = false; ///< Because on_drag_drop suppresses on_mouse_lbtn_up call - this.stop_drag_scroll(); - - if (!this.selection_handler.is_dragging() || !this.trace_list(x, y) || !this.selection_handler.can_drop()) { - this.selection_handler.disable_drag(); - action.Effect = g_drop_effect.none; - return; - } - - const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); - - if (action.IsInternal) { - const copy_drop = ctrl_pressed && ((action.Effect & 1) || (action.Effect & 4)); - this.selection_handler.drop(!!copy_drop); - - // Suppress native drop, since we've handled it ourselves - action.Effect = g_drop_effect.none; - } - else { - action.Effect = g_drop_effect.copy; // * Wine/Linux drag n drop fix // action.Effect = this.filter_effect_by_modifiers(action.Effect); - if (g_drop_effect.none !== action.Effect) { - this.selection_handler.external_drop(action); - } - else { - this.selection_handler.disable_drag(); - } - } - } - - /** - * Handles key down events when a key on the keyboard is pressed down. - * @param {number} vkey The virtual key code. - */ - on_key_down(vkey) { - this.key_down = true; - } - - /** - * Handles key up events when a key on the keyboard is pressed up. - * @param {number} vkey The virtual key code. - */ - on_key_up(vkey) { - this.key_down = false; - } - - /** - * Handles changes in focus between items in the playlist, updating the focused item and scrolling to it if necessary. - * @param {number} playlist_idx The index of the playlist that the focus change event is occurring in. - * @param {number} from_idx The index of the previously focused item in the playlist. - * @param {number} to_idx The index of the item that is gaining focus in the playlist. - */ - on_item_focus_change(playlist_idx, from_idx, to_idx) { - if (playlist_idx !== this.cur_playlist_idx || this.focused_item && this.focused_item.idx === to_idx) { - return; - } - - if (this.focused_item) { - this.focused_item.is_focused = false; - } - - if (to_idx === -1) { - this.focused_item = undefined; - } - else if (this.cnt.rows.length && to_idx >= 0 && to_idx < this.cnt.rows.length) { - to_idx = Math.min(to_idx, this.cnt.rows.length - 1); - this.focused_item = this.cnt.rows[to_idx]; - this.focused_item.is_focused = true; - } - - if (this.focused_item) { - const from_row = from_idx === -1 ? null : this.cnt.rows[from_idx]; - const playing_item_location = plman.GetPlayingItemLocation(); - if (!playing_item_location.IsValid || this.on_playlist_items_removed) { // * Prevent scroll jump when removing items - return; - } - this.scroll_to_row(from_row, this.focused_item); - } - - this.repaint(); - } - - /** - * Handles changes in the playlists when content is added/removed/reordered/renamed. - */ - on_playlists_changed() { - if ((plman.ActivePlaylist > plman.PlaylistCount || plman.ActivePlaylist === -1) && plman.PlaylistCount > 0) { - plman.ActivePlaylist = plman.PlaylistCount - 1; - } - - Header.grouping_handler.on_playlists_changed(); - Header.grouping_handler.set_active_playlist(plman.GetPlaylistName(plman.ActivePlaylist)); - - if (plman.ActivePlaylist !== this.cur_playlist_idx) { - this.initialize_and_repaint_list(); - } - - if (this.collapse_handler && g_properties.show_header && (g_properties.auto_collapse || g_properties.collapse_on_start)) { - this.collapse_handler.collapse_all_but_now_playing(); - } - } - - /** - * Handles when active playlist changes to another playlist index. - */ - on_playlist_switch() { - if (this.cur_playlist_idx !== plman.ActivePlaylist) { - g_properties.scroll_pos = this.scroll_pos_list[plman.ActivePlaylist] == null ? 0 : this.scroll_pos_list[plman.ActivePlaylist]; - } - - this.initialize_and_repaint_list(); - - if (this.collapse_handler && g_properties.show_header && (g_properties.auto_collapse || g_properties.collapse_on_start)) { - this.collapse_handler.collapse_all_but_now_playing(); - } - } - - /** - * Handles and ensures that a playlist item is visible in the playlist if it is not already. - * @param {number} playlist_idx The current playlist index. - * @param {number} playlistItemIndex The playlist item index that needs to be ensured visible in the playlist. - */ - on_playlist_item_ensure_visible(playlist_idx, playlistItemIndex) { - if (playlist_idx !== this.cur_playlist_idx) { - return; - } - - const row = this.cnt.rows[playlistItemIndex]; - if (!row) { - return; - } - - if (!displayLibrarySplit()) this.scroll_to_row(null, row); // * Prevent scroll to focused after drag and drop in split layout - } - - /** - * Handles when playlist items are added and sets the playlist sort order and updates the playlist. - * @param {number} playlist_idx The current playlist index. - */ - on_playlist_items_added(playlist_idx) { - if (playlist_idx !== this.cur_playlist_idx) { - return; - } - if (pref.playlistSortOrderAuto) setPlaylistSortOrder(); - - this.debounced_initialize_and_repaint_list(); - } - - /** - * Handles when playlist items are reordered and updates the playlist. - * @param {number} playlist_idx The current playlist index. - */ - on_playlist_items_reordered(playlist_idx) { - if (playlist_idx !== this.cur_playlist_idx) { - return; - } - - this.debounced_initialize_and_repaint_list(true); - } - - /** - * Handles when playlist items are removed and updates the playlist. - * @param {number} playlist_idx The current playlist index. - */ - on_playlist_items_removed(playlist_idx) { - if (playlist_idx !== this.cur_playlist_idx) { - return; - } - - this.debounced_initialize_and_repaint_list(); - } - - /** - * Handles when playlist item selection has been changed and updates the selection. - */ - on_playlist_items_selection_change() { - if (!this.mouse_in && !this.key_down) { - this.selection_handler.initialize_selection(); - } - } - - /** - * Handles when Per-track dynamic info (stream track titles etc) changes and resets queried data - * and hyperlinks for each row and header, and then updates the playlist. - */ - on_playback_dynamic_info_track() { - this.cnt.rows.forEach((item) => { - item.reset_queried_data(); - }); - - this.cnt.sub_items.forEach((header) => { - header.reset_hyperlinks(); - }); - - this.repaint(); - } - - /** - * Updates the currently playing track in the playlist and performs actions such as clearing the title text, - * setting the track as playing, collapsing the playlist if necessary, and scrolling to the currently playing track. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ - on_playback_new_track(metadb) { - if (this.playing_item) { - this.playing_item.is_playing = false; - this.playing_item.clear_title_text(); - this.playing_item = undefined; - } - - const playing_item_location = plman.GetPlayingItemLocation(); - if (playing_item_location.IsValid && playing_item_location.PlaylistIndex === this.cur_playlist_idx) { - this.playing_item = this.cnt.rows[playing_item_location.PlaylistItemIndex]; - if (this.playing_item) { - this.playing_item.is_playing = true; - this.playing_item.clear_title_text(); - } - - if (this.collapse_handler && g_properties.show_header && (g_properties.auto_collapse || g_properties.collapse_on_start)) { - this.selection_handler.clear_selection(); - this.collapse_handler.collapse_all_but_now_playing(); - this.scroll_to_now_playing(); - } - } - - if (pref.playlistAutoScrollNowPlaying || fb.PlaybackFollowCursor || fb.CursorFollowPlayback) { - setTimeout(() => { // * Wait until new album art / disc art loaded and other things finished for smoother auto-scrolling - this.show_now_playing(); - }, newTrackFetchingDone + 200); - } - - this.repaint(); - } - - /** - * Handles when playback pauses and repaints the currently playing item. - * @param {boolean} state The new playback state. - */ - on_playback_pause(state) { - if (this.playing_item) { - this.playing_item.repaint(); - } - } - - /** - * Handles when playback items are send in queue, initializes the queue handler and updates the playlist. - * @param {number} origin The origin of the change. - */ - on_playback_queue_changed(origin) { - if (!this.queue_handler) { - return; - } - - this.queue_handler.initialize_queue(); - this.repaint(); - } - - /** - * Handles playback stop events and clears the title text of the currently playing item and updates its track number. - * @param {number} reason The type of playback stop. - */ - on_playback_stop(reason) { - if (this.playing_item) { - this.playing_item.clear_title_text(); // Mordred: need to regen tracknumber - this.playing_item.is_playing = false; - this.playing_item.repaint(); - } - } - - /** - * Updates the focus state of an item and triggers a repaint if necessary. - * @param {boolean} is_focused Whether the playlist is focused. - */ - on_focus(is_focused) { - if (this.focused_item) { - this.focused_item.is_focused = is_focused; - this.focused_item.repaint(); - } - this.is_in_focus = is_focused; - } - - /** - * Handles the metadb changed event and updates the header and queried data. - * @param {FbMetadbHandleList} handles The metadb of the tracks. - * @param {boolean} fromhook Whether the `on_metadb_changed` event was triggered by a hook or not. - */ - on_metadb_changed(handles, fromhook) { - handles.Sort(); - for (const item of this.cnt.sub_items) { - // Only need to update the header data if it's already been drawn/cached - if (item instanceof Header && (item.header_image || item.hyperlinks_initialized)) { - const index = handles.BSearch(item.get_first_row().metadb); - // If the item is in the array, update its header image and hyperlinks. - if (index !== -1) { - item.header_image = null; - item.reset_hyperlinks(); - } - } - } - - this.cnt.rows.forEach((item) => { - // Reset the queried data for each row. - item.reset_queried_data(); - }); - } - - /** - * Assigns album art to a header item and repaints if the art is not already loaded. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @param {number} art_id The id for the album art is used to associate the album art with the specific header. - * @param {GdiBitmap} image The album art image that was retrieved. - * @param {string} image_path The file path where the album art image is stored. - */ - on_get_album_art_done(metadb, art_id, image, image_path) { - if (!image) { - image = null; - } - - /** @type {Array} */ - const items = this.items_to_draw; - items.forEach((item) => { - if (item instanceof Header && (!item.is_art_loaded() && item.get_first_row().metadb.Compare(metadb))) { - item.assign_art(image); - item.repaint(); - } - }); - } - - /** - * Handles notifications and synchronizes the state of a grouping handler. - * Called in other panels after window.NotifyOthers is executed. - * @param {string} name The name of the notification event that triggered the function. - * @param {*} info The information about the state of the sync group. - */ - on_notify_data(name, info) { - if (name === 'sync_group_query_state') { - if (!window.IsVisible) { - // Need to reinitialize grouping_handler manually, since most of the callbacks are ignored when panel is hidden - Header.grouping_handler.on_playlists_changed(); - Header.grouping_handler.set_active_playlist(plman.GetPlaylistName(plman.ActivePlaylist)); - Header.grouping_handler.sync_state(info); - } - else { - Header.grouping_handler.sync_state(info); - - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - } - } - } - - // #endregion - - /** - * Registers key actions and handles key presses. - * @param {KeyActionHandler} key_handler The KeyActionHandler object. - */ - register_key_actions(key_handler) { - key_handler.register_key_action(VK_UP, - (modifiers) => { - if (!this.cnt.rows.length) { - return; - } - - if (modifiers.ctrl && modifiers.shift) { - if (!this.selection_handler.has_selected_items()) { - return; - } - - this.selection_handler.move_selection_up(); - return; - } - - if (!this.focused_item) { - const top_item = this.items_to_draw[0]; - this.focused_item = top_item instanceof Row ? top_item : top_item.get_first_row(); - } - - const visible_item = this.cnt_helper.is_item_visible(this.focused_item) ? this.focused_item : this.cnt_helper.get_visible_parent(this.focused_item); - let new_item = this.cnt_helper.get_navigateable_neighbour(visible_item, -1); - if (!new_item) { - new_item = visible_item; - } - - this.selection_handler.update_selection(new_item, undefined, modifiers.shift); - this.repaint(); - }); - - key_handler.register_key_action(VK_DOWN, - (modifiers) => { - if (!this.cnt.rows.length) { - // Skip repaint - return; - } - - if (modifiers.ctrl && modifiers.shift) { - if (!this.selection_handler.has_selected_items()) { - return; - } - - this.selection_handler.move_selection_down(); - return; - } - - if (!this.focused_item) { - const top_item = this.items_to_draw[0]; - this.focused_item = top_item instanceof Row ? top_item : top_item.get_first_row(); - } - - const visible_item = this.cnt_helper.is_item_visible(this.focused_item) ? this.focused_item : this.cnt_helper.get_visible_parent(this.focused_item); - let new_item = this.cnt_helper.get_navigateable_neighbour(visible_item, 1); - if (!new_item) { - new_item = visible_item; - } - - this.selection_handler.update_selection(new_item, undefined, modifiers.shift); - this.repaint(); - }); - - key_handler.register_key_action(VK_LEFT, - (modifiers) => { - if (!this.collapse_handler || !this.cnt.rows.length) { - return; - } - - if (!this.focused_item) { - const top_item = this.items_to_draw[0]; - this.focused_item = top_item instanceof Row ? top_item : top_item.get_first_row(); - } - /** @type {BaseHeader|Row} */ - let new_focus = this.focused_item; - - // Get top uncollapsed header - let visible_header = this.cnt_helper.get_visible_parent(this.focused_item); - if (visible_header) { - while (visible_header.parent instanceof BaseHeader && visible_header.is_collapsed) { - visible_header = visible_header.parent; - } - - this.collapse_handler.collapse(visible_header); - new_focus = visible_header; - } - - this.selection_handler.update_selection(new_focus); - this.repaint(); - }); - - key_handler.register_key_action(VK_RIGHT, - (modifiers) => { - if (!this.collapse_handler || !this.cnt.rows.length) { - return; - } - - if (!this.focused_item) { - const top_item = this.items_to_draw[0]; - this.focused_item = top_item instanceof Row ? top_item : top_item.get_first_row(); - } - /** @type {BaseHeader|Row} */ - let new_focus = this.focused_item; - - const visible_header = this.cnt_helper.get_visible_parent(this.focused_item); - const new_focus_item = visible_header.get_first_row(); - - this.collapse_handler.expand(visible_header); - if (this.collapse_handler.changed) { - this.scroll_to_row(this.focused_item, new_focus_item); - new_focus = new_focus_item; - } - - this.selection_handler.update_selection(new_focus); - this.repaint(); - }); - - key_handler.register_key_action(VK_PRIOR, - (modifiers) => { - if (!this.cnt.rows.length) { - return; - } - - if (!this.focused_item) { - const top_item = this.items_to_draw[0]; - this.focused_item = top_item instanceof Row ? top_item : top_item.get_first_row(); - } - - let new_focus_item; - if (this.is_scrollbar_available) { - new_focus_item = this.items_to_draw[0]; - if (!this.cnt_helper.is_item_navigateable(new_focus_item)) { - new_focus_item = this.cnt_helper.get_navigateable_neighbour(new_focus_item, 1); - } - if (new_focus_item.y < this.list_y && new_focus_item.y + new_focus_item.h > this.list_y) { - new_focus_item = this.cnt_helper.get_navigateable_neighbour(new_focus_item, 1); - } - if (new_focus_item === this.focused_item) { - this.scrollbar.shift_page(-1); - - new_focus_item = this.items_to_draw[0]; - if (!this.cnt_helper.is_item_navigateable(new_focus_item)) { - new_focus_item = this.cnt_helper.get_navigateable_neighbour(new_focus_item, 1); - } - } - } - else { - new_focus_item = this.items_to_draw[0]; - } - - this.selection_handler.update_selection(new_focus_item, undefined, modifiers.shift); - this.repaint(); - }); - - key_handler.register_key_action(VK_NEXT, - (modifiers) => { - if (!this.cnt.rows.length) { - return; - } - - if (!this.focused_item) { - this.focused_item = this.items_to_draw[0]; - if (!this.cnt_helper.is_item_navigateable(this.focused_item)) { - // @ts-ignore - this.focused_item = this.cnt_helper.get_navigateable_neighbour(this.focused_item, 1); - } - } - - let new_focus_item; - if (this.is_scrollbar_available) { - new_focus_item = Last(this.items_to_draw); - if (!this.cnt_helper.is_item_navigateable(new_focus_item)) { - new_focus_item = this.cnt_helper.get_navigateable_neighbour(new_focus_item, -1); - } - if (new_focus_item.y < this.list_y + this.list_h && new_focus_item.y + new_focus_item.h > this.list_y + this.list_h) { - new_focus_item = this.cnt_helper.get_navigateable_neighbour(new_focus_item, -1); - } - if (new_focus_item === this.focused_item) { - this.scrollbar.shift_page(1); - new_focus_item = Last(this.items_to_draw); - if (!this.cnt_helper.is_item_navigateable(new_focus_item)) { - new_focus_item = this.cnt_helper.get_navigateable_neighbour(new_focus_item, -1); - } - } - } - else { - new_focus_item = Last(this.items_to_draw); - } - - this.selection_handler.update_selection(new_focus_item, undefined, modifiers.shift); - this.repaint(); - }); - - key_handler.register_key_action(VK_HOME, - (modifiers) => { - this.selection_handler.update_selection(this.cnt.rows[0], undefined, modifiers.shift); - this.scrollbar.scroll_to_start(); - }); - - key_handler.register_key_action(VK_END, - (modifiers) => { - this.selection_handler.update_selection(Last(this.cnt.rows), undefined, modifiers.shift); - this.scrollbar.scroll_to_end(); - }); - - key_handler.register_key_action(VK_DELETE, - (modifiers) => { - if (!this.selection_handler.has_selected_items() && this.focused_item) { - this.selection_handler.update_selection(this.focused_item); - } - plman.UndoBackup(this.cur_playlist_idx); - plman.RemovePlaylistSelection(this.cur_playlist_idx); - }); - - key_handler.register_key_action(VK_KEY_A, - (modifiers) => { - if (modifiers.ctrl) { - this.selection_handler.select_all(); - this.repaint(); - } - }); - - key_handler.register_key_action(VK_KEY_F, - (modifiers) => { - if (modifiers.ctrl) { - fb.RunMainMenuCommand('Edit/Search'); - } - else if (modifiers.shift) { - fb.RunMainMenuCommand('Library/Search'); - } - }); - - key_handler.register_key_action(VK_RETURN, - (modifiers) => { - if (!this.focused_item) { // Needed to reinit lost focus to prevent crash, e.g from 3rd party components using their own window - const top_item = this.items_to_draw[0]; - this.focused_item = top_item instanceof Row ? top_item : top_item.get_first_row(); - } - plman.ExecutePlaylistDefaultAction(this.cur_playlist_idx, this.focused_item.idx); - }); - - key_handler.register_key_action(VK_KEY_O, - (modifiers) => { - if (modifiers.shift) { - fb.RunContextCommandWithMetadb('Open Containing Folder', this.focused_item.metadb); - } - }); - - key_handler.register_key_action(VK_KEY_Q, - (modifiers) => { - if (!this.queue_handler) { - return; - } - - if (modifiers.ctrl && modifiers.shift) { - this.queue_handler.flush(); - } - else if (this.selection_handler.selected_items_count() >= 1) { - const rows = this.cnt.rows; - if (modifiers.ctrl) { - const indexes = this.selection_handler.get_selected_items(); - indexes.forEach((idx) => { - this.queue_handler.add_row(rows[idx]); - }); - } - else if (modifiers.shift) { - const indexes = this.selection_handler.get_selected_items(); - indexes.forEach((idx) => { - this.queue_handler.remove_row(rows[idx]); - }); - } - } - }); - - key_handler.register_key_action(VK_F5, - (modifiers) => { - Header.art_cache.clear(); - this.initialize_and_repaint_list(true); - }); - - key_handler.register_key_action(VK_KEY_C, - (modifiers) => { - if (modifiers.ctrl) { - this.selection_handler.copy(); - } - }); - - key_handler.register_key_action(VK_KEY_X, - (modifiers) => { - if (modifiers.ctrl) { - this.selection_handler.cut(); - } - }); - - key_handler.register_key_action(VK_KEY_V, - (modifiers) => { - if (modifiers.ctrl && !plman.IsPlaylistLocked(this.cur_playlist_idx)) { - this.selection_handler.paste(); - } - }); - } - - /** - * Collapses or expands the playlist header. - */ - auto_collapse_header() { - if (g_properties.show_header && g_properties.auto_collapse) { - this.collapse_handler.collapse_all_but_now_playing(); - if (this.collapse_handler.changed && pref.playlistAutoScrollNowPlaying) { - this.scroll_to_now_playing_or_focused(); - } - } else { - this.collapse_handler.expand_all(); - } - } - - /** - * Collapses the playlist header. - */ - collapse_header() { - playlist.auto_collapse_header(); - } - - /** - * Expands the playlist header. - */ - expand_header() { - this.collapse_handler.expand_all(); - } - - /** - * Updates the playlist with the option to refocus on a specific playlist item. - * @param {boolean} refocus Whether the playlist should be scrolled to the focused item after init. - */ - initialize_and_repaint_list(refocus) { - this.initialize_list(); - if (refocus) { - this.scroll_to_focused(); // Needed after drag-drop, because it might cause dropped (i.e. focused) item to be outside of drawn list - } - this.repaint(); - } - - /** - * Initializes and updates the playlist. - * Clearing its contents, initializing rows and headers and setting up various objects and handlers. - * This method does not contain any redraw calls, it's purely back-end. - */ - initialize_list() { - trace_call && console.log('initialize_list'); - const profiler = fb.CreateProfiler(); - const profiler_part = trace_initialize_list_performance && fb.CreateProfiler(); - - this.cur_playlist_idx = plman.ActivePlaylist; - - // * Clear contents - - this.cnt.rows = []; - this.cnt.sub_items = []; - - // * Initialize rows - - trace_initialize_list_performance && profiler_part.Reset(); - - const rows_metadb = plman.GetPlaylistItems(this.cur_playlist_idx); - this.cnt.rows = this.initialize_rows(plman.GetPlaylistItems(this.cur_playlist_idx)); - - trace_initialize_list_performance && console.log(`Rows initialized in ${profiler_part.Time}ms`); - - this.playing_item = undefined; - const playing_item_location = plman.GetPlayingItemLocation(); - if (playing_item_location.IsValid && playing_item_location.PlaylistIndex === this.cur_playlist_idx) { - this.playing_item = this.cnt.rows[playing_item_location.PlaylistItemIndex]; - this.playing_item.is_playing = true; - } - - this.focused_item = undefined; - const focus_item_idx = plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx); - if (focus_item_idx !== -1) { - this.focused_item = this.cnt.rows[focus_item_idx]; - this.focused_item.is_focused = true; - } - - // * Initialize headers - - trace_initialize_list_performance && profiler_part.Reset(); - - Header.grouping_handler.set_active_playlist(plman.GetPlaylistName(this.cur_playlist_idx)); - this.cnt.sub_items = this.create_headers(this.cnt.rows, rows_metadb); - getHeaderArtwork(this.cnt.sub_items.slice(0, 10)); // Preload first 10 artworks - - trace_initialize_list_performance && console.log(`Headers initialized in ${profiler_part.Time}ms`); - - if (!this.was_on_size_called) { - // * First time init - this.collapse_handler = new CollapseHandler(/** @type {PlaylistContent} */ this.cnt); - - if (g_properties.show_header) { - if (g_properties.collapse_on_start) { - this.collapse_handler.collapse_all(); - } - - this.collapse_handler.set_callback(() => { - this.on_list_items_change(); - }); - } - } - else { - // * Update list control - if (this.collapse_handler) { - this.collapse_handler.on_content_change(); - } - - if (g_properties.show_header && g_properties.auto_collapse) { - this.collapse_header(); - } - - this.scrollbar.stopScrolling(); - this.update_scrollbar(); - - this.on_list_items_change(); - } - - // * Initialize other objects - if (g_properties.show_queue_position) { - this.queue_handler = new QueueHandler(this.cnt.rows, this.cur_playlist_idx); - } - this.selection_handler = new SelectionHandler(/** @type {PlaylistContent} */ this.cnt, this.cur_playlist_idx); - - this.set_now_playing_hyperlink(); - - (true || trace_initialize_list_performance) && console.log(`Playlist initialized in ${profiler.Time}ms`); - } - - /** - * Initializes and updates the scrollbar if it is visible. - * Used to update scrollbar colors. - */ - initScrollbar() { - if (this.is_scrollbar_visible) { - this.initialize_scrollbar(); - this.update_scrollbar(); - } - } - - /** - * Reinitializes and updates the content of the playlist. - */ - reinitialize() { - if (this.cur_playlist_idx !== plman.ActivePlaylist) { - g_properties.scroll_pos = this.scroll_pos_list[plman.ActivePlaylist] == null ? 0 : this.scroll_pos_list[plman.ActivePlaylist]; - } - this.row_h = SCALE(g_properties.row_h); - this.header_h_in_rows = this.calcHeaderRows(); - this.initialize_list(); - this.scroll_to_focused(); - } - - /** - * Handles when playlist content has changed, i.e playlist items were added, reordered or removed. - * Sets the rows boundary status and fetches album art for playlist headers. - * @protected - * @override - */ - on_content_to_draw_change() { - this.set_rows_boundary_status(); - // @ts-ignore - List.prototype.on_content_to_draw_change.apply(this); - if (g_properties.show_album_art && !g_properties.use_compact_header) { - getAlbumArt(this.items_to_draw); - } - } - - /** - * Updates the scroll position of a playlist and redraws the scrollbar. - * @protected - * @override - */ - scrollbar_redraw_callback() { - this.scroll_pos_list[this.cur_playlist_idx] = Math.round(this.scrollbar.scroll * 1e2) / 1e2; - // @ts-ignore - List.prototype.scrollbar_redraw_callback.apply(this); - } - - /** - * Iterates through all playlist rows and updates the title color of each item. - * Used when updating title color in Reborn/Random theme. - */ - title_color_change() { - const rows = this.cnt.rows.length; - for (let i = 0; i < rows; i++) { - const item = this.cnt.rows[i]; - item.update_title_color(); - } - } - - // private: - - /** - * Initializes an array of rows based on the given playlist, where each row represents a playlist item. - * @param {FbMetadbHandleList} playlist_items An object or array that can be converted to an array using the `Convert()` method. - * @returns {Array} An array of row objects. - */ - initialize_rows(playlist_items) { - const playlist_items_array = playlist_items.Convert(); - - const rows = []; - for (let i = 0, playlist_size = playlist_items_array.length; i < playlist_size; ++i) { - rows[i] = new Row(this.list_x, 0, this.list_w, this.row_h, playlist_items_array[i], i, this.cur_playlist_idx); - if (!g_properties.show_header) { - rows[i].is_odd = !(i & 1); - } - } - - return rows; - } - - /** - * Creates headers for the playlist based on the provided rows and metadata. - * @param {Array} rows The rows of the playlist - * @param {FbMetadbHandleList} rows_metadb The metadb of the rows. - * @returns {Array
} The result of calling the `Header.create_headers` function with the provided arguments. - */ - create_headers(rows, rows_metadb) { - const prepared_rows = Header.prepare_initialization_data(rows, rows_metadb); - return Header.create_headers(/** @type {PlaylistContent} */ this.cnt, this.list_x, 0, this.list_w, this.row_h * this.header_h_in_rows, prepared_rows); - } - - /** - * Sets the status of the last row based on whether the scrollbar is available and scrolled down. - */ - set_rows_boundary_status() { - const last_row = Last(this.cnt.rows); - if (last_row) { - last_row.is_cropped = this.is_scrollbar_available ? this.scrollbar.is_scrolled_down : false; - } - } - - // #region Context Menu - - /** - * Appends the playlist tools menu to the parent menu. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_pltools_menu_to(parent_menu) { - const pltools = new ContextMenu('Playlist tools'); - const playlist_count = plman.PlaylistCount; - pltools.append_item('Playlist manager \tCtrl+M', () => { - fb.RunMainMenuCommand('View/Playlist Manager'); - }); - pltools.append_item('Playlist search \tCtrl+F', () => { - fb.RunMainMenuCommand('View/Playlist search'); - }); - pltools.append_separator(); - - pltools.append_item('Create new playlist \tCtrl+N', () => { - plman.CreatePlaylist(playlist_count, ''); - plman.ActivePlaylist = playlist_count; - }); - - const autopl = new ContextMenu('Create new auto playlist'); - pltools.append(autopl); - autopl.append_item('Custom auto playlist', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) New auto playlist', '', '', 0); - plman.ActivePlaylist = playlist_count; - plman.ShowAutoPlaylistUI(playlist_count); - }); - autopl.append_separator(); - autopl.append_item('Tracks from the library', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks from the library', 'ALL', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_separator(); - autopl.append_item('Tracks most played', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks most played', '%play_count% GREATER 9', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks never played', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks never played', '%play_count% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks played in the last week', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks played in the last week', '%last_played% DURING LAST 1 WEEK', '%last_played%', 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks played in the last month', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks played in the last month', '%last_played% DURING LAST 4 WEEKS', '%last_played%', 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks played in the last year', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks played in the last year', '%last_played% DURING LAST 52 WEEKS', '%last_played%', 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_separator(); - autopl.append_item('Tracks unrated', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks unrated', '%rating% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks rated 1', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 1', '%rating% IS 1', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks rated 2', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 2', '%rating% IS 2', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks rated 3', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 3', '%rating% IS 3', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks rated 4', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 4', '%rating% IS 4', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_item('Tracks rated 5', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Tracks rated 5', '%rating% IS 5', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - autopl.append_separator(); - autopl.append_item('Loved tracks', () => { - plman.CreateAutoPlaylist(playlist_count, '(Auto) Loved tracks', '%mood% GREATER 0', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); - plman.ActivePlaylist = playlist_count; - }); - pltools.append_separator(); - - pltools.append_item('Save playlist \tCtrl+S', () => { - fb.RunMainMenuCommand('File/Save playlist...'); - }); - pltools.append_item('Load playlist', () => { - fb.RunMainMenuCommand('File/Load playlist...'); - }); - const isAutoPl = !plman.PlaylistCount ? '' : plman.IsAutoPlaylist(this.cur_playlist_idx); - const isLocked = !plman.PlaylistCount ? '' : plman.IsPlaylistLocked(this.cur_playlist_idx); - pltools.append_item(isLocked ? isAutoPl ? 'Unlock playlist (N/A for auto playlists)' : 'Unlock playlist' : 'Lock playlist', () => { - if (isLocked && !isAutoPl) { - plman.SetPlaylistLockedActions(this.cur_playlist_idx, null); - } else if (!isAutoPl) { - plman.SetPlaylistLockedActions(this.cur_playlist_idx, ['ExecuteDefaultAction']); - } - }, { is_grayed_out: isAutoPl }); - parent_menu.append(pltools); - } - - /** - * Appends the playlist edit menu to the parent menu based on the presence of selected items and data in the clipboard. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_edit_menu_to(parent_menu) { - const has_selected_item = this.selection_handler.has_selected_items(); - const is_playlist_locked = !plman.PlaylistCount ? '' : plman.IsPlaylistLocked(this.cur_playlist_idx); - // Check only for data presence, since parsing it's contents might take a while - const has_data_in_clipboard = fb.CheckClipboardContents(); - if (has_selected_item || has_data_in_clipboard) { - if (!parent_menu.is_empty()) { - parent_menu.append_separator(); - } - - if (has_selected_item) { - parent_menu.append_item('Cut', () => { - this.selection_handler.cut(); - }, { is_grayed_out: !has_selected_item }); - - parent_menu.append_item('Copy', () => { - this.selection_handler.copy(); - }, { is_grayed_out: !has_selected_item }); - } - - if (has_data_in_clipboard) { - parent_menu.append_item('Paste', () => { - this.selection_handler.paste(); - }, { is_grayed_out: !has_data_in_clipboard || is_playlist_locked }); - } - } - - if (has_selected_item) { - if (!parent_menu.is_empty()) { - parent_menu.append_separator(); - } - - parent_menu.append_item('Remove', () => { - plman.RemovePlaylistSelection(this.cur_playlist_idx); - }, { is_grayed_out: is_playlist_locked }); - } - } - - /** - * Appends the playlist collapse menu to the parent menu. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_collapse_menu_to(parent_menu) { - const ce = new ContextMenu('Collapse/Expand'); - parent_menu.append(ce); - - ce.append_item('Collapse all', () => { - if (g_properties.show_header) { - this.collapse_handler.collapse_all(); - if (this.collapse_handler.changed) { - this.scroll_to_focused_or_now_playing(); - } - } - }); - - if (plman.ActivePlaylist === plman.PlayingPlaylist && g_properties.show_header) { - ce.append_item('Collapse all but now playing', () => { - this.collapse_handler.collapse_all_but_now_playing(); - if (this.collapse_handler.changed) { - this.scroll_to_now_playing_or_focused(); - } - }); - } - - ce.append_item('Expand all', () => { - if (g_properties.show_header) { - this.collapse_handler.expand_all(); - if (this.collapse_handler.changed) { - this.scroll_to_focused_or_now_playing(); - } - } - }); - - ce.append_separator(); - - ce.append_item('Auto', () => { - g_properties.auto_collapse = !g_properties.auto_collapse; - if (g_properties.show_header && g_properties.auto_collapse) { - this.collapse_handler.collapse_all_but_now_playing(); - if (this.collapse_handler.changed) { - this.scroll_to_now_playing_or_focused(); - } - } else { - this.collapse_handler.expand_all(); - } - }, { is_checked: g_properties.auto_collapse }); - - ce.append_item('Collapse on start', () => { - g_properties.collapse_on_start = !g_properties.collapse_on_start; - }, { is_checked: g_properties.collapse_on_start }); - - ce.append_item('Collapse on playlist switch', () => { - g_properties.collapse_on_playlist_switch = !g_properties.collapse_on_playlist_switch; - }, { is_checked: g_properties.collapse_on_playlist_switch }); - } - - /** - * Appends the playlist appearance menu to the parent menu allowing to customize the appearance of the playlist. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_appearance_menu_to(parent_menu) { - const appear = new ContextMenu('Appearance'); - parent_menu.append(appear); - - PlaylistManager.append_playlist_info_visibility_context_menu_to(appear); - - this.append_scrollbar_visibility_context_menu_to(appear); - - appear.append_item('Show artist name on difference', () => { - pref.showDifferentArtist = !pref.showDifferentArtist; - - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: pref.showDifferentArtist }); - - appear.append_item('Show artist name in all rows', () => { - pref.showArtistPlaylistRows = !pref.showArtistPlaylistRows; - - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: pref.showArtistPlaylistRows }); - - appear.append_item('Show album title in all rows', () => { - pref.showAlbumPlaylistRows = !pref.showAlbumPlaylistRows; - - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: pref.showAlbumPlaylistRows }); - - appear.append_item('Show group header', () => { - g_properties.show_header = !g_properties.show_header; - if (g_properties.show_header) { - this.collapse_handler = new CollapseHandler(/** @type {PlaylistContent} */ this.cnt); - this.collapse_handler.expand_all(); - this.collapse_handler.set_callback(() => { - this.on_list_items_change(); - }); - } - else { - this.collapse_handler.expand_all(); - // this.collapse_handler = null; - } - - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.show_header }); - - if (g_properties.show_header) { - const appear_header = new ContextMenu('Headers'); - appear.append(appear_header); - - appear_header.append_item('Use compact group header', () => { - g_properties.use_compact_header = !g_properties.use_compact_header; - this.header_h_in_rows = this.calcHeaderRows(); - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.use_compact_header }); - - appear_header.append_item('Show disc sub-header', () => { - g_properties.show_disc_header = !g_properties.show_disc_header; - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.show_disc_header }); - - appear_header.append_item('Show rating', () => { - g_properties.show_rating_header = !g_properties.show_rating_header; - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.show_rating_header }); - - appear_header.append_item('Show PLR value', () => { - g_properties.show_PLR_header = !g_properties.show_PLR_header; - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.show_PLR_header }); - - if (!g_properties.use_compact_header) { - appear_header.append_item('Show group info', () => { - g_properties.show_group_info = !g_properties.show_group_info; - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.show_group_info }); - - const art = new ContextMenu('Album art'); - appear_header.append(art); - - art.append_item('Show', () => { - g_properties.show_album_art = !g_properties.show_album_art; - if (g_properties.show_album_art) { - getAlbumArt(this.items_to_draw); - } - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, { is_checked: g_properties.show_album_art }); - - art.append_item('Auto-hide when no cover', () => { - g_properties.auto_album_art = !g_properties.auto_album_art; - if (g_properties.show_album_art) { - getAlbumArt(this.items_to_draw); - } - this.initialize_list(); - this.scroll_to_focused_or_now_playing(); - }, - { - is_checked: g_properties.auto_album_art, - is_grayed_out: !g_properties.show_album_art - } - ); - } - } - - const appear_row = new ContextMenu('Rows'); - appear.append(appear_row); - - appear_row.append_item('Alternate row color', () => { - g_properties.show_row_stripes = !g_properties.show_row_stripes; - }, { is_checked: g_properties.show_row_stripes }); - - appear_row.append_item('Show play count', () => { - g_properties.show_playcount = !g_properties.show_playcount; - }, { is_checked: g_properties.show_playcount }); - - appear_row.append_item('Show queue position', () => { - g_properties.show_queue_position = !g_properties.show_queue_position; - this.queue_handler = g_properties.show_queue_position ? new QueueHandler(this.cnt.rows, this.cur_playlist_idx) : undefined; - }, { is_checked: g_properties.show_queue_position }); - - appear_row.append_item('Show rating', () => { - g_properties.show_rating = !g_properties.show_rating; - }, { is_checked: g_properties.show_rating }); - - appear_row.append_item('Show PLR value', () => { - g_properties.show_PLR = !g_properties.show_PLR; - }, { is_checked: g_properties.show_PLR }); - } - - /** - * Appends the playlist sort menu to a parent menu allowing to sort items in the playlist. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_sort_menu_to(parent_menu) { - const has_multiple_selected_items = this.selection_handler.selected_items_count() > 1; - const is_auto_playlist = plman.IsAutoPlaylist(this.cur_playlist_idx); - - const sort = new ContextMenu(has_multiple_selected_items ? 'Sort selection' : 'Sort', - { is_grayed_out: is_auto_playlist } - ); - parent_menu.append(sort); - - sort.append_item('Always auto-sort', () => { - pref.playlistSortOrderAuto = !pref.playlistSortOrderAuto; - }, { is_checked: pref.playlistSortOrderAuto }); - sort.append_separator(); - - sort.append_item('Sort by...', () => { - if (has_multiple_selected_items) { - fb.RunMainMenuCommand('Edit/Selection/Sort/Sort by...'); - } - else { - fb.RunMainMenuCommand('Edit/Sort/Sort by...'); - } - }); - - sort.append_separator(); - - const sortOrder = [ - ['Default', 'default'], - ['Artist | date', 'artistDate'], - ['Album', 'albumTitle'], - ['Album rating', 'albumRating'], - ['Album playcount', 'albumPlaycount'], - ['Track', 'trackTitle'], - ['Track number', 'trackNumber'], - ['Track rating', 'trackRating'], - ['Track playcount', 'trackPlaycount'], - ['Year', 'year'], - ['Genre', 'genre'], - ['Label', 'label'], - ['Country', 'country'], - ['File path', 'filePath'], - ['Custom', 'custom'] - ]; - - const sortOrderDirection = [ - ['Order by ascending', '_asc'], - ['Order by descending', '_dsc'] - ]; - - const setSorting = () => { - setPlaylistSortOrder(); - playlist.on_size(ww, wh); - RepaintWindow(); - }; - - sortOrderDirection.forEach((direction) => { - const savedOrder = pref.playlistSortOrder.slice(0, -4); - const sortOrderWithDirection = ['artistDate', 'albumRating', 'albumPlaycount', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country'].includes(savedOrder); - sort.append_item(direction[0], () => { - pref.playlistSortOrderDirection = direction[1]; - pref.playlistSortOrder = sortOrderWithDirection ? `${savedOrder}${pref.playlistSortOrderDirection}` : savedOrder; - setSorting(); - }, { - is_radio_checked: pref.playlistSortOrderDirection === direction[1], - is_grayed_out: !sortOrderWithDirection - }); - }); - - sort.append_separator(); - - sortOrder.forEach((item) => { - const sortOrderWithDirection = ['artistDate', 'albumRating', 'albumPlaycount', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country'].includes(item[1]); - sort.append_item(item[0], function (order) { - const savedDirection = pref.playlistSortOrderDirection; - pref.playlistSortOrder = sortOrderWithDirection ? `${order}${savedDirection}` : order; - if (pref.playlistSortOrder === 'custom') inputBox('playlistSortCustom'); - setSorting(); - }.bind(null, item[1]), { - is_radio_checked: pref.playlistSortOrderAuto && - (sortOrderWithDirection && item[1] === pref.playlistSortOrder.slice(0, -4) || - !sortOrderWithDirection && item[1] === pref.playlistSortOrder) - }); - }); - - sort.append_separator(); - - sort.append_item('Randomize', () => { - if (has_multiple_selected_items) { - fb.RunMainMenuCommand('Edit/Selection/Sort/Randomize'); - } - else { - fb.RunMainMenuCommand('Edit/Sort/Randomize'); - } - }); - - sort.append_item('Reverse', () => { - if (has_multiple_selected_items) { - fb.RunMainMenuCommand('Edit/Selection/Sort/Reverse'); - } - else { - fb.RunMainMenuCommand('Edit/Sort/Reverse'); - } - }); - - sort.append_separator(); - - sort.append_item('Save', () => { - fb.RunMainMenuCommand('File/Save playlist...'); - }); - - sort.append_item('Load', () => { - fb.RunMainMenuCommand('File/Load playlist...'); - }); - - sort.append_item('Undo', () => { - fb.RunMainMenuCommand('Edit/Undo'); - }); - } - - /** - * Appends the playlist weblinks menu to the parent menu, allowing the user to open various websites. - * @param {ContextMenu} parent_menu The parent menu to append to. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ - append_weblinks_menu_to(parent_menu, metadb) { - const web = new ContextMenu('Weblinks'); - parent_menu.append(web); - - const web_links = [ - ['Google', 'google'], - ['Google Images', 'googleImages'], - ['Wikipedia', 'wikipedia'], - ['YouTube', 'youTube'], - ['Last FM', 'lastFM'], - ['Discogs', 'discogs'] - ]; - - web_links.forEach((item) => { - web.append_item(item[0], function (url) { - qwr_utils.link(url, metadb); - }.bind(null, item[1])); - }); - } - - /** - * Appends the "Send selection" menu to the parent menu, allowing to perform various actions on selected items in a playlist. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_send_items_menu_to(parent_menu) { - const playlist_count = plman.PlaylistCount; - - const send = new ContextMenu('Send selection'); - parent_menu.append(send); - - send.append_item('To top', () => { - plman.UndoBackup(this.cur_playlist_idx); - plman.MovePlaylistSelection(this.cur_playlist_idx, -plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx)); - }); - - send.append_item('To bottom', () => { - plman.UndoBackup(this.cur_playlist_idx); - plman.MovePlaylistSelection(this.cur_playlist_idx, plman.PlaylistItemCount(this.cur_playlist_idx) - plman.GetPlaylistSelectedItems(this.cur_playlist_idx).Count); - }); - - send.append_separator(); - - send.append_item('Create New Playlist \tCtrl+N', () => { - plman.CreatePlaylist(playlist_count, ''); - plman.InsertPlaylistItems(playlist_count, 0, plman.GetPlaylistSelectedItems(this.cur_playlist_idx), true); - }); - - send.append_separator(); - - for (let i = 0; i < playlist_count; ++i) { - let playlist_text = `${plman.GetPlaylistName(i)} [${plman.PlaylistItemCount(i)}]`; - - const is_item_autoplaylist = plman.IsAutoPlaylist(i); - if (is_item_autoplaylist) { - playlist_text += ' (Auto)'; - } - - if (i === plman.PlayingPlaylist) { - playlist_text += ' (Now Playing)'; - } - - send.append_item(playlist_text, ((playlist_idx) => { - this.selection_handler.send_to_playlist(playlist_idx); - }).bind(undefined, i), { is_grayed_out: is_item_autoplaylist || i === this.cur_playlist_idx }); - } - } - - /** - * Appends the playlist "write playlist stats list" menu to the parent menu. - * @param {ContextMenu} parent_menu The parent menu to append to. - */ - append_write_playlist_stats_list_menu_to(parent_menu) { - // * Set up metadata and playlist stats settings - const metadata = Array.from(this.meta_handler.get_metadata().values()); - const playlistName = plman.GetPlaylistName(plman.ActivePlaylist); - const playlistStatsMenu = new ContextMenu('Write playlist statistics to list'); - const sortBy = g_properties.playlist_stats_sort_by; - const sortDirection = g_properties.playlist_stats_sort_direction; - - // * Set up playlist stats option menu items - const sortByMenu = [ - ['Sort by artist', 'artist'], - ['Sort by album', 'albumTitle'], - ['Sort by track', 'trackTitle'], - ['Sort by year', 'year'], - ['Sort by genre', 'genre'], - ['Sort by label', 'label'], - ['Sort by country', 'country'], - ['Sort by stats', ''] - ]; - - const sortDirectionMenu = [ - ['Order by ascending', '_asc'], - ['Order by descending', '_dsc'] - ]; - - const statsTypeMenu = [ - ['Album rating', 'albumRating'], - ['Album playcount', 'albumPlaycount'], - ['Album playcount total', 'albumPlaycountTotal'], - ['Album track rating', 'albumTrackRating'], - ['Album track playcount', 'albumTrackPlaycount'], - ['Track rating', 'trackRating'], - ['Track playcount', 'trackPlaycount'], - ['Top rated', 'topRated'], - ['Top played', 'topPlayed'] - ]; - - // * Create playlist stats settings menu - playlistStatsMenu.append_item('Include artist', () => { - g_properties.playlist_stats_include_artist = !g_properties.playlist_stats_include_artist; - }, { is_checked: g_properties.playlist_stats_include_artist }); - playlistStatsMenu.append_item('Include album', () => { - g_properties.playlist_stats_include_album = !g_properties.playlist_stats_include_album; - }, { is_checked: g_properties.playlist_stats_include_album }); - playlistStatsMenu.append_item('Include track', () => { - g_properties.playlist_stats_include_track = !g_properties.playlist_stats_include_track; - }, { is_checked: g_properties.playlist_stats_include_track }); - playlistStatsMenu.append_item('Include year', () => { - g_properties.playlist_stats_include_year = !g_properties.playlist_stats_include_year; - }, { is_checked: g_properties.playlist_stats_include_year }); - playlistStatsMenu.append_item('Include genre', () => { - g_properties.playlist_stats_include_genre = !g_properties.playlist_stats_include_genre; - }, { is_checked: g_properties.playlist_stats_include_genre }); - playlistStatsMenu.append_item('Include label', () => { - g_properties.playlist_stats_include_label = !g_properties.playlist_stats_include_label; - }, { is_checked: g_properties.playlist_stats_include_label }); - playlistStatsMenu.append_item('Include country', () => { - g_properties.playlist_stats_include_country = !g_properties.playlist_stats_include_country; - }, { is_checked: g_properties.playlist_stats_include_country }); - playlistStatsMenu.append_item('Include stats', () => { - g_properties.playlist_stats_include_stats = !g_properties.playlist_stats_include_stats; - }, { is_checked: g_properties.playlist_stats_include_stats }); - - playlistStatsMenu.append_separator(); - - sortByMenu.forEach((sortBy) => { - playlistStatsMenu.append_item(sortBy[0], () => { - g_properties.playlist_stats_sort_by = sortBy[1]; - }, { is_radio_checked: g_properties.playlist_stats_sort_by === sortBy[1] }); - }); - - playlistStatsMenu.append_separator(); - - sortDirectionMenu.forEach((direction) => { - playlistStatsMenu.append_item(direction[0], () => { - g_properties.playlist_stats_sort_direction = direction[1]; - }, { is_radio_checked: g_properties.playlist_stats_sort_direction === direction[1] }); - }); - - playlistStatsMenu.append_separator(); - - playlistStatsMenu.append_item('Reset settings', () => { - g_properties.playlist_stats_include_artist = true; - g_properties.playlist_stats_include_album = true; - g_properties.playlist_stats_include_track = true; - g_properties.playlist_stats_include_year = false; - g_properties.playlist_stats_include_genre = false; - g_properties.playlist_stats_include_label = false; - g_properties.playlist_stats_include_country = false; - g_properties.playlist_stats_include_stats = true; - g_properties.playlist_stats_sort_direction = '_dsc'; - g_properties.playlist_stats_sort_by = ''; - }); - - playlistStatsMenu.append_separator(); - - // * Create playlist stats list menu - statsTypeMenu.forEach((item) => { - const statsTypeAlbums = item[1].startsWith('album'); - const statsTypeTracks = statsTypeAlbums && g_properties.playlist_stats_sort_by.startsWith('track'); - - playlistStatsMenu.append_item(item[0], function (statsType) { - const newStatsType = sortBy ? `${sortBy}${sortDirection}_${statsType}` : `${statsType}${sortDirection}`; - const metadataType = - item[1].startsWith('track') ? 'track' : - item[1].startsWith('album') ? 'album' : - item[1].endsWith('topRated') ? 'topRated' : - item[1].endsWith('topPlayed') ? 'topPlayed' : - 'album'; - - const statsName = item[0]; - const filePath = `${fb.ProfilePath}cache\\playlist\\${playlistName}_${statsName}${statsType.startsWith('top') ? '_statistics' : `_[${newStatsType}]`}.txt`; - - const ratingType = - statsType === 'albumRating' ? 'albumAverage' : - statsType === 'albumRatingTotal' ? 'albumTotal' : - statsType === 'albumTrackRating' ? 'albumTracks' : - false; - const playcountType = - statsType === 'albumPlaycount' ? 'albumAverage' : - statsType === 'albumPlaycountTotal' ? 'albumTotal' : - statsType === 'albumTrackPlaycount' ? 'albumTracks' : - false; - - this.meta_handler.write_stats_to_text_file(metadata, metadataType, filePath, statsName, newStatsType, ratingType, playcountType); - fb.ShowPopupMessage(`${statsName} list was saved in:\n\n${filePath}`, `${statsName} list`); - }.bind(this, item[1]), { is_grayed_out: statsTypeTracks }); - }); - - parent_menu.append(playlistStatsMenu); - } - // #endregion - - /** - * Determines the row and position (above or below) where an item should be dropped based on the mouse coordinates. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {{row: ?Row, is_above: ?boolean}} - */ - get_drop_row_info(x, y) { - const drop_info = { - row: undefined, - is_above: undefined - }; - - let item = this.get_item_under_mouse(x, y); - if (!item) { - if (!this.trace_list(x, y) || !this.cnt.rows.length) { - return drop_info; - } - - item = Last(this.cnt.rows); - } - - const is_above = y < (item.y + item.h / 2); - - if (item instanceof BaseHeader) { - const first_row_in_header = item.get_first_row(); - - if (is_above) { - if (first_row_in_header === this.cnt.rows[0]) { - drop_info.row = first_row_in_header; - drop_info.is_above = true; - } - else { - drop_info.row = this.cnt.rows[first_row_in_header.idx - 1]; - drop_info.is_above = false; - } - } - else { - drop_info.row = first_row_in_header; - drop_info.is_above = true; - } - } - else if (item instanceof Row) { - if (is_above) { - drop_info.row = item; - drop_info.is_above = true; - } - else if (g_properties.show_header && item.idx === Last(item.parent.sub_items).idx - || item === Last(this.cnt.rows)) { - drop_info.row = item; - drop_info.is_above = false; - } - else { - drop_info.row = this.cnt.rows[item.idx + 1]; - drop_info.is_above = true; - } - } - - return drop_info; - } - - // #region 'Scroll to' Methods - - /** - * Sets a hyperlink for the currently playing item if the current playlist index is "Search". - */ - set_now_playing_hyperlink() { - if (!fb.GetNowPlaying() || plman.GetPlaylistName(this.cur_playlist_idx) !== 'Search') { - return; - } - - const plIndex = plman.FindOrCreatePlaylist('Search', true); - const handles = plman.GetPlaylistItems(plIndex); - const index = handles.Find(fb.GetNowPlaying()); - - setTimeout(() => { // Wait for loadingThemeComplete - this.playing_item = this.cnt.rows[index]; - if (this.playing_item) { - this.playing_item.is_playing = true; - this.playing_item.clear_title_text(); - } - window.RepaintRect(this.x, this.y, this.w, this.h); - }, loadingThemeComplete); - } - - /** - * Scrolls the playlist to the current now playing track after some checks. - */ - show_now_playing() { - const playing_item_location = plman.GetPlayingItemLocation(); - if (!playing_item_location.IsValid) { - return; - } - - if (playing_item_location.PlaylistIndex !== this.cur_playlist_idx) { - plman.ActivePlaylist = playing_item_location.PlaylistIndex; - this.initialize_list(); - } - else if (this.playing_item && this.collapse_handler) { - this.collapse_handler.expand(this.playing_item.parent); - } - - this.selection_handler.update_selection(this.cnt.rows[playing_item_location.PlaylistItemIndex]); - - this.scroll_to_now_playing(); - } - - /** - * Scrolls the playlist to the current now playing track or focused item. - */ - scroll_to_now_playing_or_focused() { - if (this.playing_item) { - this.scroll_to_row(null, this.playing_item); - } - else if (this.focused_item) { - this.scroll_to_row(null, this.focused_item); - } - } - - /** - * Scrolls the playlist to the focused item or current now playing track. - */ - scroll_to_focused_or_now_playing() { - if (this.focused_item) { - this.scroll_to_row(null, this.focused_item); - } - else if (fb.CursorFollowPlayback && this.playing_item) { - this.scroll_to_row(null, this.playing_item); - } - } - - /** - * Scrolls the playlist to the focused item. - */ - scroll_to_focused() { - if (this.focused_item && !displayLibrarySplit()) { - this.scroll_to_row(null, this.focused_item); - } - } - - /** - * Scrolls the playlist to the current now playing track. - */ - scroll_to_now_playing() { - if (this.playing_item) { - this.scroll_to_row(null, this.playing_item); - } - } - - /** - * Scrolls the playlist to a specific row, taking into account the visibility state of the row. - * @param {?Row} from_row The starting row from which to scroll. - * @param {Row} to_row The row index to which to scroll. - */ - scroll_to_row(from_row, to_row) { - if (!this.is_scrollbar_available) { - return; - } - - const has_headers = g_properties.show_header; - - const visible_to_item = this.cnt_helper.is_item_visible(to_row) ? to_row : this.cnt_helper.get_visible_parent(to_row); - const to_item_state = this.get_item_visibility_state(visible_to_item); - - let shifted_successfully = false; - switch (to_item_state.visibility) { - case visibility_state.none: { - if (!from_row) { - break; - } - - const visible_from_item = this.cnt_helper.is_item_visible(from_row) ? from_row : this.cnt_helper.get_visible_parent(from_row); - - const from_item_state = this.get_item_visibility_state(visible_from_item); - if (from_item_state.visibility === visibility_state.none) { - break; - } - - const direction = (to_row.idx - from_row.idx) > 0 ? 1 : -1; - let scroll_shift = 0; - let neighbour_item = visible_from_item; - do { - const neighbour_item_state = this.get_item_visibility_state(neighbour_item); - scroll_shift += neighbour_item_state.invisible_part; - neighbour_item = direction > 0 ? this.cnt_helper.get_next_visible_item(neighbour_item) : this.cnt_helper.get_prev_visible_item(neighbour_item); - } while (neighbour_item && !this.cnt_helper.is_item_navigateable(neighbour_item)); - - Assert(neighbour_item != null, LogicError, 'Failed to get navigateable neighbour'); - - if (visible_to_item !== neighbour_item) { - // I.e. to_item and from_item are not neighbours - break; - } - - scroll_shift += visible_to_item.h / this.row_h; - - this.scrollbar.smooth_scroll_to(g_properties.scroll_pos + direction * scroll_shift); - shifted_successfully = true; - break; - } - case visibility_state.partial_top: { - if (to_item_state.invisible_part % 1 > 0) { - this.scrollbar.shift_line(-1); - } - this.scrollbar.smooth_scroll_to(g_properties.scroll_pos - Math.floor(to_item_state.invisible_part)); - shifted_successfully = true; - break; - } - case visibility_state.partial_bottom: { - if (to_item_state.invisible_part % 1 > 0) { - this.scrollbar.shift_line(1); - } - this.scrollbar.smooth_scroll_to(g_properties.scroll_pos + Math.floor(to_item_state.invisible_part)); - shifted_successfully = true; - break; - } - case visibility_state.full: { - shifted_successfully = true; - break; - } - default: { - throw new ArgumentError('visibility_state', to_item_state.visibility); - } - } - - if (shifted_successfully) { - if (has_headers) { - let scroll_shift = 0; - let top_item = visible_to_item; - while (top_item.parent && top_item.parent instanceof BaseHeader && top_item === top_item.parent.sub_items[0]) { - top_item = top_item.parent; - const header_state = this.get_item_visibility_state(top_item); - scroll_shift += header_state.invisible_part; - } - this.scrollbar.smooth_scroll_to(g_properties.scroll_pos - scroll_shift); - } - } - else { - const item_draw_idx = this.get_item_draw_row_idx(visible_to_item); - const new_scroll_pos = Math.max(0, item_draw_idx - Math.floor(this.rows_to_draw_precise / 2)); - if (!playlistHistoryUsed) { - this.scrollbar.smooth_scroll_to(new_scroll_pos); - } - } - } - - /** - * Determines the visibility state and the amount of invisibility of a given item in the playlist. - * @param {Row|BaseHeader} item_to_check - * @returns {{visibility: visibility_state, invisible_part: number}} - */ - get_item_visibility_state(item_to_check) { - const item_state = { - visibility: visibility_state.none, - invisible_part: item_to_check.h / this.row_h - }; - - this.items_to_draw.every((item) => { - if (item === item_to_check) { - if (item.y < this.list_y && item.y + item.h > this.list_y) { - item_state.visibility = visibility_state.partial_top; - item_state.invisible_part = (this.list_y - item.y) / this.row_h; - } - else if (item.y < this.list_y + this.list_h && item.y + item.h > this.list_y + this.list_h) { - item_state.visibility = visibility_state.partial_bottom; - item_state.invisible_part = ((item.y + item.h) - (this.list_y + this.list_h)) / this.row_h; - } - else { - item_state.visibility = visibility_state.full; - item_state.invisible_part = 0; - } - return false; // Aborts every - } - return true; - }); - - return item_state; - } - - /** - * Determines the row index of a target item in a drawn item list. - * Note: at worst has O (playlist_size) complexity. - * @param {Row|BaseHeader} target_item The item that you want to find the row index for. It can be either a `Row` object or a `BaseHeader` object. - * @returns {number} The row index of the target item in the drawn item list. - */ - get_item_draw_row_idx(target_item) { - let cur_row = 0; - const is_target_row = target_item instanceof Row; - - const iterate_level = (sub_items, target_item) => { - if (sub_items[0] instanceof BaseHeader) { - const header_h_in_rows = Math.round(sub_items[0].h / this.row_h); - - for (let i = 0; i < sub_items.length; ++i) { - const header = sub_items[i]; - if (header === target_item) { - return true; - } - - cur_row += header_h_in_rows; - - if (header.is_collapsed) { - continue; - } - - if (iterate_level(header.sub_items, target_item)) { - return true; - } - } - } - else if (is_target_row) { // Row - for (let j = 0; j < sub_items.length; ++j) { - if (target_item === sub_items[j]) { - return true; - } - - ++cur_row; - } - } - else { - cur_row += sub_items.length; - } - - return false; - }; - - if (!iterate_level(g_properties.show_header ? this.cnt.sub_items : this.cnt.rows, target_item)) { - return 0; // throw new LogicError('Could not find item in drawn item list'); - } - - return cur_row; - } - - // #endregion - - /** - * Starts the drag scroll action when reordering playlist items. - * @param {string} key The string value of 'up' or 'down'. - */ - start_drag_scroll(key) { - if (this.drag_scroll_timeout_timer) { - return; - } - - this.drag_scroll_timeout_timer = setTimeout(() => { - if (this.drag_scroll_repeat_timer) { - return; - } - - this.drag_scroll_repeat_timer = setInterval(() => { - if (!this.scrollbar.in_sbar && !this.selection_handler.is_dragging() || !this.drag_scroll_timeout_timer) { - return; - } - - this.drag_scroll_in_progress = true; - - let cur_marked_item; - if (key === 'up') { - this.scrollbar.shift_line(-1); - this.scrollbar.start_shift_timer(-1); - - cur_marked_item = this.items_to_draw[0]; - if (cur_marked_item instanceof BaseHeader) { - this.collapse_handler.expand(cur_marked_item); - if (this.collapse_handler.changed) { - this.scrollbar.scroll_to(g_properties.scroll_pos + cur_marked_item.get_sub_items_total_h_in_rows()); - } - - cur_marked_item = cur_marked_item.get_first_row(); - } - - this.selection_handler.drag(cur_marked_item, true); - cur_marked_item.is_drop_boundary_reached = true; - } - else if (key === 'down') { - this.scrollbar.shift_line(1); - this.scrollbar.start_shift_timer(1); - - cur_marked_item = Last(this.items_to_draw); - if (cur_marked_item instanceof BaseHeader) { - this.collapse_handler.expand(cur_marked_item); - if (this.collapse_handler.changed) { - this.repaint(); - } - - cur_marked_item = this.cnt.rows[cur_marked_item.get_first_row().idx - 1]; - } - - this.selection_handler.drag(cur_marked_item, false); - cur_marked_item.is_drop_boundary_reached = true; - } - else { - throw new ArgumentError('drag_scroll_command', key); - } - - if (this.scrollbar.is_scrolled_down || this.scrollbar.is_scrolled_up) { - this.stop_drag_scroll(); - } - }, 50); - }, 350); - } - - /** - * Stops the drag scroll action when dropping reordered playlist items. - */ - stop_drag_scroll() { - if (this.drag_scroll_repeat_timer) { - clearInterval(this.drag_scroll_repeat_timer); - } - if (this.drag_scroll_timeout_timer) { - clearTimeout(this.drag_scroll_timeout_timer); - } - - this.drag_scroll_timeout_timer = undefined; - this.drag_scroll_repeat_timer = undefined; - this.drag_scroll_in_progress = false; - this.scrollbar.stop_shift_timer(); - } - - /** - * Filters a drop effect based on the modifiers (Ctrl, Shift, Alt) pressed during the event. - * @param {number} effect A bitmask that represents the available drop effects for a drag-and-drop action. - * @returns {number} Returns the filtered effect that can be one of the following: - * - Only link effect if Ctrl and Shift are pressed together or Alt is pressed alone. - * - Copy effect if Ctrl is pressed alone. - * - Move effect if Shift is pressed alone. - * - Move effect, then Copy effect, then Link effect if no modifiers are pressed. - */ - filter_effect_by_modifiers(effect) { - const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); - const shift_pressed = utils.IsKeyPressed(VK_SHIFT); - const alt_pressed = utils.IsKeyPressed(VK_MENU); - - if (ctrl_pressed && shift_pressed && !alt_pressed - || alt_pressed && !ctrl_pressed && !shift_pressed) { - // Link only - return (effect & g_drop_effect.link); - } - - if (ctrl_pressed && !shift_pressed && !alt_pressed) { - // Copy (also via link) - return (effect & g_drop_effect.copy) || (effect & g_drop_effect.link); - } - - if (shift_pressed) { - // Move only - return (effect & g_drop_effect.move); - } - - // Move > Copy > Link - return (effect & g_drop_effect.move) || (effect & g_drop_effect.copy) || (effect & g_drop_effect.link); - } - - /** - * Calculates the number of header rows based on the layout and font size setting. - * @returns {number} The number of rows to be displayed in the header section of the playlist. - */ - calcHeaderRows() { - const headerFontSize = pref[`playlistHeaderFontSize_${pref.layout}`]; - const rowFontSize = pref[`playlistFontSize_${pref.layout}`]; - let numRows; - if (g_properties.use_compact_header) { - numRows = g_properties.rows_in_compact_header; - } else { - numRows = g_properties.rows_in_header; - if ((headerFontSize * 2 + 3 + rowFontSize) > (g_properties.rows_in_header * g_properties.row_h * 0.6)) { - numRows++; - } - } - return numRows; - } -} - - -///////////////// -// * CONTENT * // -///////////////// -/** - * Generates and manages items to draw in a playlist. - */ -class PlaylistContent extends ListRowContent { - /** - * @extends {ListRowContent} - * @final - * @class - */ - constructor() { - super(); - - /** - * It is assumed that every sub_items array contains only items of the same single type. - * @type {Array
} - */ - this.sub_items = []; - - this.helper = new ContentNavigationHelper(this); - } - - /** - * Generates items to draw based on the given parameters and returns an array of items. - * @param {number} wy The y-coordinate of the top edge of the visible area. - * @param {number} wh The height of the viewport. - * @param {number} row_shift The number of rows to shift the drawing position vertically. - * @param {number} pixel_shift The number of pixels to shift the items vertically. - * @param {number} row_h The height of each row in the drawing. - * @returns {Array} An array of items to draw. - * @override - */ - generate_items_to_draw(wy, wh, row_shift, pixel_shift, row_h) { - if (!this.rows.length) { - return []; - } - - if (!g_properties.show_header) { - return ListRowContent.prototype.generate_items_to_draw.apply(this, [wy, wh, row_shift, pixel_shift, row_h]); - } - - const first_item = this.generate_first_item_to_draw(wy, wh, row_shift, pixel_shift, row_h); - return this.generate_all_items_to_draw(wy, wh, first_item); - } - - /** - * Updates the size of items based on the given width, taking into account whether the header is shown or not. - * @param {number} w The width. - * @override - */ - update_items_w_size(w) { - if (!g_properties.show_header) { - ListRowContent.prototype.update_items_w_size.apply(this, [w]); - return; - } - - this.sub_items.forEach((item) => { - item.set_w(w); - }); - } - - /** - * Calculates the total height of rows in a playlist, taking into account sub-items and whether the header is shown. - * @override - */ - calculate_total_h_in_rows() { - if (!g_properties.show_header) { - return ListRowContent.prototype.calculate_total_h_in_rows.apply(this); - } - - if (!this.sub_items.length) { - return 0; - } - - const row_h = playlist_geo.row_h; - let total_height_in_rows = Math.round(this.sub_items[0].h / row_h) * this.sub_items.length; - this.sub_items.forEach(item => { - if (!item.is_collapsed) { - total_height_in_rows += item.get_sub_items_total_h_in_rows(); - } - }); - - return total_height_in_rows; - } - - /** - * Calculates the position of the first visible item to draw in the playlist. - * @param {number} wy The y-coordinate of the top edge of the visible area. - * @param {number} wh The height of the viewport. - * @param {number} row_shift The number of rows to shift the drawing position vertically. - * @param {number} pixel_shift The number of pixels to shift the items vertically. - * @param {number} row_h The height of each row in the drawing. - * @returns {Row|BaseHeader} - */ - generate_first_item_to_draw(wy, wh, row_shift, pixel_shift, row_h) { - const start_y = wy + pixel_shift; - let cur_row = 0; - - /** - * Searches sub_items for first visible item. {@link ContentNavigationHelper.is_item_visible} - * @param {Array|Array} sub_items - * @returns {?BaseHeader|?Row} - */ - function iterate_level(sub_items) { - if (sub_items[0] instanceof BaseHeader) { - const header_h_in_rows = Math.round(sub_items[0].h / row_h); - - for (let i = 0; i < sub_items.length; ++i) { - /** @type {BaseHeader} */ - // @ts-ignore - const header = sub_items[i]; - if (cur_row + header_h_in_rows - 1 >= row_shift /*&& !header.dont_draw*/) { - header.set_x(setPlaylistX()); - header.set_y(start_y + (cur_row - row_shift) * row_h); - return header; - } - - // if (!header.dont_draw) { - cur_row += header_h_in_rows; - // } - - if (header.is_collapsed) { - continue; - } - - const result = iterate_level(header.sub_items); - if (result) { - return result; - } - } - } - else { // Row - if (cur_row + sub_items.length - 1 >= row_shift) { - const row_start_idx = (cur_row > row_shift) ? 0 : row_shift - cur_row; - cur_row += row_start_idx; - - const row = sub_items[row_start_idx]; - row.set_x(setPlaylistX()); - row.set_y(start_y + (cur_row - row_shift) * row_h); - - return row; - } - - cur_row += sub_items.length; - } - - return null; - } - - const first_item = iterate_level(this.sub_items); - - if (first_item == null) { - g_properties.scroll_pos = 0; // Additional safe guard for invalid scroll positions to break error loop - } - // No idea if this debug warning is still needed when we are using the safeguard - // Maybe we need to init the playlist or scrollbar if first_item is null due to a race condition - // Assert(first_item != null, LogicError, 'first_item_to_draw can\'t be null!'); - - return first_item; - } - - /** - * Generates an array of items to draw based on the given parameters and starting item. - * @param {number} wy The y-coordinate of the top edge of the visible area. - * @param {number} wh The height of the viewport. - * @param {Row|BaseHeader} first_item The starting item from which the drawing of items will begin. - * @returns {Array} - */ - generate_all_items_to_draw(wy, wh, first_item) { - const items_to_draw = [first_item]; - let cur_y = first_item.y + first_item.h; - - /** - * - * @param {Array|Array} sub_items - * @param {Row|BaseHeader} start_item - * @returns {boolean} True if start_item was used. - */ - function iterate_level(sub_items, start_item) { - const is_cur_level_header = sub_items[0] instanceof BaseHeader; - let start_item_used = !start_item; - - let leveled_start_item = start_item; - while (leveled_start_item && leveled_start_item.parent !== sub_items[0].parent) { - leveled_start_item = leveled_start_item.parent; - } - - const start_idx = leveled_start_item ? leveled_start_item instanceof Row ? leveled_start_item.idx_in_header : leveled_start_item.idx : 0; - - for (let i = start_idx; i < sub_items.length; ++i) { - const item = sub_items[i]; - if (start_item_used /* && !item.dont_draw*/) { - item.set_x(setPlaylistX()); - item.set_y(cur_y); - - items_to_draw.push(item); - cur_y += item.h; - } - if (!start_item_used && item === start_item) { - start_item_used = true; - } - - if (cur_y >= wy + wh) { - break; - } - - if (is_cur_level_header) { - if (item instanceof BaseHeader && item.is_collapsed) { - continue; - } - - // @ts-ignore - if (iterate_level(item.sub_items, start_item_used ? null : start_item)) { - start_item_used = true; - } - - if (cur_y >= wy + wh) { - break; - } - } - } - - return start_item_used; - } - - iterate_level(this.sub_items, first_item); - - return items_to_draw; - } -} - - -/** - * Provides methods for navigating and determining the visibility of items in a playlist content. - * @param {PlaylistContent} cnt_arg The content of a playlist. - * @class - */ -function ContentNavigationHelper(cnt_arg) { - /** - * @const - * @type {PlaylistContent} - */ - const cnt = cnt_arg; - - /** - * Gets the visible parent of the given item. - * @param {Row|BaseHeader} item The item to get the parent of. - * @returns {?BaseHeader} The visible parent item, or null if the item is not visible. - */ - this.get_visible_parent = (item) => { - if (!item.parent || !(item.parent instanceof BaseHeader)) { - return null; - } - - let cur_item = item; - do { - cur_item = cur_item.parent; - } while (cur_item.parent && cur_item.parent.is_collapsed); - - return cur_item; - }; - - /** - * Checks if the given item is visible. - * @param {Row|BaseHeader} item The item to check. - * @returns {boolean} True if item is not hidden by collapsed parent. - */ - this.is_item_visible = (item) => { - if (item.parent && item.parent instanceof BaseHeader) { - return !item.parent.is_collapsed; - } - - return true; - }; - - /** - * Checks if the given item is navigable. - * @param {Row|BaseHeader} item The item to check. - * @returns {boolean} True if item can be selected by arrow keys. - */ - this.is_item_navigateable = (item) => item instanceof Row ? true : item.is_collapsed; - - /** - * Gets the navigable neighbour of the given item in the given direction. - * {@link ContentNavigationHelper.is_item_navigateable} - * @param {Row|BaseHeader} item The item to start from. - * @param {number} direction The direction to search in. - * @returns {Row|BaseHeader|null} The first navigable neighbour, or null if no neighbour is found. - */ - this.get_navigateable_neighbour = function (item, direction) { - let neighbour_item = item; - do { - neighbour_item = this.get_visible_neighbour(neighbour_item, direction); - } while (neighbour_item && !this.is_item_navigateable(neighbour_item)); - - return neighbour_item; - }; - - /** - * Gets the visible neighbour of the given item in the given direction. - * {@link ContentNavigationHelper.is_item_visible} - * @param {Row|BaseHeader} item The item to get the neighbour of. - * @param {number} direction The direction to get the neighbour in. - * @returns {Row|BaseHeader|null} The visible neighbour of the given item in the given direction. - */ - this.get_visible_neighbour = function (item, direction) { - return direction > 0 ? this.get_next_visible_item(item) : this.get_prev_visible_item(item); - }; - - /** - * Gets the previous visible item of the given item. - * @param {Row|BaseHeader} item The item to get the previous visible item of. - * @returns {Row|BaseHeader|null} The previous visible item in the list. - */ - this.get_prev_visible_item = (item) => { - /** - * Get the last visible item in the list. - * @param {BaseHeader} item The item to search for the last visible item in. - * @returns {BaseHeader|Row} The last visible item in the given item. - */ - function get_last_visible_item(item) { - let last_item = item; - while (!(last_item instanceof Row) && !last_item.is_collapsed) { - last_item = Last(last_item.sub_items); - } - - return last_item; - } - - /** - * Gets the previous item before the header. - * @param {BaseHeader} header The header to search for the previous item before. - * @returns {Row|BaseHeader|null} The previous item before the given header. - */ - function get_prev_item_before_header(header) { - if (header === header.parent.sub_items[0]) { - return header.parent instanceof BaseHeader ? header.parent : null; - } - - // @ts-ignore - return get_last_visible_item(header.parent.sub_items[header.idx - 1]); - } - - /** - * Gets the previous item before the given row. - * @param {Row} row The row to search for the previous item before. - * @returns {Row|BaseHeader|null} The previous item before the given row. - */ - function get_prev_item_before_row(row) { - if (row === row.parent.sub_items[0]) { - return row.parent; - } - - return row.parent.sub_items[row.idx_in_header - 1]; - } - - if (!g_properties.show_header) { - if (item === cnt.rows[0]) { - return null; - } - - // @ts-ignore - return cnt.rows[item.idx - 1]; - } - - if (item instanceof BaseHeader) { - return get_prev_item_before_header(item); - } - - return get_prev_item_before_row(item); - }; - - /** - * Gets the next visible item in the list. - * @param {Row|BaseHeader} item The current item. - * @returns {Row|BaseHeader|null} The next visible item, or null if there is none. - */ - this.get_next_visible_item = (item) => { - /** - * Gets the next item in the list, or null if there is no next item. - * @param {Row|BaseHeader} item The item to start searching from. - * @returns {Row|BaseHeader|null} The next item, or null if there is no next item. - */ - function get_next_item(item) { - let next_item = item; - while (next_item) { - if (!next_item.parent) { - return null; - } - - if (next_item !== Last(next_item.parent.sub_items)) { - const next_item_idx = next_item instanceof Row ? next_item.idx_in_header : next_item.idx; - return next_item.parent.sub_items[next_item_idx + 1]; - } - - next_item = next_item.parent; - } - - return next_item; - } - - if (!g_properties.show_header) { - if (item === Last(cnt.rows)) { - return null; - } - - // @ts-ignore - return cnt.rows[item.idx + 1]; - } - - if (item instanceof BaseHeader && !item.is_collapsed) { - return item.sub_items[0]; - } - - return get_next_item(item); - }; -} - - -/** - * Determines the type of an item based on its class. - * @param {Object} item Header, DiscHeader, Row. - * @returns {Object} Header, DiscHeader, Row, null. - */ -function getItemType(item) { - if (!item) { - return 'Item is undefined or null'; - } - else if (item instanceof Header) { - return 'Header'; - } - else if (item instanceof DiscHeader) { - return 'DiscHeader'; - } - else if (item instanceof Row) { - return 'Row'; - } - return 'Unknown Item Type'; -} - - -///////////////////// -// * BASE HEADER * // -///////////////////// -/** - * The base header in the playlist provides methods for initializing sub-items and drawing the headers. - */ -class BaseHeader extends ListItem { - /** - * Represents a header element in a parent container. - * @param {ListContent|BaseHeader} parent The parent header. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} idx The index. - * @extends {ListItem} - * @abstract - * @class - */ - constructor(parent, x, y, w, h, idx) { - super(x, y, w, h); - - /** - * @const - * @type {BaseHeader} - */ - // @ts-ignore - this.parent = parent; - - /** - * @const - * @type {number} - */ - this.idx = idx; - - /** @type {boolean} */ this.is_collapsed = false; - /** @type {boolean} currently only updated at draw time */ this.hasSelection = false; - - /** - * @type {number} - * @private - */ - this.row_count = 0; - - /** @type {Array|Array} */ - this.sub_items = []; - - /** @type {MetaHandler} */ - this.meta_handler = new MetaHandler(); - } - - /** - * Initializes the items in the header. - * @param {Array|Array} items The items to initialize the contents with. - * @returns {number} The number of processed items. - * @abstract - */ - initialize_items(items) { - throw new LogicError('initialize_contents not implemented'); - } - - /** - * Draws the header. - * @override - * @abstract - */ - draw(gr) { - // Need this useless method to suppress warning =( - throw new LogicError('draw not implemented'); - } - - /** - * Sets the width of a ListItem and recursively sets the width of its sub-items. - * @param {number} w The width. - * @override - */ - set_w(w) { - ListItem.prototype.set_w.apply(this, [w]); - - this.sub_items.forEach((item) => { - item.set_w(w); - }); - } - - /** - * Gets the first row object from a list of sub-items. - * @returns {Row} The first row, or null if there are no rows. - */ - get_first_row() { - if (!this.sub_items.length) { - return null; - } - - let item = this.sub_items[0]; - while (item && !(item instanceof Row)) { - item = item.sub_items[0]; - } - - // @ts-ignore - return item; - } - - /** - * Gets an array of row indexes by recursively iterating through sub_items. - */ - get_row_indexes() { - let row_indexes = []; - - if (this.sub_items[0] instanceof Row) { - this.sub_items.forEach((item) => { - row_indexes.push(item.idx); - }); - } - else { - this.sub_items.forEach((item) => { - row_indexes = row_indexes.concat(item.get_row_indexes()); - }); - } - - return row_indexes; - } - - /** - * Calculates the total height of sub-items in rows, taking into account any nested sub-items. - * @returns {number} The total height in rows. - */ - get_sub_items_total_h_in_rows() { - if (!this.sub_items.length) { - return 0; - } - - if (this.sub_items[0] instanceof Row) { - return this.sub_items.length; - } - - const row_h = playlist_geo.row_h; - let h_in_rows = Math.round(this.sub_items[0].h / row_h) * this.sub_items.length; - this.sub_items.forEach((item) => { - if (!item.is_collapsed) { - h_in_rows += item.get_sub_items_total_h_in_rows(); - } - }); - - return h_in_rows; - } - - /** - * Checks if any of the sub_items have been selected, taking into account whether the first sub_item is a header or not. - * @returns {boolean} True or false. - */ - has_selected_items() { - // const is_function = typeof this.sub_items[0].has_selected_items === 'function'; - const isHeader = this.sub_items[0] instanceof BaseHeader; - return this.sub_items.some(item => isHeader ? item.has_selected_items() : item.is_selected()); - } - - /** - * Checks if all sub-items are completely selected, taking into account whether the first sub-item is a header or not. - * @returns {boolean} True or false. - */ - is_completely_selected() { - const isHeader = this.sub_items[0] instanceof BaseHeader; - return this.sub_items.every(item => isHeader ? item.is_completely_selected() : item.is_selected()); - } - - /** - * Checks if any of the sub_items are currently playing. - * @returns {boolean} True or false. - */ - is_playing() { - const is_function = typeof this.sub_items[0].is_playing === 'function'; - return this.sub_items.some(item => is_function ? item.is_playing() : item.is_playing); - } - - /** - * Checks if any of the sub_items are focused, taking into account whether the is_focused property is a function or a boolean value. - * @returns {boolean} True or false. - */ - is_focused() { - const is_function = typeof this.sub_items[0].is_focused === 'function'; - return this.sub_items.some(item => is_function ? item.is_focused() : item.is_focused); - } - - /** - * Handles mouse double click events on the playlist header. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - */ - on_mouse_lbtn_dblclk(x, y, m) { - plman.ExecutePlaylistDefaultAction(plman.ActivePlaylist, this.get_first_row().idx); - } - - /** - * Calculates the total duration in seconds of a list of sub-items, recursively if the sub-items are not of type Row. - * @returns {number} A float number. - */ - get_duration() { - let duration_in_seconds = 0; - - if (this.sub_items[0] instanceof Row) { - this.sub_items.forEach((item) => { - duration_in_seconds += item.metadb.Length; - }); - } - else { - this.sub_items.forEach((item) => { - duration_in_seconds += item.get_duration(); - }); - } - - return duration_in_seconds; - } -} - - -///////////////////// -// * DISC HEADER * // -///////////////////// -/** - * The disc header creates collapseable headers in the playlist row for music albums that have multiple CDs. - */ -class DiscHeader extends BaseHeader { - /** - * @param {Object} parent The parent object or container that this object belongs to. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} idx The index to identify the instance of the object. - * @extends {BaseHeader} - * @class - */ - constructor(parent, x, y, w, h, idx) { - super(parent, x, y, w, h, idx); - - this.idx = idx; - this.num_in_header = 0; - // this.dont_draw = false; - this.disc_title = ''; - } - - /** - * Initializes sub_items with rows from rows_with_data that have the same value in the second column, - * sets properties for each row, and returns the length of sub_items. - * @param {Array} rows_with_data An array of arrays, where each inner array represents a row of data. - * Each inner array contains two elements: the row object and the data value for that row. - * @override - */ - initialize_items(rows_with_data) { - /** @type {Row[]} */ - this.sub_items = []; - if (!rows_with_data.length) { - return 0; - } - - const first_data = rows_with_data[0][1]; - rows_with_data.every((item, i) => { - if (first_data !== item[1]) { - return false; // Aborts the every - } - - const row = item[0]; - - row.idx_in_header = i; - // noinspection JSBitwiseOperatorUsage - row.is_odd = !(i & 1); - row.parent = this; - - this.sub_items.push(row); - return true; - }); - - this.disc_title = first_data; - - return this.sub_items.length; - } - - // public: - - /** - * Draws the disc header in the rows when an album contains multiple CDs. - * @param {GdiGraphics} gr - * @param {number} top The top of the header. - * @param {number} bottom The bottom of the header. - */ - draw(gr, top, bottom) { - gr.SetSmoothingMode(SmoothingMode.None); - - const cur_x = this.x + SCALE(20); - const right_pad = SCALE(20); - - let title_font = g_pl_fonts.title_normal; - let title_color = g_pl_colors.row_title_normal; - - if (this.is_selected()) { - title_color = g_pl_colors.row_title_selected; - title_font = g_pl_fonts.title_selected; - gr.FillSolidRect(this.x, this.y, SCALE(8), this.h - 1, g_pl_colors.row_sideMarker); - } - - const disc_header_text_format = g_string_format.v_align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - const disc_text = this.disc_title; // $('[Disc %discnumber% $if('+ tf.disc_subtitle+', \u2014 ,) ]['+ tf.disc_subtitle +']', that.sub_items[0].metadb); - gr.DrawString(disc_text, title_font, title_color, cur_x, this.y, this.w, this.h, disc_header_text_format); - const disc_w = Math.ceil(gr.MeasureString(disc_text, title_font, 0, 0, 0, 0).Width + 14); - - const subheader_PLR_album = (g_properties.show_PLR_header && $('[%totaldiscs%]', this.sub_items[0].metadb) > 1) ? `${this.meta_handler.get_PLR($('%replaygain_album_gain%', this.sub_items[0].metadb), $('%replaygain_album_peak_db%', this.sub_items[0].metadb))} LU | ` : ''; - const replainGain = ($('[%totaldiscs%]', this.sub_items[0].metadb) > 1) ? $('[%replaygain_album_gain% | ]', this.sub_items[0].metadb) : ''; - const tracks_text = `${(replainGain)}${(subheader_PLR_album)}${this.sub_items.length} Track${this.sub_items.length > 1 ? 's' : ''} - ${utils.FormatDuration(this.get_duration())}`; - - const tracks_w = Math.ceil(gr.MeasureString(tracks_text, title_font, 0, 0, 0, 0).Width + 20); - const tracks_x = this.x + this.w - tracks_w - right_pad; - - gr.DrawString(tracks_text, title_font, title_color, tracks_x, this.y, tracks_w, this.h, g_string_format.v_align_center | g_string_format.h_align_far); - - if (this.is_collapsed || !this.is_collapsed) { - const line_y = Math.round(this.y + this.h / 2) + SCALE(4); - gr.DrawLine(RES_4K ? cur_x + disc_w + 5 : cur_x + disc_w - 5, line_y, RES_4K ? this.x + this.w - tracks_w - 40 : this.x + this.w - tracks_w - 10, line_y, 1, g_pl_colors.row_disc_subheader_line); - } - } - - /** - * Checks if all sub_items are selected. - * @returns {boolean} True or false. - */ - is_selected() { - return this.sub_items.every(row => row.is_selected()); - } - - /** - * Toggles the state of the disc header. - */ - toggle_collapse() { - this.is_collapsed = !this.is_collapsed; - - // this.header.collapse(); - // this.header.expand(); - } - - /** - * Toggles the collapse state of the disc header when the left mouse button is double-clicked. - * @param {CollapseHandler} collapse_handler The collapse handler to use. - */ - on_mouse_lbtn_dblclk(collapse_handler) { - collapse_handler.toggle_collapse(this); - } - - /** - * Calculates the total duration in seconds of a list of sub items. - * @returns {number} A float number. - * @override - */ - get_duration() { - let duration_in_seconds = 0; - - this.sub_items.forEach(item => { - duration_in_seconds += item.metadb.Length; - }); - - if (!duration_in_seconds) { - return 0; - } - - return duration_in_seconds; - } -} - - -/** - * Prepares the initialization data for the disc header. - * @param {Array} rows_to_process The rows to process. - * @param {FbMetadbHandleList} rows_metadb The metadb of the rows to process. - * @returns {Array} Has the following format Array<[row,row_data]> - */ -DiscHeader.prepare_initialization_data = (rows_to_process, rows_metadb) => { - const tfo = fb.TitleFormat(`$ifgreater(%totaldiscs%,1,[Disc %discnumber% $if(${tf.disc_subtitle}, \u2014 ,) ],)[${tf.disc_subtitle}]`); - const disc_data = tfo.EvalWithMetadbs(rows_metadb); - - return Zip(rows_to_process, disc_data); -}; - - -/** - * Creates a header for each disc. - * @param {BaseHeader} parent The parent element. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {Array} prepared_rows The rows of data. Has the following format Array<[row,row_data]> - * @param {number} rows_to_process_count The number of rows to process. - * @returns {Array} - */ -DiscHeader.create_headers = (parent, x, y, w, h, prepared_rows, rows_to_process_count) => { - /** - * Checks if the data in the rows is valid. - * @param {Array} rows_with_data The rows of data. - * @param {number} rows_to_check_count The number of rows to check. - * @return {boolean} True if the data is valid, false otherwise. - */ - function has_valid_data(rows_with_data, rows_to_check_count) { - for (let i = 0; i < rows_to_check_count; ++i) { - if (!IsEmpty(rows_with_data[i][1])) { - return true; - } - } - return false; - } - - if (!has_valid_data(prepared_rows, rows_to_process_count)) { - TrimArray(prepared_rows, rows_to_process_count, true); - return []; - } - - const prepared_rows_copy = Object.assign(Object.create(prepared_rows)); - prepared_rows_copy.length = rows_to_process_count; - - let header_idx = 0; - const headers = []; - const start_length = prepared_rows_copy.length; - while (prepared_rows_copy.length) { - const header = new DiscHeader(parent, x, y, w, h, header_idx); - const processed_items = header.initialize_items(prepared_rows_copy); - - TrimArray(prepared_rows_copy, processed_items, true); - - headers.push(header); - ++header_idx; - } - - TrimArray(prepared_rows, rows_to_process_count, true); - - return headers; -}; - - -//////////////// -// * HEADER * // -//////////////// -/** - * Creates and draws the playlist header and disc header. - */ -class Header extends BaseHeader { - /** - * @param {ListContent|BaseHeader} parent The parent object or container that this object belongs to. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {number} idx The index. - * @extends {BaseHeader} - * @final - * @class - */ - constructor(parent, x, y, w, h, idx) { - super(parent, x, y, w, h, idx); - - /** - * @const - * @type {number} - */ - this.art_max_size = this.h - SCALE(16); - - /** @type {FbMetadbHandle} */ - this.metadb = undefined; - /** - * @type {?GdiBitmap} - * undefined > Not Loaded; null > Loaded & Not Found; !isNil > Loaded & Found - */ - this.art = undefined; - this.grouping_handler = Header.grouping_handler; - - this.hyperlinks = {}; - this.hyperlinks_initialized = false; - this.was_playing = undefined; // Last value of this.is_playing() updated each draw cycle - - /** - * @type {number} - */ - this.rating_left_pad = 0; - /** - * @type {number} - */ - this.rating_right_pad = 10; - /** @type {?Rating} */ - this.rating = undefined; - - this.header_image = undefined; - - this.initialize_rating(); - } - - /** - * Initializes items by creating sub headers and assigning properties to each item. - * @param {Array} rows_with_data The rows with data. - * @returns {number} The length of the `owned_rows` array. - * @override - */ - initialize_items(rows_with_data) { - this.sub_items = []; - - const rows_with_header_data = rows_with_data[0]; - if (!rows_with_header_data.length) { - return 0; - } - - const first_data = rows_with_header_data[0][1]; - - const owned_rows = []; - for (let i = 0; i < rows_with_header_data.length; i++) { - if (first_data !== rows_with_header_data[i][1]) { - break; - } - owned_rows.push(rows_with_header_data[i][0]); - } - - this.metadb = owned_rows[0].metadb; - - let sub_headers = []; - if (g_properties.show_disc_header && this.grouping_handler.show_disc()) { - const rows_with_discheader_data = rows_with_data[1]; - sub_headers = this.create_disc_headers(rows_with_discheader_data, owned_rows.length); - if (sub_headers.length) { - this.sub_items = sub_headers; - } - } - - if (sub_headers.length) { - this.sub_items = sub_headers; - } - else { - this.sub_items = owned_rows; - - owned_rows.forEach((item, i) => { - item.idx_in_header = i; - if (g_properties.show_header) { - // noinspection JSBitwiseOperatorUsage - item.is_odd = !(i & 1); - } - item.parent = this; - }); - } - - return owned_rows.length; - } - - /** - * Initializes the rating and sets its position within a given area. - */ - initialize_rating() { - this.rating = new Rating(0, this.y, this.w - this.rating_right_pad, this.h, this.metadb); - this.rating.x = this.x + this.w - (this.rating.w + this.rating_right_pad); - } - - /** - * Creates disc headers for the playlist based on the provided parameters. - * @param {Array} prepared_rows The rows with data. - * @param {number} rows_to_proccess_count The number of rows to process. - * @returns {Array} The disc headers. - */ - create_disc_headers(prepared_rows, rows_to_proccess_count) { - return DiscHeader.create_headers(this, this.x, 0, this.w, playlist_geo.row_h, prepared_rows, rows_to_proccess_count); - } - - /** - * Returns a string containing information about a group of audio files, - * including codec, sample rate, bit depth, track count, disc number, replay gain, genre tags and duration. - * @param {boolean} is_radio If the playback source is from radio streaming. - * @param {boolean} hasGenreTags If the group has genre tags or not. - * @returns {string} The information about the group. - */ - getGroupInfoString(is_radio, hasGenreTags) { - const bitspersample = Number($('$info(bitspersample)', this.metadb)); - const samplerate = Number($('$info(samplerate)', this.metadb)); - const sample = ((bitspersample > 16 || samplerate > 44100 || settings.playlistShowBitSampleAlways) ? $(' [$info(bitspersample)bit/]', this.metadb) + samplerate / 1000 + 'khz' : ''); - const formatAiffWav = 'aiff' || 'wav'; - let codec = formatAiffWav ? $('$upper($ext(%path%))', this.metadb) : $('$if2(%codec%,$ext(%path%))', this.metadb); - - if (codec === 'dca (dts coherent acoustics)') { - codec = 'dts'; - } - if (codec === 'cue') { - codec = $('$ext($info(referenced_file))', this.metadb); - } - else if (codec === 'mpc') { - codec += $('[-$info(codec_profile)]', this.metadb).replace('quality ', 'q'); - } - else if (['dts', 'ac3', 'atsc a/52'].includes(codec)) { - codec += $("[ $replace($replace($replace($info(channel_mode), + LFE,),' front, ','/'),' rear surround channels',$if($strstr($info(channel_mode),' + LFE'),.1,.0))] %bitrate%", this.metadb) + ' kbps'; - codec = codec.replace('atsc a/52', 'Dolby Digital'); - } - else if ($('$info(encoding)', this.metadb) === 'lossy') { - codec += $('$info(codec_profile)', this.metadb) === 'CBR' ? $('[-%bitrate% kbps]', this.metadb) : $('[-$info(codec_profile)]', this.metadb); - } - if (codec) { - codec += sample; - } - else { - codec = (this.metadb.RawPath.startsWith('3dydfy:') || this.metadb.RawPath.startsWith('fy+')) ? 'yt' : this.metadb.Path; - } - - let track_count = this.sub_items.length; - let has_discs = false; - if (this.sub_items[0] instanceof DiscHeader) { - track_count = 0; - has_discs = true; - this.sub_items.forEach(discHeader => { - track_count += discHeader.sub_items.length; - }); - } - - const disc_number = (!g_properties.show_disc_header && $('[%totaldiscs%]', this.metadb) !== '1') ? $('[ | Disc: %discnumber%[/%totaldiscs%]]', this.metadb) : ''; - const track_text = is_radio ? '' : ' | ' + - // (this.grouping_handler.show_disc() && has_discs ? this.sub_items.length + ' Discs - ' : '') + - (this.grouping_handler.show_disc() && has_discs && ($('[%totaldiscs%]', this.metadb) > 1) ? `${this.sub_items.length} Discs - ` : '') + - track_count + (track_count === 1 ? ' Track' : ' Tracks'); - const replaygain = (this.grouping_handler.show_disc() && (!has_discs || $('[%totaldiscs%]', this.metadb) === '')) ? $('[ | %replaygain_album_gain%]', this.metadb) : ''; - const plr_album = (g_properties.show_PLR_header && this.grouping_handler.show_disc() && (!has_discs || $('[%totaldiscs%]', this.metadb) === '')) ? ` | ${this.meta_handler.get_PLR($('%replaygain_album_gain%', this.metadb), $('%replaygain_album_peak_db%', this.metadb))} LU` : ''; - let info_text = codec + disc_number + replaygain + plr_album + track_text; - - if (hasGenreTags) { - info_text = ` | ${info_text}`; - } - if (this.get_duration()) { - info_text += ` | Time: ${utils.FormatDuration(this.get_duration())}`; - } - - if (g_properties.show_rating_header && $('%rating%', this.metadb) !== '') { - const albumName = $('%album%', this.metadb); - const albumRating = this.rating.get_album_rating().get(albumName); - - if (albumRating !== undefined) { - info_text += ` | Rating: ${albumRating}`; - } - } - - // * Use custom playlist header info if pattern was defined in the config file - const customInfoText = $(settings.playlistCustomHeaderInfo, this.metadb); - if (customInfoText !== '') { - return ` | ${customInfoText}`; - } - - return info_text; - } - - /** - * Draws the playlist header either in normal or compact layout. - * @param {GdiGraphics} gr - * @param {number} top The y-coordinate of the top edge of the header area. - * @param {number} bottom The y-coordinate of the bottom edge of the header. - * @override - */ - draw(gr, top, bottom) { - if (this.w <= 0 || this.h <= 0) return; - // drawProfiler = fb.CreateProfiler('Header.draw items:' + this.sub_items.length); - if (g_properties.use_compact_header) { - this.draw_compact_header(gr); - } - else { - this.draw_normal_header(gr, top, bottom); - } - // drawProfiler.Print(); - } - - /** - * Draws the playlist header in normal layout. - * @param {GdiGraphics} gr - * @param {number} top The y-coordinate of the top edge of the header area. - * @param {number} bottom The y-coordinate of the bottom edge of the header. - */ - draw_normal_header(gr, top, bottom) { - let clipImg = gdi.CreateImage(this.w, this.h); - const grClip = clipImg.GetGraphics(); - const scrollbar = g_properties.show_scrollbar && playlist.is_scrollbar_available; - - if (!this.hyperlinks_initialized) { - this.initialize_hyperlinks(gr); - } - let cache_header = true; // Caching is a lot faster, but need to handle artwork loading - if (this.was_playing !== this.is_playing()) { - this.was_playing = this.is_playing(); - cache_header = false; - this.clearCachedHeaderImg(); - } - const hasSelection = this.has_selected_items(); - if (this.hasSelection !== hasSelection) { - this.hasSelection = hasSelection; - cache_header = false; - this.clearCachedHeaderImg(); - } - if (!cache_header || !this.header_image || this.clearCachedHeaderImg) { - let artist_color = g_pl_colors.header_artist_normal; - let album_color = g_pl_colors.header_album_normal; - let info_color = g_pl_colors.header_info_normal; - let date_color = g_pl_colors.header_date_normal; - let line_color = g_pl_colors.header_line_normal; - let artist_font = g_pl_fonts.artist_normal; - const date_font = g_pl_fonts.date; - const updatedNowpBg = g_pl_colors.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing - - if (this.is_playing() && updatedNowpBg) { - artist_color = g_pl_colors.header_artist_playing; - album_color = g_pl_colors.header_album_playing; - info_color = g_pl_colors.header_info_playing; - date_color = g_pl_colors.header_date_playing; - line_color = g_pl_colors.header_line_playing; - artist_font = g_pl_fonts.artist_playing; - - if (lightBg && pref.theme === 'black') { - artist_color = RGB(0, 0, 0); - album_color = RGB(20, 20, 20); - info_color = RGB(20, 20, 20); - date_color = RGB(20, 20, 20); - } else if (lightBg && pref.theme === 'white' && pref.layout !== 'default') { - artist_color = RGB(60, 60, 60); - album_color = RGB(60, 60, 60); - info_color = RGB(60, 60, 60); - date_color = RGB(60, 60, 60); - line_color = RGB(100, 100, 100); - } - } - - if (!pref.styleBlend) grClip.FillSolidRect(0, 0, this.w, this.h, g_pl_colors.bg); // Solid background for ClearTypeGridFit text rendering - - // if (this.hasSelection && pref.theme.startsWith('custom')) { - // grClip.FillSolidRect(0, 0, this.w, this.h, g_pl_colors.row_selection_bg); - // } - - if (this.is_playing() && updatedNowpBg) { - const p = SCALE(6); // From art below - - if (pref.theme === 'white' && !pref.styleBlackAndWhite && !pref.styleBlackAndWhite2 && pref.layout === 'default' || (pref.theme === 'reborn' || pref.theme === 'random') && noAlbumArtStub) { - grClip.FillSolidRect(0, p, SCALE(8), this.h - p * 2, g_pl_colors.header_nowplaying_bg); - } - else { - grClip.FillSolidRect(0, 0, scrollbar ? this.w - SCALE(12) : this.w, this.h * 2, g_pl_colors.header_nowplaying_bg); - } - if (pref.theme === 'white' && (pref.styleBlackAndWhite || pref.styleBlackAndWhite2) || !['white', 'black', 'cream'].includes(pref.theme)) { - grClip.FillSolidRect(0, 0, SCALE(8), this.h, g_pl_colors.header_sideMarker); - } - } - - // * Need to apply text rendering AntiAliasGridFit when using style Blend or when using custom theme fonts with larger font sizes - grClip.SetTextRenderingHint(pref.styleBlend || pref.customThemeFonts && headerFontSize > 18 ? TextRenderingHint.AntiAliasGridFit : TextRenderingHint.ClearTypeGridFit); - - if (this.is_collapsed && this.is_focused() || this.is_completely_selected() && g_properties.show_header && g_properties.auto_collapse) { - grClip.DrawRect(-1, 0, this.w + 1, this.h - 1, 1, line_color); - grClip.FillSolidRect(0, 0, SCALE(8), this.h, g_pl_colors.header_sideMarker); - } - - //************************************************************// - - let left_pad = SCALE(10) + (this.art === null && g_properties.auto_album_art || !g_properties.show_album_art ? SCALE(10) : 0); - - // * ARTBOX * // - if (g_properties.show_album_art) { - if (!this.is_art_loaded()) { - const cached_art = Header.art_cache.get_image_for_meta(this.metadb); - if (cached_art) { - this.assign_art(cached_art); - } - } - - if (this.art !== null || !g_properties.auto_album_art) { - const p = SCALE(6); - const spacing = SCALE(2); - - const art_box_size = this.art_max_size + spacing * 2; - const art_box_x = p * 3; - let art_box_y = p; - let art_box_w = art_box_size; - let art_box_h = art_box_size; - - if (this.art) { - const art_x = art_box_x + spacing; - let art_y = art_box_y + spacing; - const art_h = this.art.Height; - const art_w = this.art.Width; - if (art_h > art_w) { - art_box_w = art_w + spacing * 2; - } - else { - art_box_h = art_h + spacing * 2; - art_y += Math.round((this.art_max_size - art_h) / 2); - art_box_y = art_y - spacing; - } - grClip.DrawImage(this.art, art_x, art_y, art_w, art_h, 0, 0, art_w, art_h, 0, 220); - } - else if (!this.is_art_loaded()) { - grClip.DrawString('LOADING', g_pl_fonts.cover, this.is_playing() ? artist_color : g_pl_colors.row_title_normal, art_box_x, art_box_y, art_box_size, art_box_size, g_string_format.align_center); - cache_header = false; // Don't cache until artwork is loaded - } - else { // null - const is_radio = this.metadb.RawPath.startsWith('http') || this.metadb.Path.startsWith('spotify'); - grClip.DrawString(isStreaming && is_radio ? 'LIVE\n ON AIR' : 'NO COVER', g_pl_fonts.cover, this.is_playing() ? artist_color : g_pl_colors.row_title_normal, art_box_x, art_box_y, art_box_size, art_box_size, g_string_format.align_center); - } - - // * Used for Library thumbnail size - playlistThumbSize = art_box_w - 4; - grClip.DrawRect(art_box_x, art_box_y, art_box_w - 1, art_box_h - 1, 1, line_color); - left_pad += art_box_x + art_box_w; - - let i_ = 0; - let offset_ = 0; - while (this.hyperlinks['artist' + i_]) { - if (i_ === 0) { - offset_ = this.hyperlinks.artist0.x - left_pad; - if (!offset_) break; - } - this.hyperlinks['artist' + i_].x -= offset_; - i_++; - } - - this.hyperlinks.album && this.hyperlinks.album.set_xOffset(left_pad); - - let i = 0; - let offset = 0; - while (this.hyperlinks['genre' + i]) { - if (i === 0) { - offset = this.hyperlinks.genre0.x - left_pad; - if (!offset) break; - } - this.hyperlinks['genre' + i].x -= offset; - i++; - } - } - else if (this.art === null && g_properties.auto_album_art) { - this.hyperlinks.artist0 && this.hyperlinks.artist0.set_xOffset(left_pad); - this.hyperlinks.album && this.hyperlinks.album.set_xOffset(left_pad); - this.hyperlinks.genre0 && this.hyperlinks.genre0.set_xOffset(left_pad); - } - } - - //************************************************************// - - const is_radio = this.metadb.RawPath.startsWith('http') || this.metadb.Path.startsWith('spotify'); - - // * Part1: Artist - // * Part2: Album + line + Date OR line - // * Part3: Info OR album - const part1_cur_x = left_pad; - let part2_cur_x = left_pad; - let part3_cur_x = left_pad; - - const part_h = !g_properties.show_group_info ? this.h / 2 : this.h / 3; - let part2_right_pad = 0; - - // * DATE * // - if (this.grouping_handler.show_date()) { - const date_query = pref.showPlaylistFullDate ? tf.date : tf.year; - const date_text = $(date_query, this.metadb); - if (date_text && date_text !== '0000') { - const date_w = Math.ceil(gr.MeasureString(date_text, date_font, 0, 0, 0, 0).Width + 5); - const date_x = this.w - date_w; - - if (!this.hyperlinks.date && date_x > left_pad) { - const date_y = 0; - const date_h = this.h - 4; - grClip.DrawString(date_text, date_font, date_color, date_x, date_y, date_w, date_h, g_string_format.v_align_center); - } else { - this.hyperlinks.date.draw(grClip, date_color); - } - - part2_right_pad += this.w - date_x; - } - } - - // * ARTIST * // - if (this.grouping_handler.get_title_query()) { - const artist_text_format = g_string_format.v_align_far | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - let artist_text = $(this.grouping_handler.get_title_query(), this.metadb); - - if (!artist_text && is_radio) { - artist_text = 'Radio Stream'; - } - if (artist_text) { - let artist_x = part1_cur_x; - let artist_w = this.w - artist_x * 2; - let artist_h = part_h; - if (!g_properties.show_group_info) { - artist_w -= part2_right_pad + 5; - artist_h -= 5; - } - - if (is_radio || !this.hyperlinks.artist0) { - grClip.DrawString(artist_text, artist_font, artist_color, artist_x, 0, artist_w, artist_h, artist_text_format); - } - else { - let i = 0; - let artist_hyperlink; - while (this.hyperlinks['artist' + i]) { - if (i > 0) { - grClip.DrawString(' \u2022 ', artist_font, artist_color, artist_hyperlink.x + artist_hyperlink.getWidth(), artist_h * 0.25, SCALE(20), artist_h); - } - artist_hyperlink = this.hyperlinks['artist' + i]; - artist_hyperlink.draw(grClip, artist_color); - artist_x = artist_hyperlink.x; - artist_w = artist_hyperlink.getWidth(); - i++; - } - } - // part1_cur_x += artist_w; - } - } - - // * ALBUM * // - if (this.grouping_handler.get_sub_title_query()) { - const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); - - if (album_text) { - const album_h = part_h; - let album_y = part_h; - let album_x; - if (g_properties.show_group_info) { - album_x = part2_cur_x; - } - else { - album_y += 5; - album_x = part3_cur_x; - } - const album_w = this.w - album_x - (part2_right_pad + 5); - - let album_text_format = g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - if (g_properties.show_group_info) { - album_text_format |= g_string_format.v_align_center; - } - - if (!this.hyperlinks.album) { - grClip.DrawString(album_text, g_pl_fonts.album, album_color, album_x, album_y, album_w, album_h, album_text_format); - } else { - this.hyperlinks.album.draw(grClip, album_color); - } - - const album_text_w = Math.ceil( - /** @type {!number} */ - gr.MeasureString(album_text, g_pl_fonts.album, 0, 0, 0, 0).Width - ); - if (g_properties.show_group_info) { - part2_cur_x += album_text_w; - } - else { - part3_cur_x += album_text_w; - } - } - } - - // * INFO * // - if (g_properties.show_group_info) { - const info_x = part3_cur_x; - const info_y = 2 * part_h - (RES_4K ? 5 : 0); - const info_h = part_h; //row_h; - const info_w = this.w - info_x; - const info_text_format = g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - - // * Genres - let genre_text_w = 0; - const extraGenreSpacing = 0; // Don't use SCALE() due to font differences - let genreX = info_x; - if (!is_radio && this.grouping_handler.get_query_name() !== 'artist') { - if (!this.hyperlinks.genre0) { - const genre_text = $('[%genre%]', this.metadb).replace(/, /g, ' \u2022 '); - genre_text_w = Math.ceil(gr.MeasureString(genre_text, g_pl_fonts.info, 0, 0, 0, 0).Width + extraGenreSpacing); - grClip.DrawString(genre_text, g_pl_fonts.info, info_color, genreX, info_y, info_w, info_h, info_text_format); - } else { - let i = 0; - let genre_hyperlink; - while (this.hyperlinks['genre' + i]) { - if (i > 0) { - grClip.DrawString(' \u2022 ', g_pl_fonts.info, info_color, genre_hyperlink.x + genre_hyperlink.getWidth() + SCALE(2), info_y, SCALE(20), info_h); - } - genre_hyperlink = this.hyperlinks['genre' + i]; - genre_hyperlink.draw(grClip, info_color); - genreX = genre_hyperlink.x; - genre_text_w = genre_hyperlink.getWidth(); - i++; - } - } - } - - const info_text = this.getGroupInfoString(is_radio, genre_text_w > 0); - const info_text_w = Math.ceil(gr.MeasureString(info_text, g_pl_fonts.info, 0, 0, 0, 0).Width); // TODO: Mordred - should only call MeasureString once - grClip.DrawString(info_text, g_pl_fonts.info, info_color, genreX + (genre_text_w || 0), info_y + 1, info_w - (genreX - info_x) - (genre_text_w || 0) - SCALE(20), info_h, info_text_format); - - // * Record labels - if (!this.hyperlinks.label0) { - const label_string = $('$if2(%label%,[%publisher%])', this.metadb).replace(/, /g, ' \u2022 '); - const label_w = Math.ceil(gr.MeasureString(label_string, g_pl_fonts.info, 0, 0, 0, 0).Width + 10); - if (info_w > label_w + info_text_w) { - grClip.DrawString(label_string, g_pl_fonts.info, info_color, this.w - label_w - 10, info_y, label_w, info_h, g_string_format.h_align_far); - } - } else { - let i = 0; - let drawCount = 0; - let lastLabel; - while (this.hyperlinks['label' + i]) { - const label_hyperlink = this.hyperlinks['label' + i]; - if (label_hyperlink.x > genreX + genre_text_w + info_text_w) { - if (drawCount > 0) { - grClip.DrawString(' \u2022', g_pl_fonts.info, info_color, lastLabel.x + lastLabel.getWidth(), info_y, SCALE(20), info_h); - } - label_hyperlink.draw(grClip, info_color); - drawCount++; - } - lastLabel = label_hyperlink; // We want to draw bullet AFTER the previous label - i++; - } - } - - // * Info line - const info_text_h = Math.ceil(gr.MeasureString(info_text, g_pl_fonts.info, 0, 0, 0, 0).Height + 5); - const line_x1 = left_pad; - const line_x2 = this.w - SCALE(20); - const line_y = info_y + info_text_h; - if (line_x2 - line_x1 > 0) { - grClip.DrawLine(line_x1, line_y, line_x2, line_y, 1, line_color); - } - } - - // * PART 2 ALBUM LINE - { - let line_x1 = part2_cur_x; - if (line_x1 !== left_pad) { - line_x1 += RES_4K ? 20 : 9; - } - - const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); - const album_height = gr.MeasureString(album_text, g_pl_fonts.album, 0, 0, 0, 0).Height; - const date_query = pref.showPlaylistFullDate ? tf.date : tf.year; - const date_text = $(date_query, this.metadb); - const date_height = gr.MeasureString(date_text, g_pl_fonts.date, 0, 0, 0, 0).Height; - const line_x2 = this.w - part2_right_pad - (date_text ? (RES_4K ? 58 : 25) : SCALE(20)); - const line_y = !g_properties.show_group_info ? Math.round(this.h / 2) : - pref.customThemeFonts ? Math.floor(this.h / 2 + (date_height - album_height)) : - Math.round(this.h / 2) + SCALE(RES_4K ? 7 : 6) + - // Y-corrections - (headerFontSize === 22 ? RES_4K ? -4 : -1 : - headerFontSize === 20 ? RES_4K ? -5 : 0 : - headerFontSize === 18 ? RES_4K ? -5 : -1 : - headerFontSize === 17 ? RES_4K ? -4 : 1 : - headerFontSize === 16 ? RES_4K ? -3 : -1 : - headerFontSize === 15 ? RES_4K ? -4 : 0 : - headerFontSize === 14 && RES_4K ? -4 : - headerFontSize === 13 && RES_4K ? -3 : - headerFontSize === 12 && RES_4K ? -5 : - headerFontSize === 10 && RES_4K ? -2 : - headerFontSize && !RES_4K < 15 ? -1 : - 0); - - if (line_x2 - line_x1 > 0) { - grClip.DrawLine(line_x1, line_y, line_x2, line_y, 1, line_color); - } - } - - clipImg.ReleaseGraphics(grClip); - if (cache_header) { - this.header_image = clipImg; - } - } - - let y = this.y; - let h = this.h; - let srcY = 0; - if (this.y < top) { - y = top; - h = this.h - (top - this.y); - srcY = this.h - h; - } else if (this.y + this.h > bottom) { - h = bottom - this.y; - } - gr.DrawImage(cache_header ? this.header_image : clipImg, this.x, y, this.w, h, 0, srcY, this.w, h); - if (this.is_completely_selected() && (pref.theme === 'white' || pref.theme === 'black')) { - gr.SetSmoothingMode(SmoothingMode.None); - gr.FillSolidRect(this.x, y + SCALE(6), 0, h, col.primary); - gr.SetSmoothingMode(SmoothingMode.HighQuality); - } - clipImg = null; - } - - /** - * Draws the playlist header in compact layout. - * @param {GdiGraphics} gr - */ - draw_compact_header(gr) { - let artist_color = g_pl_colors.header_artist_normal; - let album_color = g_pl_colors.header_album_normal; - let date_color = g_pl_colors.header_date_normal; - let line_color = g_pl_colors.header_line_normal; - let artist_font = g_pl_fonts.artist_normal_compact; - const date_font = g_pl_fonts.date_compact; - const updatedNowpBg = g_pl_colors.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing - const scrollbar = g_properties.show_scrollbar && playlist.is_scrollbar_available; - - if (this.is_playing() && updatedNowpBg) { - artist_color = g_pl_colors.header_artist_playing; - album_color = g_pl_colors.header_album_playing; - date_color = g_pl_colors.header_date_playing; - line_color = g_pl_colors.header_line_playing; - artist_font = g_pl_fonts.artist_playing_compact; - - if (lightBg && (pref.theme === 'white' || pref.theme === 'black')) { - artist_color = RGB(0, 0, 0); - album_color = RGB(20, 20, 20); - date_color = RGB(20, 20, 20); - } - else if (!lightBg && (pref.theme === 'white' || pref.theme === 'black')) { - artist_color = RGB(240, 240, 240); - album_color = RGB(220, 220, 220); - date_color = RGB(220, 220, 220); - } - } - - const clipImg = gdi.CreateImage(this.w, this.h); - const grClip = clipImg.GetGraphics(); - - //---> - if (!pref.styleBlend) grClip.FillSolidRect(0, 0, this.w, this.h, g_pl_colors.bg); // Solid background for ClearTypeGridFit text rendering - // if (this.has_selected_items() && pref.theme.startsWith('custom')) { - // grClip.FillSolidRect(0, 0, this.w, this.h, g_pl_colors.row_selection_bg); - // } - - if (this.is_playing() && updatedNowpBg) { - grClip.FillSolidRect(0, 0, scrollbar ? this.w - SCALE(12) : this.w, this.h, g_pl_colors.header_nowplaying_bg); - grClip.FillSolidRect(0, 0, ['white', 'black', 'cream'].includes(pref.theme) ? 0 : SCALE(8), this.h, g_pl_colors.header_sideMarker); - } - - // * Need to apply text rendering AntiAliasGridFit when using style Blend or when using custom theme fonts with larger font sizes - grClip.SetTextRenderingHint(pref.styleBlend || pref.customThemeFonts && headerFontSize > 18 ? TextRenderingHint.AntiAliasGridFit : TextRenderingHint.ClearTypeGridFit); - - if (this.is_collapsed && this.is_focused()) { - grClip.DrawRect(-1, 0, this.w + 1, this.h - 1, 1, line_color); - } - - //************************************************************// - - const is_radio = this.metadb.RawPath.startsWith('http'); - - const left_pad = SCALE(20); - let right_pad = 0; - let cur_x = left_pad; - - // * Date - if (this.grouping_handler.show_date()) { - const date_query = pref.showPlaylistFullDate ? tf.date : tf.year; - const date_text = $(date_query, this.metadb); - if (date_text) { - const date_w = Math.ceil(gr.MeasureString(date_text, date_font, 0, 0, 0, 0).Width + 5); - const date_x = this.w - date_w - SCALE(12); - const date_y = 0; - const date_h = this.h; - - if (date_x > left_pad) { - grClip.DrawString(date_text, date_font, date_color, date_x, date_y, date_w, date_h, g_string_format.v_align_center); - } - - right_pad += this.w - date_x; - } - } - - // * Artist - if (this.grouping_handler.get_title_query()) { - let artist_text = $(this.grouping_handler.get_title_query(), this.metadb); - if (!artist_text) { - artist_text = is_radio ? 'Radio Stream' : '?'; - } - - if (artist_text) { - const artist_x = cur_x; - const artist_w = this.w - artist_x - (right_pad + 5); - const artist_h = this.h; - - const artist_text_format = g_string_format.v_align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - grClip.DrawString(artist_text, artist_font, artist_color, artist_x, 0, artist_w, artist_h, artist_text_format); - - cur_x += Math.ceil( - /** @type {!number} */ - gr.MeasureString(artist_text, artist_font, 0, 0, 0, 0).Width - ); - } - } - - // * Album - if (this.grouping_handler.get_sub_title_query()) { - // let album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); - let album_text = g_properties.show_disc_header ? $('[%album%]', this.metadb) : $(this.grouping_handler.get_sub_title_query(), this.metadb); - - if (album_text) { - album_text = ` - ${album_text}`; - - const album_h = this.h; - const album_x = cur_x; - const album_w = this.w - album_x - (right_pad + SCALE(40)); - - const album_text_format = g_string_format.v_align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - grClip.DrawString(album_text, g_pl_fonts.album, album_color, album_x, 0, album_w, album_h, album_text_format); - - // cur_x += gr.MeasureString(album_text, g_pl_fonts.album, 0, 0, 0, 0).Width; - } - } - - clipImg.ReleaseGraphics(grClip); - gr.DrawImage(clipImg, this.x, this.y, this.w, this.h, 0, 0, this.w, this.h, 0, 255); - - // * Callbacks for headerTooltip - this.artist_w_compact = this.w - cur_x - (right_pad + 5); - this.album_w_compact = this.w - cur_x - (right_pad + SCALE(40)); - this.artist_text_w_compact = gr.MeasureString($(this.grouping_handler.get_title_query(), this.metadb), artist_font, 0, 0, 0, 0).Width; - this.album_text_w_compact = gr.MeasureString($(this.grouping_handler.get_sub_title_query(), this.metadb), g_pl_fonts.album, 0, 0, 0, 0).Width; - } - - /** - * Assigns and resizes an image to the "art" property, based on certain conditions and constraints. - * @param {GdiBitmap} image The album art image that is being assigned to the `art` property. - */ - assign_art(image) { - if (!image || !g_properties.show_album_art) { - this.art = null; - return; - } - - if ((image.Height === this.art_max_size && image.Width <= this.art_max_size) - || (image.Height <= this.art_max_size && image.Width === this.art_max_size)) { - this.art = image; - } - else { - const ratio = image.Height / image.Width; - let art_h = this.art_max_size; - let art_w = this.art_max_size; - if (image.Height > image.Width) { - art_w = Math.round(art_h / ratio); - } - else { - art_h = Math.round(art_w * ratio); - } - - try { // Prevent crash if album art is corrupt, file format is not supported or has an unusual ICC profile embedded - this.art = image.Resize(art_w, art_h); - } catch (e) { - console.log('\n\n'); - } - } - - Header.art_cache.add_image_for_meta(this.art, this.metadb); - } - - /** - * Checks if the album art was sucessfully loaded. - * @returns {boolean} True or false. - */ - is_art_loaded() { - return this.art !== undefined; - } - - /** - * Initializes hyperlinks for various metadata fields such as date, artist, album, record labels, and genres. - * @param {GdiGraphics} gr - */ - initialize_hyperlinks(gr) { - const date_font = g_pl_fonts.date; - const artist_font = g_pl_fonts.artist_normal; - const art_box_x = 3 * SCALE(6); - const spacing = SCALE(2); - const art_box_size = this.art_max_size + spacing * 2; - const left_pad = SCALE(10) + (this.art !== null && g_properties.show_album_art && !g_properties.auto_album_art ? art_box_x + art_box_size : 0); - const right_edge = SCALE(20); - const part_h = this.h / 3; - const separatorWidth = gr.MeasureString(' \u2020', g_pl_fonts.info, 0, 0, 0, 0).Width; - const bulletWidth = Math.ceil(gr.MeasureString('\u2020', g_pl_fonts.info, 0, 0, 0, 0).Width); - const spaceWidth = Math.ceil(separatorWidth - bulletWidth) + SCALE(1); - - // * Date - const date_query = pref.showPlaylistFullDate ? tf.date : tf.year; - const date_text = $(date_query, this.metadb); - if (date_text) { - const date_w = Math.ceil(gr.MeasureString(date_text, date_font, 0, 0, 0, 0).Width + 5); - const date_x = -date_w - right_edge + (RES_4K ? 5 : 7); - const date_y = !g_properties.show_group_info ? part_h : part_h - (RES_4K ? pref.customThemeFonts ? 1 : 3 : pref.customThemeFonts ? -1 : 0) + - // Y-corrections - (RES_4K && headerFontSize === 20 || RES_4K && headerFontSize === 18 || RES_4K && headerFontSize === 13 || RES_4K && headerFontSize === 12 ? -1 : RES_4K && headerFontSize === 16 ? 1 : 0) + - (!RES_4K && headerFontSize < 15 ? 1 : 0) + (!RES_4K && headerFontSize === 20 ? 2 : 0); - - this.hyperlinks.date = new Hyperlink(date_text, date_font, 'date', date_x, date_y, this.w, true); - } - - // * Artist - const albumArtist = '%album artist%'; - const is_radio = this.metadb.RawPath.startsWith('http'); - let artist_text = []; - let artist_x = left_pad; - let artist_w; - let multi_artist_spacing_w = 0; - let multi_artist = false; - if (!is_radio) { - for (let i = 0; i < albumArtist.length; i++) { - artist_text.push(...GetMetaValues(albumArtist, this.metadb)); - } - artist_text = [...new Set(artist_text)]; // Remove duplicates - for (let i = 0; i < artist_text.length; i++) { - if (i > 0) { - artist_x += bulletWidth + spaceWidth * 3; // Spacing between multi artists - multi_artist_spacing_w = bulletWidth + spaceWidth * 3 * i; // Total spacing width - } - const single_artist_w = this.w - left_pad * 2; - const multi_artist_w = this.w - left_pad - artist_x; - const ellipsis_w = gr.MeasureString('...', artist_font, 0, 0, 0, 0).Width; - artist_w = gr.MeasureString(artist_text[i], artist_font, 0, 0, 0, 0).Width; - if (artist_text.length > 1) { - multi_artist = true; - if (artist_w > multi_artist_w) { - while (artist_w + ellipsis_w > multi_artist_w && artist_text[i].length > 0) { - artist_text[i] = artist_text[i].substring(0, artist_text[i].length - 1); - artist_w = gr.MeasureString(artist_text[i], artist_font, 0, 0, 0, 0).Width; - } - if (artist_text[i].length > 0) { - artist_text[i] += '...'; - artist_w += ellipsis_w; - } - } - } else { - artist_w = single_artist_w; - } - this.hyperlinks['artist' + i] = new Hyperlink(artist_text[i], artist_font, 'artist', artist_x, SCALE(5 * (!g_properties.show_group_info ? 2 : 1)), artist_w, true); - artist_x += artist_w; - } - } - - // * Album - const album_y = part_h * (!g_properties.show_group_info ? 1.5 : 1) + ((RES_4K || RES_QHD && headerFontSize === 17 ? 5 : 4) * (!g_properties.show_group_info ? 2 : 1)); - const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); - - if (album_text) { - this.hyperlinks.album = new Hyperlink(album_text, g_pl_fonts.album, 'album', left_pad, album_y, this.w - left_pad * 2, true); - } - - // * Record labels - let labels = []; - for (let i = 0; i < globals.labels.length; i++) { - labels.push(...GetMetaValues(globals.labels[i], this.metadb)); - } - labels = [...new Set(labels)]; // Remove duplicates - let label_left = -right_edge * 2 + (RES_4K ? 42 : 20); - const label_y = Math.round(2 * this.h / 3) - (RES_4K ? 4 : -1); - for (let i = labels.length - 1; i >= 0; --i) { - if (i !== labels.length - 1) { - label_left -= (bulletWidth + spaceWidth * 2); // Spacing between labels - } - const label_w = gr.MeasureString(labels[i], g_pl_fonts.info, 0, 0, 0, 0).Width; - label_left -= label_w; - this.hyperlinks['label' + i] = new Hyperlink(labels[i], g_pl_fonts.info, 'label', label_left, label_y, this.w, true); - } - - // * Genres - const genres = GetMetaValues('%genre%', this.metadb); - let genre_left = left_pad; - const genre_y = label_y; - for (let i = 0; i < genres.length; i++) { - if (i > 0) { - genre_left += bulletWidth + spaceWidth * 2; // Spacing between genres - } - const genre_w = gr.MeasureString(genres[i], g_pl_fonts.info, 0, 0, 0, 0).Width; - this.hyperlinks['genre' + i] = new Hyperlink(genres[i], g_pl_fonts.info, 'genre', genre_left, genre_y, this.w, true); - genre_left += genre_w; - } - - for (const h in this.hyperlinks) { - this.hyperlinks[h].set_y(this.y); - } - - // * Callbacks for headerTooltip - this.artist_text_w = gr.MeasureString(artist_text, artist_font, 0, 0, 0, 0).Width + (multi_artist ? multi_artist_spacing_w : 0); - this.album_text_w = gr.MeasureString(album_text, g_pl_fonts.album, 0, 0, 0, 0).Width; - this.max_w = this.w - left_pad * 2; - - // * Hyperlinks init done - this.hyperlinks_initialized = true; - } - - /** - * Handles mouse movement events and updates the state of hyperlinks based on the mouse position. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_move(x, y, m) { - let handled = false; - let needs_redraw = false; - - for (const h in this.hyperlinks) { - if (this.hyperlinks[h].trace(x - this.x, y)) { - if (this.hyperlinks[h].state !== HyperlinkStates.Hovered) { - this.hyperlinks[h].state = HyperlinkStates.Hovered; - needs_redraw = true; - } - handled = true; - } - else if (this.hyperlinks[h].state !== HyperlinkStates.Normal) { - this.hyperlinks[h].state = HyperlinkStates.Normal; - needs_redraw = true; - } - } - - if (needs_redraw) { - this.clearCachedHeaderImg(); - this.repaint(); - } - - return handled; - } - - /** - * Checks if the mouse click is within the boundaries of any hyperlinks and triggers their click event. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - on_mouse_lbtn_down(x, y, m) { - for (const h in this.hyperlinks) { - if (this.hyperlinks[h].trace(x - this.x, y)) { - this.hyperlinks[h].click(); - return true; - } - } - return false; - } - - /** - * Sets the x-coordinate for the ListItem object. - * @param {number} x The x-coordinate. - */ - set_x(x) { - ListItem.prototype.set_x.apply(this, [x]); - } - - /** - * Sets the y-coordinate for the ListItem object. - * @param {number} y The y-coordinate. - */ - set_y(y) { - ListItem.prototype.set_y.apply(this, [y]); - - for (const h in this.hyperlinks) { - this.hyperlinks[h].set_y(y); - } - } - - /** - * Sets the width of a list item and its sub-items, as well as the container width of any hyperlinks within the item. - * @param {number} w The width. - */ - set_w(w) { - this.reset_hyperlinks(); // Update hyperlinks container width when this.list_w changes, i.e when auto-hide scrollbar visiblity state changes - ListItem.prototype.set_w.apply(this, [w]); - - this.sub_items.forEach((item) => { - item.set_w(w); - }); - - for (const h in this.hyperlinks) { - this.hyperlinks[h].setContainerWidth(w); - } - - this.initialize_rating(); - } - - /** - * Resets the current hyperlinks. - */ - reset_hyperlinks() { - this.hyperlinks_initialized = false; - this.hyperlinks = {}; - } - - /** - * Clears the playlist header background image. - */ - clearCachedHeaderImg() { - this.header_image = null; - } - - /** - * Displays the playlist header tooltip when artist or album text is truncated. - */ - headerTooltip() { - if (!pref.showTooltipMain && !pref.showTooltipTruncated || displayCustomThemeMenu && displayBiography) { - return; - } - - const artist_text = $(this.grouping_handler.get_title_query(), this.metadb); - const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); - - if (this.artist_text_w > this.max_w || this.album_text_w > this.max_w || - g_properties.use_compact_header && (this.artist_text_w_compact > this.artist_w_compact || this.album_text_w_compact > this.album_w_compact)) { - tt.showDelayed(`${artist_text}\n${album_text}`); - } else { - tt.stop(); - } - } -} - - -/** - * Prepares the initialization data for the playlist header. - * @param {Array} rows_to_process The rows to process. - * @param {FbMetadbHandleList} rows_metadb The metadb of the rows to process. - * @returns {Array} An array of two elements, has the following format [Array<[row,row_data]>, disc_header_prepared_data]. - */ -Header.prepare_initialization_data = (rows_to_process, rows_metadb) => { - let query = Header.grouping_handler.get_query(); - if (g_properties.show_disc_header && query && Header.grouping_handler.show_disc()) { - query = query.replace(/%discnumber%/, '').replace(/%totaldiscs%/, '').replace(/%subtitle%/, ''); - } - - const tfo = fb.TitleFormat(query || ''); // Workaround a bug, because of which '' is sometimes treated as null :\ - const rows_data = tfo.EvalWithMetadbs(rows_metadb); - - const prepared_disc_data = g_properties.show_disc_header ? DiscHeader.prepare_initialization_data(rows_to_process, rows_metadb) : []; - - return [Zip(rows_to_process, rows_data), prepared_disc_data]; -}; - - -/** - * Creates the playlist header for each item. - * @param {PlaylistContent} parent The parent element. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {Array} prepared_rows The prepared rows, has the following format [Array<[row,row_data]>, disc_header_prepared_data]. - * @returns {Array
} An array of headers. - */ -Header.create_headers = (parent, x, y, w, h, prepared_rows) => { - const prepared_header_rows = prepared_rows[0]; - - let header_idx = 0; - const headers = []; - while (prepared_header_rows.length) { - const header = new Header(parent, x, y, w, h, header_idx); - const processed_rows_count = header.initialize_items(prepared_rows); - - TrimArray(prepared_header_rows, processed_rows_count, true); - - headers.push(header); - ++header_idx; - } - - return headers; -}; - - -//////////////////////// -// * HEADER ARTWORK * // -//////////////////////// -/** - * Debounces the getAlbumArtwork function. - * @type {function} - * @param {Array} items The items to get the album art for. - * @returns {Promise} A promise that resolves when the album art has been retrieved. - */ -const getAlbumArtDebounced = Debounce((items) => { - getHeaderArtwork(items); -}, 500, { - leading: false, - trailing: true -}); - - -/** - * Gets album art for playlist headers and is only executed if certain conditions are met. - * @param {Array
} items - */ -function getAlbumArt(items) { - if (!g_properties.show_album_art || g_properties.use_compact_header) { - return; - } - - getAlbumArtDebounced(items); -} - - -/** - * Checks if the album art is loaded for each item in the given array, and if not, - * retrieves the artwork asynchronously and assigns it to the item. Typically called in a debounce. - * @param {Header[]|Row[]} items - */ -function getHeaderArtwork(items) { - const headers = items.reduce((acc, item) => item instanceof Header ? [...acc, item] : acc, []); - for (const header of headers) { - const { metadb } = header.get_first_row(); - const isArtLoaded = header.is_art_loaded(); - const cached_art = Header.art_cache.get_image_for_meta(metadb); - - if (!isArtLoaded) { - if (cached_art) { - header.assign_art(cached_art); - } else { - // TODO: Once this has been better tested, remove on_get_album_art_done callback from this file, and probably gr-callbacks.js as well - // utils.GetAlbumArtAsync(window.ID, metadb, g_album_art_id.front); - if (!playlistThumbList.has(metadb)) { - playlistThumbList.add(metadb); - utils.GetAlbumArtAsyncV2(window.ID, metadb, g_album_art_id.front).then((artResult) => { - if (!header.is_art_loaded()) { - header.assign_art(artResult.image); - header.repaint(); - } - }); - } - } - } - } -} - - -/** - * Implements a cache for storing and retrieving images associated with metadata objects. - * @param {number} max_cache_size_arg The maximum number of images that can be stored in the cache. - * Once the cache reaches this size, the oldest images will be removed to make space for new ones. - * @class - */ -function ArtImageCache(max_cache_size_arg) { - /** @type {LinkedList} */ - const queue = new LinkedList(); - /** @type {Object} */ - let cache = {}; - - /** - * creates an object that stores metadata, an image, and a queue iterator. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @param {GdiBitmap} img The loaded image to cache. - * @param {LinkedList.Iterator} queue_iterator A reference to an iterator object that is - * used to iterate over a queue of items. It is likely used to keep track of the current position in - * the queue and to provide methods for accessing the next item in the queue. - * @class - */ - function CacheItem(metadb, img, queue_iterator) { - this.metadb = metadb; - this.img = img; - this.queue_iterator = queue_iterator; - } - - /** - * Gets the image from the cache. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @returns {?GdiBitmap} The image for the given metadb. - */ - this.get_image_for_meta = (metadb) => { - const cache_item = cache[metadb.Path]; - if (!cache_item) { - return undefined; // undefined means Not Loaded - } - - const img = cache_item.img; - move_item_to_top(cache_item); - - return img; - }; - - /** - * Adds the image to the cache. - * @param {GdiBitmap} img The image to add. - * @param {FbMetadbHandle} metadb The metadb of the track. - */ - this.add_image_for_meta = (img, metadb) => { - const cache_item = cache[metadb.Path]; - if (cache_item) { - cache_item.img = img; - move_item_to_top(cache_item); - } - else { - queue.push_front(metadb); - cache[metadb.Path] = new CacheItem(metadb, img, queue.begin()); - if (queue.length() > max_cache_size_arg) { - delete cache[queue.back().Path]; - queue.pop_back(); - } - } - }; - - /** - * Clears the cache and the queue. - * @returns {void} - */ - this.clear = () => { - cache = {}; - queue.clear(); - }; - - /** - * Moves an item to the top of the queue. - * @param {CacheItem} cache_item The item to move to the top. - */ - function move_item_to_top(cache_item) { - queue.remove(cache_item.queue_iterator); - queue.push_front(cache_item.metadb); - cache_item.queue_iterator = queue.begin(); - } -} - -/** @type {ArtImageCache} */ -Header.art_cache = new ArtImageCache(200); - - -///////////// -// * ROW * // -///////////// -/** - * Creates and draws the playlist rows. - */ -class Row extends ListItem { - /** - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @param {number} idx The playlist row index. - * @param {number} cur_playlist_idx_arg The current playlist index. - * @extends {ListItem} - * @class - */ - constructor(x, y, w, h, metadb, idx, cur_playlist_idx_arg) { - super(x, y, w, h); - - /** - * @const - * @type {number} - */ - this.idx = idx; - /** - * @const - * @type {FbMetadbHandle} - */ - this.metadb = metadb; - - // * Const after header creation - /** @type {boolean} */ - this.is_odd = false; - /** @type {number} */ - this.idx_in_header = undefined; - /** - * @const - * @type {BaseHeader} - */ - this.parent = undefined; - - /** @type {?number[]} */ - this.queue_indexes = undefined; - /** @type {number} */ - this.queue_idx_count = 0; - - // * State - this.is_playing = false; - this.is_focused = false; - this.is_drop_boundary_reached = false; - this.is_drop_bottom_selected = false; - this.is_drop_top_selected = false; - this.is_cropped = false; - - /** - * Playlist title color for row hover. - * @type {number} - */ - this.title_color = g_pl_colors.row_title_normal; - - /** - * @const - * @type {number} - */ - this.cur_playlist_idx = cur_playlist_idx_arg; - - /** - * @type {number} - */ - this.rating_left_pad = 0; - /** - * @type {number} - */ - this.rating_right_pad = 10; - /** @type {?Rating} */ - this.rating = undefined; - - /** @type {MetaHandler} */ - this.meta_handler = new MetaHandler(); - - /** @type {?string} */ - this.title_text = undefined; - /** @type {?string} */ - this.title_artist_text = undefined; - /** @type {?string} */ - this.count_text = undefined; - /** @type {?string} */ - this.length_text = undefined; - - this.initialize_rating(); - } - - // public: - - /** - * Draws the playlist rows, including the titles, artists, lengths, ratings, playcounts, and queue positions. - * @param {GdiGraphics} gr - */ - draw(gr) { - if (pref.panelWidthAuto && !g_properties.show_header) { - this.x = playlist.x; - } - - gr.SetSmoothingMode(SmoothingMode.None); - - if (this.is_odd && g_properties.show_row_stripes) { - gr.FillSolidRect(this.x, this.y, this.w, this.h, g_pl_colors.row_stripes_bg); - } - - let title_font = g_pl_fonts.title_normal; - const title_artist_font = g_pl_fonts.title_selected; - let title_artist_color = g_pl_colors.row_title_selected; - const scrollbar = g_properties.show_scrollbar && playlist.is_scrollbar_available; - - if (!pref.playlistRowHover) this.title_color = g_pl_colors.row_title_normal; - - if (this.is_selected()) { - this.title_color = g_pl_colors.row_title_selected; - title_font = g_pl_fonts.title_selected; - title_artist_color = g_pl_colors.row_title_normal; - - // if (g_properties.show_row_stripes) { // Don't need this!? - // // Last item is cropped - // const rect_h = this.is_cropped ? this.h - 1 : this.h; - // gr.DrawRect(this.x, this.y, this.w, rect_h, 1, g_pl_colors.row_selection_frame_cropped); - // } - - // if (pref.theme.startsWith('custom')) { - // gr.FillSolidRect(this.x, this.y, this.w, this.h, g_pl_colors.row_selection_bg); - // } - if (!this.is_playing) { // Do not draw selection on now playing to prevent 1px overlapping - gr.DrawRect(this.x, this.is_playing ? this.y - 1 : this.y, scrollbar ? this.w - (RES_4K ? 25 : 13) : this.w, this.h, 1, g_pl_colors.row_selection_frame); - // Hide DrawRect 1px gaps when all songs are completely selected - gr.DrawRect(this.x, this.is_playing ? this.y - 1 : this.y, SCALE(7), this.h, 1, g_pl_colors.row_sideMarker); - gr.FillSolidRect(this.x, this.y, SCALE(8), this.h, g_pl_colors.row_sideMarker); - } - } - - if (this.is_playing && g_pl_colors.row_nowplaying_bg !== '') { // * Wait until nowplaying bg has a new color to prevent flashing - this.title_color = g_pl_colors.row_title_playing; - title_font = g_pl_fonts.title_playing; - - const bg_color = g_pl_colors.row_nowplaying_bg; - gr.FillSolidRect(this.x, this.y, scrollbar ? this.w - SCALE(12) : this.w, this.h, bg_color); - if (ColorDistance(bg_color, title_artist_color) < 195) { - title_artist_color = this.title_color; - } - - if (lightBg && (pref.theme === 'white' && !pref.styleBlackAndWhite && !pref.styleBlackAndWhite2 || pref.theme === 'black')) { - this.title_color = RGB(20, 20, 20); - title_artist_color = RGB(0, 0, 0); - } - if (!lightBg && pref.theme === 'white' && !pref.styleBlackAndWhite && !pref.styleBlackAndWhite2 && !isStreaming && pref.layout === 'default') { - this.title_color = RGB(240, 240, 240); - title_artist_color = RGB(220, 220, 220); - } - if (pref.theme === 'white' && (pref.styleBlackAndWhite || pref.styleBlackAndWhite2) || !['white', 'black', 'cream'].includes(pref.theme)) { - gr.FillSolidRect(this.x, this.y, SCALE(8), this.h, g_pl_colors.row_sideMarker); - } - } - - //---> - if (this.is_drop_top_selected) { - gr.DrawLine(this.x, this.y + 1, this.x + this.w, this.y + 1, 2, this.is_drop_boundary_reached ? g_pl_colors.row_drag_line_reached : g_pl_colors.row_drag_line); - } - if (this.is_drop_bottom_selected) { - gr.DrawLine(this.x, this.y + this.h - 1, this.x + this.w, this.y + this.h - 1, 2, this.is_drop_boundary_reached ? g_pl_colors.row_drag_line_reached : g_pl_colors.row_drag_line); - } - - //////////////////////////////////////////////////////////// - - const is_radio = this.metadb.RawPath.startsWith('http'); - - const right_spacing = SCALE(20); - let cur_x = this.x + right_spacing; - let right_pad = SCALE(20); - const testRect = false; - - if ($('$ifgreater(%totaldiscs%,1,true,false)', this.metadb) !== 'false') { - cur_x += SCALE(0); - } - - // * LENGTH - { - if (this.length_text == null) { - this.length_text = $('[%length%]', this.metadb); - } - this.length_text = this.is_playing && pref.playlistTimeRemaining ? $('[-%playback_time_remaining%]') : $('[%length%]', this.metadb); - - const length_w = SCALE(60); - if (this.length_text) { - const length_x = this.x + this.w - length_w - right_pad; - - gr.DrawString(this.length_text, title_font, this.title_color, length_x, this.y, length_w, this.h, g_string_format.h_align_far | g_string_format.v_align_center); - testRect && gr.DrawRect(length_x, this.y - 1, length_w, this.h, 1, RGBA(155, 155, 255, 250)); - } - // We always want that padding - right_pad += Math.max(length_w, Math.ceil(gr.MeasureString(this.length_text, title_font, 0, 0, 0, 0).Width + 10)); - } - - // * RATING - if (g_properties.show_rating) { - this.rating.x = this.x + this.w - this.rating.w - right_pad; - this.rating.y = this.y; - this.rating.draw(gr, g_pl_colors.row_rating_color); - - right_pad += this.rating.w + this.rating_right_pad + this.rating_left_pad; - } - - // * PLR - if (g_properties.show_PLR) { - if ($('[%replaygain_track_gain%]', this.metadb) && $('[%replaygain_track_peak_db%]', this.metadb)) { - this.plr_track = this.meta_handler.get_PLR($('%replaygain_track_gain%', this.metadb), $('%replaygain_track_peak_db%', this.metadb)) - } - - if (this.plr_track) { - if (this.plr_track < 10) { - this.plr_track = ` ${this.plr_track}` - } - this.plr_track += ' LU |'; - - const plr_track_w = Math.ceil(gr.MeasureString(this.plr_track, g_pl_fonts.plr_track, 0, 0, 0, 0).Width); - const plr_track_x = this.x + this.w - plr_track_w - right_pad; - - gr.DrawString(this.plr_track, g_pl_fonts.plr_track, this.title_color, plr_track_x, this.y, plr_track_w, this.h, g_string_format.align_center); - testRect && gr.DrawRect(plr_track_x, this.y - 1, plr_track_w, this.h, 1, RGBA(155, 155, 255, 250)); - - right_pad = this.w - (plr_track_x - this.x) + 5; - } - } - - // * COUNT - if (g_properties.show_playcount) { - if (this.count_text == null) { - if (is_radio) { - this.count_text = ''; - } - else { - this.count_text = $('%play_count%', this.metadb); - if (this.count_text !== '0') { - this.count_text = $('[$max(%play_count%, %lastfm_play_count%)]', this.metadb); - this.count_text = !Number(this.count_text) ? '' : (`${this.count_text} |`); - } - else if (pref.lastFmScrobblesFallback) { - this.count_text = $('[$max(%lastfm_play_count%, %play_count%)]', this.metadb); - this.count_text = !Number(this.count_text) ? '' : (`${this.count_text} |`); - } - else { - // Don't want to show lastfm play count if track hasn't been played locally - this.count_text = ''; - } - } - } - - if (this.count_text) { - const count_w = Math.ceil( - /** @type {!number} */ - gr.MeasureString(this.count_text, g_pl_fonts.playcount, 0, 0, 0, 0).Width - ); - const count_x = this.x + this.w - count_w - right_pad; - - gr.DrawString(this.count_text, g_pl_fonts.playcount, this.title_color, count_x, this.y, count_w, this.h, g_string_format.align_center); - testRect && gr.DrawRect(count_x, this.y - 1, count_w, this.h, 1, RGBA(155, 155, 255, 250)); - - right_pad += count_w; - } - } - - // * QUEUE - const queueText = g_properties.show_queue_position && this.queue_indexes != null ? ` [${this.queue_indexes}]` : ''; - - // * TITLE init - if (this.title_text == null) { - const margin = !pref.showPlaylistTrackNumbers && !pref.showPlaylistIndexNumbers ? this.is_playing ? ' ' : '' : ' '; - const indexNumbers = this.idx < 9 ? `0${this.idx + 1}. ` : `${this.idx + 1}. `; - const trackNumbers = pref.showPlaylistIndexNumbers ? indexNumbers : `$if2(%tracknumber%,$pad_right(${this.idx_in_header + 1},2,0)). `; - const trackNumbersVinyl = pref.showPlaylistIndexNumbers ? indexNumbers : `$if2(${globals.vinyl_track},00. )`; - const trackNumberQuery = this.is_playing ? g_properties.show_header ? ' ' : pref.showVinylNums ? trackNumbersVinyl : trackNumbers : trackNumbers; - const showTrackNumber = pref.showPlaylistTrackNumbers || pref.showPlaylistIndexNumbers ? trackNumberQuery : ''; - const customTitle = settings.playlistCustomTitle; - const customTitleNoHeader = settings.playlistCustomTitleNoHeader; - - const titleQuery = - g_properties.show_header ? showTrackNumber + - (pref.showArtistPlaylistRows && pref.showAlbumPlaylistRows && customTitle === '' ? `${margin}%artist% - %album% - %title%[ '('%original artist%' cover)']` : - pref.showArtistPlaylistRows && customTitle === '' ? `${margin}%artist% - %title%[ '('%original artist%' cover)']` : - pref.showAlbumPlaylistRows && customTitle === '' ? `${margin}%album% - %title%[ '('%original artist%' cover)']` : - customTitle !== '' && !customTitle.includes('%tracknumber%') ? `${margin}${showTrackNumber}${customTitle}` : - customTitle !== '' ? `${margin}${customTitle}` : - `${margin}%title%[ '('%original artist%' cover)']`) : - customTitleNoHeader !== '' && !customTitleNoHeader.includes('%tracknumber%') ? `${margin} ${showTrackNumber} ${customTitleNoHeader}` : - customTitleNoHeader !== '' ? `${margin} ${customTitleNoHeader}` : - `${margin} %artist% - %album% - ${showTrackNumber} %title%[ '('%original artist%' cover)']`; - - this.title_text = (fb.IsPlaying && this.is_playing && is_radio) ? $(titleQuery) : $(titleQuery, this.metadb); - } - - // * TITLE ARTIST init - if (this.title_artist_text == null) { - const pattern = `^${$('%album artist%', this.metadb).replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')} `; - const regex = new RegExp(pattern); - this.title_artist_text = $(`[$if($strcmp(${tf.artist},%artist%),$if(%album artist%,$if(%track artist%,%track artist%,),),${tf.artist})]`, this.metadb); - if (this.title_artist_text.length) { - // if tf.artist evaluates to something different than %album artist% strip %artist% from the start of the string - // i.e. tf.artist = "Metallica feat. Iron Maiden" then we want this.title_artist_text = "feat. Iron Maiden" - this.title_artist_text = this.title_artist_text.replace(regex, ''); - this.title_artist_text = ` \u25AA ${this.title_artist_text}`; - } - } - - // * TITLE draw - { - const title_w = this.w - right_pad - SCALE(44); - const title_text_format = g_string_format.v_align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - DrawString(gr, this.title_text, title_font, this.title_color, cur_x, this.y, title_w, this.h, title_text_format); - if (this.is_playing) { - DrawString(gr, fb.IsPaused ? g_guifx.pause : g_guifx.play, ft.guifx, this.title_color, cur_x, this.y, title_w, this.h, title_text_format); - } - - testRect && gr.DrawRect(this.x, this.y - 1, title_w, this.h, 1, RGBA(155, 155, 255, 250)); - - cur_x += Math.ceil( - /** @type {!number} */ - gr.MeasureString(this.title_text, title_font, 0, 0, title_w, this.h, title_text_format | g_string_format.measure_trailing_spaces).Width - ); - } - - // * TITLE ARTIST draw - if (this.title_artist_text) { - const title_artist_x = cur_x; - const title_artist_w = this.w - (title_artist_x - this.x) - right_pad; - const title_artist_text_format = g_string_format.v_align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - - if (pref.showDifferentArtist) { - DrawString(gr, this.title_artist_text, title_artist_font, this.title_color, title_artist_x, this.y, title_artist_w, this.h, title_artist_text_format); - } - cur_x += Math.ceil( - /** @type {!number} */ - gr.MeasureString(this.title_artist_text, title_artist_font, 0, 0, title_artist_w, this.h, title_artist_text_format).Width - ); - } - - if (queueText) { - const queueX = cur_x; - const queueW = this.w - (queueX - this.x) - right_pad; - const queueTextFormat = g_string_format.v_align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - let queueColor = this.title_color; // col.primary; - - if (this.is_playing || ColorDistance(queueColor, g_pl_colors.row_stripes_bg) < 165) { - queueColor = this.title_color; - } - gr.DrawString(queueText, title_font, queueColor, queueX, this.y, queueW, this.h, queueTextFormat); - } - gr.SetSmoothingMode(SmoothingMode.HighQuality); - - // * Refresh playlist time remaining all the time when activated - let timeRemainingTimer = null; - if (pref.playlistTimeRemaining && this.is_playing && fb.IsPlaying) { - timeRemainingTimer = setTimeout(() => { - const length_w = SCALE(60); - const length_x = this.x + this.w - length_w; - window.RepaintRect(length_x + 5, this.y, length_w + 5, this.h); - }, 1000); - } else { - clearTimeout(timeRemainingTimer); - } - - // * Change and update title_color back to normal from previous mouse row hover state - this.title_color = g_pl_colors.row_title_normal; - - // * Callbacks for titleTooltip - this.title_w = this.w - right_pad - SCALE(44); - this.title_text_w = gr.MeasureString(this.title_text, title_font, 0, 0, 0, 0).Width; - } - - /** - * Sets the x-coordinate for the ListItem object and updates the x-coordinate for the rating property. - * @param {number} x The x-coordinate. - * @override - */ - set_x(x) { - ListItem.prototype.set_x.apply(this, [x]); - this.rating.x = x; - } - - /** - * Sets the y-coordinate for the ListItem object and updates the y-coordinatefor the rating property. - * @param {number} y The y-coordinate. - * @override - */ - set_y(y) { - ListItem.prototype.set_y.apply(this, [y]); - this.rating.y = y; - } - - /** - * Sets the width for the ListItem object and updates the width for the rating property. - * @param {number} w The width. - * @override - */ - set_w(w) { - ListItem.prototype.set_w.apply(this, [w]); - this.initialize_rating(); - } - - /** - * Resets the queried data for title, title artist, count, and length. - */ - reset_queried_data() { - this.title_text = undefined; - this.title_artist_text = undefined; - this.count_text = undefined; - this.length_text = undefined; - - // this.rating.reset_queried_data(); - } - - /** - * Checks if the rating is being traced. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - rating_trace(x, y) { - if (!g_properties.show_rating) { - return false; - } - return this.rating.trace(x, y); - } - - /** - * Handles mouse click events on the rating. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - rating_click(x, y) { - Assert(g_properties.show_rating, LogicError, 'Rating_click was called, when there was no rating object.\nShould use trace before calling click'); - this.rating.click(x, y); - } - - /** - * Checks if a playlist item is selected. - * @returns {boolean} True or false. - */ - is_selected() { - return plman.IsPlaylistItemSelected(this.cur_playlist_idx, this.idx); - } - - /** - * Initializes the rating and sets its position within a given area. - */ - initialize_rating() { - this.rating = new Rating(0, this.y, this.w - this.rating_right_pad, this.h, this.metadb); - this.rating.x = this.x + this.w - (this.rating.w + this.rating_right_pad); - } - - /** - * Clears the title text from an item in the playlist row. - */ - clear_title_text() { - this.title_text = null; - } - - /** - * Changes the playlist row text state when playlist item is hovered or pressed. - * @param {rowState} item The playlist row item. - */ - changeRowState(item) { - switch (item) { - case rowState.normal: { - this.title_color = g_pl_colors.row_title_normal; - break; - } - case rowState.hovered: { - this.title_color = g_pl_colors.row_title_hovered; - break; - } - case rowState.pressed: { - this.title_color = g_pl_colors.row_title_selected; - break; - } - } - } - - /** - * Checks if the mouse is on a playlist item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - trace(x, y) { - return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; - } - - /** - * Traces the mouse movement and changes the playlist text row state on an item. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - */ - on_mouse_move(x, y, m) { - if (pref.playlistRowHover && loadingThemeComplete) { - this.changeRowState(this.trace(x, y) ? rowState.hovered : rowState.normal); - } - } - - /** - * Displays the playlist row tooltip when title text is truncated. - */ - titleTooltip() { - if (!pref.showTooltipMain && !pref.showTooltipTruncated || displayCustomThemeMenu && displayBiography) { - return; - } - - const is_radio = this.metadb.RawPath.startsWith('http'); - const margin = ''; - const indexNumbers = this.idx < 9 ? `0${this.idx + 1}. ` : `${this.idx + 1}. `; - const trackNumbers = pref.showPlaylistIndexNumbers ? indexNumbers : `$if2(%tracknumber%,$pad_right(${this.idx_in_header + 1},2,0)). `; - const trackNumberQuery = pref.showVinylNums ? pref.showPlaylistIndexNumbers ? indexNumbers : globals.vinyl_track : trackNumbers; - const showTrackNumber = pref.showPlaylistTrackNumbers || pref.showPlaylistIndexNumbers ? trackNumberQuery : ''; - const customTitle = settings.playlistCustomTitle; - const customTitleNoHeader = settings.playlistCustomTitleNoHeader; - - const titleQuery = - g_properties.show_header ? showTrackNumber + - (pref.showArtistPlaylistRows && pref.showAlbumPlaylistRows && customTitle === '' ? `${margin}%artist% - %album% - %title%[ '('%original artist%' cover)']` : - pref.showArtistPlaylistRows && customTitle === '' ? `${margin}%artist% - %title%[ '('%original artist%' cover)']` : - pref.showAlbumPlaylistRows && customTitle === '' ? `${margin}%album% - %title%[ '('%original artist%' cover)']` : - customTitle !== '' && !customTitle.includes('%tracknumber%') ? `${margin}${showTrackNumber}${customTitle}` : - customTitle !== '' ? `${margin}${customTitle}` : - `${margin}%title%[ '('%original artist%' cover)']`) : - customTitleNoHeader !== '' && !customTitleNoHeader.includes('%tracknumber%') ? `${margin}${showTrackNumber}${customTitleNoHeader}` : - customTitleNoHeader !== '' ? `${margin}${customTitleNoHeader}` : - `%artist%$crlf()%album%$crlf()${showTrackNumber} %title%[ '('%original artist%' cover)']`; - - const title_text = (fb.IsPlaying && this.is_playing && is_radio) ? $(titleQuery) : $(titleQuery, this.metadb); - - if (this.title_text_w > this.title_w) { - tt.showDelayed(title_text); - } else { - tt.stop(); - } - } - - /** - * Updates and determines the color of the playlist row title text. - */ - update_title_color() { - const panelWhite = colBrightness > 210 && pref.styleRebornFusion || colBrightness2 > 210 && pref.styleRebornFusion2 || pref.styleBlackAndWhite2; - const panelBlack = colBrightness < 25 && pref.styleRebornFusion || colBrightness2 < 25 && pref.styleRebornFusion2 || pref.styleBlackAndWhite; - this.title_color = panelWhite ? RGB(80, 80, 80) : panelBlack ? RGB(200, 200, 200) : g_pl_colors.row_title_normal; - } -} - - -//////////////// -// * RATING * // -//////////////// -/** - * Displays rating in the playlist rows. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} max_w The maximum width. - * @param {number} h The height. - * @param {FbMetadbHandle} metadb The metadb of the track. - * @class - */ -function Rating(x, y, max_w, h, metadb) { - /** - * @const - * @type {number} - */ - const rowFontSize = pref[`playlistFontSize_${pref.layout}`]; - - /** - * @const - * @type {number} - */ - const btn_w = SCALE(rowFontSize + 2); - - /** - * @const - * @type {FbMetadbHandle} - */ - this.metadb = metadb; - - /** @type {number} */ - this.x = x; - /** @type {number} */ - this.y = y; - /** - * @const - * @type {number} - */ - this.w = Math.min(btn_w * 5, max_w); - /** - * @const - * @type {number} - */ - this.h = h; - - /** @type {?number} */ - let rating; // undefined - - /** - * Draws stars as rating in the playlist row. - * @param {GdiGraphics} gr - * @param {number} color The color of the stars. - */ - this.draw = function (gr, color) { - const cur_rating = this.get_rating(); - let cur_rating_x = this.x; - const y = this.y - (RES_4K ? 3 : 1); - - for (let j = 0; j < 5; j++) { - if (j < cur_rating) { - gr.DrawString('\u2605', g_pl_fonts.rating_set, loadingThemeComplete ? RGBA(0, 0, 0, 100) : color, cur_rating_x, y, btn_w + 1, this.h + 2, g_string_format.align_center); - gr.DrawString('\u2605', g_pl_fonts.rating_set, color, cur_rating_x, y, btn_w, this.h, g_string_format.align_center); - } - else if (pref.showPlaylistRatingGrid) { - gr.DrawString('\u2219', g_pl_fonts.rating_not_set, g_pl_colors.row_title_normal, cur_rating_x, y, btn_w, this.h, g_string_format.align_center); - } - cur_rating_x += btn_w; - } - }; - - /** - * Traces mouse movement and checks if mouse is within the boundaries of the clickable rating. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - this.trace = function (x, y) { - return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; - }; - - /** - * Sets rating in the playlist row rating area when double clicked. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - */ - this.click = function (x, y) { - if (!this.trace(x, y)) { - return; - } - - playlistAlbumRatings = new Map(); - const new_rating = Math.floor((x - this.x) / btn_w) + 1; - const current_rating = this.get_rating(); - - if (g_properties.use_rating_from_tags) { - if (!this.metadb.RawPath.startsWith('http')) { - const handle = new FbMetadbHandleList(); - handle.Add(this.metadb); - handle.UpdateFileInfoFromJSON( - JSON.stringify({ - RATING: (current_rating === new_rating) ? '' : new_rating - }) - ); - } - } - else { - fb.RunContextCommandWithMetadb(`Playback Statistics/Rating/${current_rating === new_rating ? '' : new_rating}`, this.metadb); - } - - rating = (current_rating === new_rating) ? 0 : new_rating; - }; - - /** - * Gets the rating for the current track. - * If no rating has been fetched yet, it fetches the rating using the get_track_rating method. - * @returns {number|null} The rating of the current track, or null if no rating. - */ - this.get_rating = () => { - const trackId = $('%rating%', this.metadb); - let rating = playlistTrackRatings.get(trackId); - - if (rating === undefined) { - rating = this.get_track_rating(this.metadb); - playlistTrackRatings.set(trackId, rating); - } - - return rating; - }; - - /** - * Gets the rating for a given track. - * If no track is provided, it defaults to the current playlist row. - * @param {FbMetadbHandle} [track=this.metadb] The track to get the rating for. - * @returns {number|null} The rating of the provided or default track, or null if no rating. - */ - this.get_track_rating = (track = this.metadb) => { - let currentRating; - - if (g_properties.use_rating_from_tags) { - const fileInfo = track.GetFileInfo(); - const ratingIdx = fileInfo.MetaFind('RATING'); - currentRating = ratingIdx !== -1 ? fileInfo.MetaValue(ratingIdx, 0) : 0; - } else { - currentRating = $('%rating%', track); - } - return currentRating === '' ? null : Number(currentRating); - } - - /** - * Gets the average rating for an album. - * @returns {number} The average rating of all tracks in the album. - */ - this.get_album_rating = () => { - // Return cached results if available - if (playlistAlbumRatings.size !== 0) { - return playlistAlbumRatings; - } - - const albums = new Map(); - const playlistItems = plman.GetPlaylistItems(plman.ActivePlaylist).Convert(); - - // Group tracks by album - for (let i = 0; i < playlistItems.length; ++i) { - const albumName = $('%album%', playlistItems[i]); - const rating = this.get_track_rating(playlistItems[i]); - - const albumData = albums.get(albumName); - if (albumData === undefined) { - albums.set(albumName, { albumTotalRating: rating, albumTrackCount: 1 }); - } else { - albumData.albumTotalRating += rating; - albumData.albumTrackCount++; - } - } - - // Calculate average rating for each album - for (const [albumName, albumData] of albums) { - const albumAverageRating = Number((albumData.albumTotalRating / albumData.albumTrackCount).toFixed(2)); - playlistAlbumRatings.set(albumName, albumAverageRating); - } - - return playlistAlbumRatings; - }; - - /** - * Resets and clears the current rating. - */ - this.reset_queried_data = () => { - rating = undefined; - }; -} - - -////////////////////// -// * META HANDLER * // -////////////////////// -/** - * Handles metadata operations including retrieving album metadata, and writing meta tags or playlist stats to a file. - */ -class MetaHandler { - /** - * @param {FbMetadbHandle} metadb The metadb of the track. - * @class - */ - constructor(metadb) { - this.metadb = metadb; - } - - /** - * Calculate Peak Loudness Ratio keeping in mind replayGain 2.0 is implemented in Foobar2000. - * Reference value in Foobar 2000 is set on -18 LUFS in order to maintain backwards compatibility with RG1, RG2. - * EBU R 128 reference is -23 LUFS. - * @param {string=} gain The ReplayGain gain value for track %replaygain_track_gain% | for album %replaygain_album_gain% - * @param {string=} peak The ReplayGain peak value for track %replaygain_track_peak_db% | for album %replaygain_album_peak_db% - * @returns {string=} Peak Loudness Ratio - */ - get_PLR(gain, peak) { - const lufs = -2300 - (Number(gain.replace(/[^0-9+-]/g, '')) - 500); - const tpfs = Number(peak.replace(/[^0-9+-]/g, '')); - const plr = tpfs - lufs; - const plr_value = plr % 100 > 49 ? plr + 100 : plr; - - return Math.floor(plr_value / 100); - } - - /** - * Gets the playcount for a given track. - * If no track is provided, it defaults to the current playlist row. - * @param {FbMetadbHandle} [track=this.metadb] The track to get the playcount for. - * @returns {number} The playcount of the provided or default track. - */ - get_track_playcount(track = this.metadb) { - let currentPlaycount; - - if (g_properties.use_rating_from_tags) { - const fileInfo = track.GetFileInfo(); - const ratingIdx = fileInfo.MetaFind('PLAY COUNT'); - currentPlaycount = ratingIdx !== -1 ? fileInfo.MetaValue(ratingIdx, 0) : 0; - } else { - currentPlaycount = $('%play_count%', track); - } - return currentPlaycount === '' ? null : Number(currentPlaycount); - } - - /** - * Iterates through the current active playlist and builds metadata for each album. - * @returns {Map} A map where keys are album names and values are objects with properties: - * - artist: The name of the artist of the album. - * - album: The name of the album. - * - year: The year of the album. - * - genre: The genre of the album. - * - label: The label the artist is signed to. - * - country: The country the artist is from. - * - albumTrackCount: The total number of tracks on the album. - * - albumTotalRating: The calculated total rating of all tracks on the album. - * - albumTotalPlaycount: The calculated total playcount of all tracks on the album. - * - albumAverageRating: The calculated average album rating. - * - albumAveragePlaycount: The calculated average album playcount. - * - tracks: An array of track objects, each with properties: - * - track: The track object from the playlist. - * - trackNumber: The track number. - * - title: The title of the track. - * - rating: The rating of the track. - * - playcount: The playcount of the track. - */ - get_metadata() { - const metadata = new Map(); - const getRating = new Rating(); - const playlistItems = plman.GetPlaylistItems(plman.ActivePlaylist).Convert(); - - for (let i = 0; i < playlistItems.length; ++i) { - const currentItem = playlistItems[i]; - const artist = $('$if2(%album artist%,[%artist%])', currentItem); - const album = $('%album%', currentItem); - const trackNumber = $('%tracknumber%', currentItem); - const title = $('%title%', currentItem); - const year = $('$if2(%year%,[%date%])', currentItem); - const genre = $('%genre%', currentItem); - const label = $('$if2(%label%,[%publisher%])', currentItem); - const country = $('$if2(%artistcountry%,[%country%])', currentItem); - const rating = getRating.get_track_rating(currentItem) || 0; - const playcount = this.get_track_playcount(currentItem) || 0; - - let albumData = metadata.get(album); - if (!albumData) { - albumData = { - artist, - album, - year, - genre, - label, - country, - albumTrackCount: 0, - albumTotalRating: 0, - albumTotalPlaycount: 0, - albumAverageRating: 0, - albumAveragePlaycount: 0, - tracks: [] - }; - metadata.set(album, albumData); - } - - albumData.albumTrackCount += 1; - albumData.albumTotalRating += rating; - albumData.albumTotalPlaycount += playcount; - - albumData.tracks.push({ - track: currentItem, - trackNumber, - title, - rating, - playcount - }); - } - - // * Calculate the averages after processing all tracks - metadata.forEach(albumData => { - albumData.albumAverageRating = albumData.albumTrackCount > 0 ? Number((albumData.albumTotalRating / albumData.albumTrackCount).toFixed(2)) : 0; - albumData.albumAveragePlaycount = albumData.albumTrackCount > 0 ? Math.round(albumData.albumTotalPlaycount / albumData.albumTrackCount) : 0; - }); - - return metadata; - } - - /** - * Gets metadata statistics for artists, albums, and tracks from the current active playlist. - * Sets default values where data is missing, computes various stats like ratings, counts, playcounts, - * and sorts them to find top rated and most played entries. - * @param {Object[]} metadata An array of metadata objects for various music entities. - * Each object contains artist, album, year, genre, label, country, and tracks information. - * @returns {Object} An object with aggregated metadata statistics, including: - * - Mappings of artists to genres, countries, and their ratings, counts, and playcounts. - * - Mappings of albums to artists, years, genres, labels, countries, and their ratings, counts, and playcounts. - * - Mappings of tracks to artists, albums, years, genres, labels, and their ratings, counts. - * - Sorted lists of top rated and most played artists, albums, tracks, genres, labels, and countries. - * - Total play counts for artists, albums, tracks, genres, labels, and countries. - */ - get_metadata_stats(metadata) { - const artistGenre = new Map(); - const artistCountry = new Map(); - - const albumArtist = new Map(); - const albumYear = new Map(); - const albumGenre = new Map(); - const albumLabel = new Map(); - const albumCountry = new Map(); - - const trackArtist = new Map(); - const trackAlbum = new Map(); - const trackYear = new Map(); - const trackGenre = new Map(); - const trackLabel = new Map(); - - const artistRatings = new Map(); - const albumRatings = new Map(); - const trackRatings = new Map(); - const artistCounts = new Map(); - const albumCounts = new Map(); - const trackCounts = new Map(); - - const artistPlaycounts = new Map(); - const albumPlaycounts = new Map(); - const trackPlaycounts = new Map(); - const genrePlaycounts = new Map(); - const labelPlaycounts = new Map(); - const countryPlaycounts = new Map(); - - const SetDefaultStringValue = (value, defaultValue) => (value && value !== '?') ? value : defaultValue; - const SetDefaultStringList = (value, defaultValue) => (value && value !== '?') ? value.split(',').map(item => item.trim()) : [defaultValue]; - const SetDefaultSet = (map, key) => map.has(key) || map.set(key, new Set()); - const SetDefaultVal = (map, key, value) => map.has(key) || map.set(key, value); - const SetDefaultNum = (map, key) => map.has(key) || map.set(key, 0); - - metadata.forEach(data => { - // * Set defaults - const artist = SetDefaultStringValue(data.artist, 'NO ARTIST'); - const album = SetDefaultStringValue(data.album, 'NO ALBUM'); - const year = SetDefaultStringValue(data.year, 'NO YEAR'); - const genre = SetDefaultStringList(data.genre, 'NO GENRE'); - const label = SetDefaultStringList(data.label, 'NO LABEL'); - const country = SetDefaultStringList(data.country, 'NO COUNTRY'); - - // * Set maps have entries for artist and album - SetDefaultSet(artistGenre, artist); - SetDefaultSet(artistCountry, artist); - SetDefaultVal(albumArtist, album, artist); - SetDefaultVal(albumYear, album, year); - SetDefaultSet(albumGenre, album); - SetDefaultSet(albumLabel, album); - SetDefaultSet(albumCountry, album); - - // * Set ratings, counts, and playcounts to 0 if they don't exist - SetDefaultNum(artistRatings, artist); - SetDefaultNum(albumRatings, album); - SetDefaultNum(artistCounts, artist); - SetDefaultNum(albumCounts, album); - SetDefaultNum(artistPlaycounts, artist); - SetDefaultNum(albumPlaycounts, album); - SetDefaultNum(labelPlaycounts, label); - SetDefaultNum(countryPlaycounts, country); - - // * Add genres to artist/album, labels to album, and countries to artist/album sets - genre.forEach(genreItem => { - artistGenre.get(artist).add(genreItem); - albumGenre.get(album).add(genreItem); - }); - label.forEach(labelItem => { - albumLabel.get(album).add(labelItem); - }); - country.forEach(countryItem => { - artistCountry.get(artist).add(countryItem); - albumCountry.get(album).add(countryItem); - }); - - // * Update all ratings, counts and playcounts - data.tracks.forEach(trackItem => { - const track = SetDefaultStringValue(trackItem.title, 'NO TRACK'); - SetDefaultVal(trackArtist, track, artist); - SetDefaultVal(trackAlbum, track, album); - SetDefaultVal(trackYear, track, year); - SetDefaultSet(trackGenre, track); - SetDefaultSet(trackLabel, track); - SetDefaultNum(trackRatings, track); - SetDefaultNum(trackCounts, track); - - artistRatings.set(artist, artistRatings.get(artist) + trackItem.rating); - albumRatings.set(album, albumRatings.get(album) + trackItem.rating); - trackRatings.set(track, (trackRatings.get(track) || 0) + trackItem.rating); - - artistCounts.set(artist, artistCounts.get(artist) + 1); - albumCounts.set(album, albumCounts.get(album) + 1); - trackCounts.set(track, (trackCounts.get(track) || 0) + 1); - - artistPlaycounts.set(artist, artistPlaycounts.get(artist) + trackItem.playcount); - albumPlaycounts.set(album, albumPlaycounts.get(album) + trackItem.playcount); - trackPlaycounts.set(track, (trackPlaycounts.get(track) || 0) + trackItem.playcount); - - if (genre.length === 0 || (genre.length === 1 && genre[0] === 'NO GENRE')) { - trackGenre.get(track).add('NO GENRE'); - } else { - genre.forEach(genreItem => { - trackGenre.get(track).add(genreItem); - genrePlaycounts.set(genreItem, (genrePlaycounts.get(genreItem) || 0) + trackItem.playcount); - }); - } - if (label.length === 0 || (label.length === 1 && label[0] === 'NO LABEL')) { - trackLabel.get(track).add('NO LABEL'); - } else { - label.forEach(labelItem => { - trackLabel.get(track).add(labelItem); - labelPlaycounts.set(labelItem, (labelPlaycounts.get(labelItem) || 0) + trackItem.playcount); - }); - } - country.forEach(countryItem => { - if (countryItem !== 'NO COUNTRY') { - countryPlaycounts.set(countryItem, (countryPlaycounts.get(countryItem) || 0) + trackItem.playcount); - } - }); - }); - }); - - // * Set top rated stats - const topRatedArtists = SortKeyValuesByAvg(artistRatings, artistCounts); - const topRatedAlbums = SortKeyValuesByAvg(albumRatings, albumCounts); - const topRatedTracks = SortKeyValuesByAvg(trackRatings, trackCounts); - - // * Set top played stats - const topPlayedArtists = SortKeyValuesByDsc(artistPlaycounts); - const topPlayedAlbums = SortKeyValuesByDsc(albumPlaycounts); - const topPlayedTracks = SortKeyValuesByDsc(trackPlaycounts); - const topPlayedGenres = SortKeyValuesByDsc(genrePlaycounts); - const topPlayedLabels = SortKeyValuesByDsc(labelPlaycounts); - const topPlayedCountries = SortKeyValuesByDsc(countryPlaycounts); - - // * Set total stats - const totalArtists = new Set(metadata.map(data => data.artist)).size; - const totalAlbums = new Set(metadata.map(data => data.album)).size; - const totalTracks = metadata.reduce((sum, data) => sum + data.tracks.length, 0); - const totalYears = new Set(metadata.map(data => data.year)).size; - const totalGenres = new Set(metadata.flatMap(data => data.genre)).size; - const totalLabels = new Set(metadata.map(data => data.label)).size; - const totalCountries = new Set(metadata.map(data => data.country)).size; - const totalRatings = metadata.reduce((sum, data) => sum + data.tracks.reduce((sum, track) => sum + track.rating, 0), 0); - const totalPlaycounts = metadata.reduce((sum, data) => sum + data.tracks.reduce((sum, track) => sum + track.playcount, 0), 0); - - const totalArtistPlays = [...artistPlaycounts.values()].reduce((acc, count) => acc + count, 0); - const totalAlbumPlays = [...albumPlaycounts.values()].reduce((acc, count) => acc + count, 0); - const totalTrackPlays = [...trackPlaycounts.values()].reduce((acc, count) => acc + count, 0); - const totalGenrePlays = [...genrePlaycounts.values()].reduce((acc, count) => acc + count, 0); - const totalLabelPlays = [...labelPlaycounts.values()].reduce((acc, count) => acc + count, 0); - const totalCountryPlays = [...countryPlaycounts.values()].reduce((acc, count) => acc + count, 0); - - // * Set best rated stats - const bestRatedArtist = GetKeyByHighestAvg(artistRatings, artistCounts); - const bestRatedAlbum = GetKeyByHighestAvg(albumRatings, albumCounts); - const bestRatedTrack = GetKeyByHighestAvg(trackRatings, trackCounts); - - // * Set most listened stats - const mostPlayedArtist = GetKeyByHighestVal(artistPlaycounts); - const mostPlayedAlbum = GetKeyByHighestVal(albumPlaycounts); - const mostPlayedTrack = GetKeyByHighestVal(trackPlaycounts); - const mostPlayedGenre = GetKeyByHighestVal(genrePlaycounts); - const mostPlayedLabel = GetKeyByHighestVal(labelPlaycounts); - const mostPlayedCountry = GetKeyByHighestVal(countryPlaycounts); - - const artistPlaycount = artistPlaycounts.get(mostPlayedArtist); - const albumPlaycount = albumPlaycounts.get(mostPlayedAlbum); - const trackPlaycount = trackPlaycounts.get(mostPlayedTrack); - const genrePlaycount = genrePlaycounts.get(mostPlayedGenre); - const labelPlaycount = labelPlaycounts.get(mostPlayedLabel); - const countryPlaycount = countryPlaycounts.get(mostPlayedCountry); - - const artistPercentage = (artistPlaycount / totalArtistPlays) * 100; - const albumPercentage = (albumPlaycount / totalAlbumPlays) * 100; - const trackPercentage = (trackPlaycount / totalTrackPlays) * 100; - const genrePercentage = (genrePlaycount / totalGenrePlays) * 100; - const labelPercentage = (labelPlaycount / totalLabelPlays) * 100; - const countryPercentage = (countryPlaycount / totalCountryPlays) * 100; - - return { - artistGenre, artistCountry, - albumArtist, albumYear, albumGenre, - albumLabel, albumCountry, - trackArtist, trackAlbum, trackYear, trackGenre, trackLabel, - - artistRatings, albumRatings, trackRatings, - artistCounts, albumCounts, trackCounts, - artistPlaycounts, albumPlaycounts, trackPlaycounts, genrePlaycounts, labelPlaycounts, countryPlaycounts, - - topRatedArtists, topRatedAlbums, topRatedTracks, - topPlayedArtists, topPlayedAlbums, topPlayedTracks, topPlayedGenres, topPlayedLabels, topPlayedCountries, - - totalArtists, totalAlbums, totalTracks, totalYears, totalGenres, totalLabels, totalCountries, totalRatings, totalPlaycounts, - totalArtistPlays, totalAlbumPlays, totalTrackPlays, totalGenrePlays, totalLabelPlays, totalCountryPlays, - - bestRatedArtist, bestRatedAlbum, bestRatedTrack, - - mostPlayedArtist, mostPlayedAlbum, mostPlayedTrack, mostPlayedGenre, mostPlayedLabel, mostPlayedCountry, - artistPlaycount, albumPlaycount, trackPlaycount, genrePlaycount, labelPlaycount, countryPlaycount, - artistPercentage, albumPercentage, trackPercentage, genrePercentage, labelPercentage, countryPercentage - }; - } - - /** - * Writes calculated %ALBUMRATING%, '%ALBUMPLAYCOUNT%' and '%ALBUMPLAYCOUNTTOTAL%' values to music files via the Playlist context menu. - * - '%ALBUMRATING%': The calculated average album rating, converted from a 0-5 scale to a 0-100 scale due to Foobar2000's incompatibility with floating point numbers when sorting. - * - '%ALBUMPLAYCOUNT%': The calculated average playcount of the album. - * - '%ALBUMPLAYCOUNTTOTAL%': The calculated total playcount of all tracks on the album. - */ - write_album_stats_to_tags() { - const metadata = this.get_metadata(); - const plItems = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); - const libItems = new FbMetadbHandleList(pop.getHandleList('newItems')); - const items = displayLibrary && !displayPlaylist || displayLibrarySplit() && state.mouse_x < ww * 0.5 ? libItems : plItems; - - if (!items || !items.Count) return; - - const albums = new Map(); - for (let i = 0; i < items.Count; i++) { - const albumName = $('%album%', items[i]); - if (!albums.has(albumName)) { - albums.set(albumName, []); - } - albums.get(albumName).push(items[i]); - } - - const albumUpdates = []; - for (const [albumName, items] of albums.entries()) { - const metadataEntry = metadata.get(albumName); - if (metadataEntry) { - const { albumAverageRating, albumAveragePlaycount, albumTotalPlaycount } = metadataEntry; - - if (albumAverageRating || albumAveragePlaycount || albumTotalPlaycount) { - const albumStats = {}; - if (albumAverageRating) { - // Convert albumAverageRating floating point number from scale 0-5 to an integer on scale 0-100. - // Foobar2000 does not handle floating point numbers well in its metadata fields when sorting, - // so we store the ratings as integers to ensure they are processed correctly. - const convertedRating = Math.round(albumAverageRating * 20); - albumStats.ALBUMRATING = convertedRating; - } - if (albumAveragePlaycount || albumTotalPlaycount) { - albumStats.ALBUMPLAYCOUNT = albumAveragePlaycount; - albumStats.ALBUMPLAYCOUNTTOTAL = albumTotalPlaycount; - } - - for (const item of items) { - albumUpdates.push(albumStats); - } - } - } - } - - if (albumUpdates.length) { - (items === libItems ? libItems : plItems).UpdateFileInfoFromJSON(JSON.stringify(albumUpdates)); - } - } - - /** - * Chooses and returns either the `rating` or `playcount` based on the `statsType`. - * The `statsType` string should be in the format 'property_subproperty'. - * If it has three parts ('property_subproperty_subproperty'), the third subproperty determines the return value. - * If `statsType` does not indicate 'rating' or 'trackPlaycount', the function defaults to returning the `rating`. - * @param {string} statsType The type of stats to return, expected to be either 'rating' or 'trackPlaycount'. - * @param {number} rating The rating value to return if `statsType` is 'rating' or invalid. - * @param {number} playcount The playcount value to return if `statsType` is 'trackPlaycount'. - * @returns {number} Either the `rating` or `playcount` value, based on the `statsType`. - */ - write_stats_choose_statistic_by_type(statsType, rating, playcount) { - const statsArray = statsType.split('_'); - const statsProperty = statsArray.length === 3 ? statsArray[2] : statsArray[0]; - return statsProperty === 'trackPlaycount' ? playcount : rating; - } - - /** - * Gets a sorting function based on the provided statistic type. - * The statistic type string should be in the format 'property_direction'. - * The property indicates the statistic to sort by, and the direction indicates the order ('asc' for ascending, 'dsc' for descending). - * @param {string} statsType The statistic type to sort by. - * @returns {(function(a: any, b: any): number)} A sorting function. - */ - write_stats_get_sorting(statsType) { - const sortMethods = { - artist: (a, b) => CompareValues(a.artist, b.artist), - albumTitle: (a, b) => CompareValues(a.album, b.album), - albumRating: (a, b) => CompareValues(a.albumAverageRating, b.albumAverageRating), - albumPlaycount: (a, b) => CompareValues(a.albumAveragePlaycount, b.albumAveragePlaycount), - albumPlaycountTotal: (a, b) => CompareValues(a.albumTotalPlaycount, b.albumTotalPlaycount), - albumTrackPlaycount: (a, b) => CompareValues(a.albumTotalPlaycount, b.albumTotalPlaycount), - trackTitle: (a, b) => CompareValues(a.track, b.track), - trackRating: (a, b) => CompareValues(a.rating, b.rating), - trackPlaycount: (a, b) => CompareValues(a.playcount, b.playcount), - year: (a, b) => CompareValues(a.year, b.year), - genre: (a, b) => CompareValues(a.genre, b.genre), - label: (a, b) => CompareValues(a.label, b.label), - country: (a, b) => CompareValues(a.country, b.country) - }; - - const [property, direction] = statsType.split('_'); - const sortMethod = sortMethods[property]; - - // Retrieve sortMethod from sortMethods with the given property. - // If 'dsc', sort descending by reversing arguments; otherwise, sort ascending. - return sortMethod && ((direction === 'dsc') ? (a, b) => sortMethod(b, a) : sortMethod); - } - - /** - * Generates a formatted string of total and top statistics from the current active playlist. - * @param {Object} metadata An object containing various statistics and counts. - * @param {string} statsType A string that indicates whether to return 'rating' or 'playcount' total statistics. - * @param {string} topStatsType A string that indicates whether to return 'topRated' or 'topPlayed' statistics. - * @returns {string} Returns a formatted string with the requested statistics. - */ - write_stats_total_top_stats(metadata, statsType, topStatsType) { - const rating = statsType.toLowerCase().includes('rating') || statsType.toLowerCase().includes('rated'); - const playcount = statsType.toLowerCase().includes('playcount') || statsType.toLowerCase().includes('played'); - let list = ''; - - list += 'Total statistics:\n' - + ` \u00B7 Artists: ${metadata.totalArtists}\n` - + ` \u00B7 Albums: ${metadata.totalAlbums}\n` - + ` \u00B7 Tracks: ${metadata.totalTracks}\n` - + ` \u00B7 Years: ${metadata.totalYears}\n` - + ` \u00B7 Genres: ${metadata.totalGenres}\n` - + ` \u00B7 Labels: ${metadata.totalLabels}\n` - + ` \u00B7 Countries: ${metadata.totalCountries}\n` - + (rating ? ` \u00B7 Ratings: ${metadata.totalRatings}\n` : '') - + (playcount ? ` \u00B7 Playcounts: ${metadata.totalPlaycounts}\n` : '') + '\n'; - - if (topStatsType === 'topRated') { - list += 'Top statistics:\n' - + ` \u00B7 Best rated artist: ${metadata.bestRatedArtist}\n` - + ` \u00B7 Best rated album: ${metadata.bestRatedAlbum}\n` - + ` \u00B7 Best rated track: ${metadata.bestRatedTrack}\n\n\n`; - } - - if (topStatsType === 'topPlayed') { - list += 'Top statistics:\n' - + ` \u00B7 Most played artist: ${metadata.mostPlayedArtist} - ${metadata.artistPlaycount} plays (${metadata.artistPercentage.toFixed(2)}%)\n` - + ` \u00B7 Most played album: ${metadata.mostPlayedAlbum} - ${metadata.albumPlaycount} plays (${metadata.albumPercentage.toFixed(2)}%)\n` - + ` \u00B7 Most played track: ${metadata.mostPlayedTrack} - ${metadata.trackPlaycount} plays (${metadata.trackPercentage.toFixed(2)}%)\n` - + ` \u00B7 Most played genre: ${metadata.mostPlayedGenre} - ${metadata.genrePlaycount} plays (${metadata.genrePercentage.toFixed(2)}%)\n` - + ` \u00B7 Most played label: ${metadata.mostPlayedLabel} - ${metadata.labelPlaycount} plays (${metadata.labelPercentage.toFixed(2)}%)\n` - + ` \u00B7 Most played country: ${metadata.mostPlayedCountry} - ${metadata.countryPlaycount} plays (${metadata.countryPercentage.toFixed(2)}%)\n\n\n`; - } - - return list; - } - - /** - * Writes top statistics list for: - * - Top rated artists - * - Top rated albums - * - Top rated tracks - * - Top played artists - * - Top played albums - * - Top played tracks - * - Top played genres - * - Top played labels - * - Top played countries - * - * It provides a detailed ranked list from top to bottom for each category from the current active playlist. - * @param {Array} metadata An array of objects, each object representing track metadata with all provided properties. - * @param {boolean} topRated Writes the top rated artist and albums as the list. - * @param {boolean} topPlayed Writes the top played artists, albums, genres, labels and countries as the list. - * @returns {string} A string formatted for display, containing the top statistics. - */ - write_stats_top_list(metadata, topRated, topPlayed) { - const includeArtist = g_properties.playlist_stats_include_artist; - const includeAlbum = g_properties.playlist_stats_include_album; - const includeYear = g_properties.playlist_stats_include_year; - const includeGenre = g_properties.playlist_stats_include_genre; - const includeLabel = g_properties.playlist_stats_include_label; - const includeCountry = g_properties.playlist_stats_include_country; - const includeStats = g_properties.playlist_stats_include_stats; - - const data = this.get_metadata_stats(metadata); - let list = ''; - - // * Top rated lists - if (topRated) { - list += `${WriteFancyHeader('Top rated artists')}\n`; - data.topRatedArtists.forEach((artist, index) => { - const country = includeCountry ? Array.from(data.artistCountry.get(artist) || []).join(', ') : ''; - const genre = includeGenre ? Array.from(data.artistGenre.get(artist) || []).join(', ') : ''; - const include = country || genre ? ` (${country}${country && genre ? ' \u00B7 ' : ''}${genre})` : ''; - const average = data.artistRatings.get(artist) / data.artistCounts.get(artist); - const stats = includeStats ? `: ${average.toFixed(2)}` : ''; - - list += `${index + 1}: ${artist}${include}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top rated albums')}\n`; - data.topRatedAlbums.forEach((album, index) => { - const year = includeYear && data.albumYear.get(album) ? `${data.albumYear.get(album)}` : ''; - const genreSet = data.albumGenre.get(album); - const genre = includeGenre && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; - const labelSet = data.albumLabel.get(album); - const label = includeLabel && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; - const includeParts = [year, genre, label].filter(part => part !== ''); - const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; - const artist = includeArtist && data.albumArtist.get(album) ? ` - ${data.albumArtist.get(album)}` : ''; - const average = data.albumRatings.get(album) / data.albumCounts.get(album); - const stats = includeStats ? `: ${average.toFixed(2)}` : ''; - - list += `${index + 1}: ${album}${include}${artist}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top rated tracks')}\n`; - data.topRatedTracks.forEach((track, index) => { - const album = includeAlbum && data.trackAlbum.get(track) ? ` - ${data.trackAlbum.get(track)}` : ''; - const year = includeYear && data.trackYear.get(track) ? `${data.trackYear.get(track)}` : ''; - const genreSet = data.trackGenre.get(track); - const genre = includeGenre && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; - const labelSet = data.trackLabel.get(track); - const label = includeLabel && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; - const includeParts = [year, genre, label].filter(part => part !== ''); - const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; - const artist = includeArtist && data.trackArtist.get(track) ? ` - ${data.trackArtist.get(track)}` : ''; - const average = data.trackRatings.get(track); - const stats = includeStats ? `: ${average.toFixed(2)}` : ''; - - list += `${index + 1}: ${track}${album}${include}${artist}${stats}\n`; - }); - } - - // * Top played lists - if (topPlayed) { - list += `${WriteFancyHeader('Top played artists')}\n`; - data.topPlayedArtists.forEach((artist, index) => { - const country = includeCountry ? Array.from(data.artistCountry.get(artist) || []).join(', ') : ''; - const genre = includeGenre ? Array.from(data.artistGenre.get(artist) || []).join(', ') : ''; - const include = country || genre ? ` (${country}${country && genre ? ' \u00B7 ' : ''}${genre})` : ''; - const playcount = data.artistPlaycounts.get(artist); - const percentage = (playcount / data.totalArtistPlays) * 100; - const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; - - list += `${index + 1}: ${artist}${include}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top played albums')}\n`; - data.topPlayedAlbums.forEach((album, index) => { - const year = includeYear && data.albumYear.get(album) ? data.albumYear.get(album) : ''; - const genreSet = data.albumGenre.get(album); - const genre = includeGenre && genreSet && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; - const labelSet = data.albumLabel.get(album); - const label = includeLabel && labelSet && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; - const includeParts = [year, genre, label].filter(part => part !== ''); - const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; - const artist = includeArtist && data.albumArtist.get(album) ? ` - ${data.albumArtist.get(album)}` : ''; - const playcount = data.albumPlaycounts.get(album); - const percentage = (playcount / data.totalAlbumPlays) * 100; - const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; - - list += `${index + 1}: ${album}${include}${artist}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top played tracks')}\n`; - data.topPlayedTracks.forEach((track, index) => { - const album = includeAlbum && data.trackAlbum.get(track) ? ` - ${data.trackAlbum.get(track)}` : ''; - const year = includeYear && data.trackYear.get(track) ? data.trackYear.get(track) : ''; - const genreSet = data.trackGenre.get(track); - const genre = includeGenre && genreSet && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; - const labelSet = data.trackLabel.get(track); - const label = includeLabel && labelSet && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; - const includeParts = [year, genre, label].filter(part => part !== ''); - const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; - const artist = includeArtist && data.trackArtist.get(track) ? ` - ${data.trackArtist.get(track)}` : ''; - const playcount = data.trackPlaycounts.get(track); - const percentage = (playcount / data.totalAlbumPlays) * 100; - const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; - - list += `${index + 1}: ${track}${album}${include}${artist}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top played genres')}\n`; - data.topPlayedGenres.forEach((genre, index) => { - const playcount = data.genrePlaycounts.get(genre); - const percentage = (playcount / data.totalGenrePlays) * 100; - const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; - list += `${index + 1}: ${genre}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top played labels')}\n`; - data.topPlayedLabels.forEach((label, index) => { - const playcount = data.labelPlaycounts.get(label); - if (playcount <= 0) return; - const percentage = (playcount / data.totalLabelPlays) * 100; - const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; - list += `${index + 1}: ${label}${stats}\n`; - }); - list += '\n\n'; - - list += `${WriteFancyHeader('Top played countries')}\n`; - data.topPlayedCountries.forEach((country, index) => { - const playcount = data.countryPlaycounts.get(country); - if (playcount <= 0) return; - const percentage = (playcount / data.totalCountryPlays) * 100; - const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; - list += `${index + 1}: ${country}${stats}\n`; - }); - } - - return list; - } - - /** - * Writes various statistics for the current playlist to a text file. - * @param {Array} metadata The metadata array of objects with properties depending on metadataType. - * @param {string} metadataType The type of metadata: 'album', 'track', 'topRated', or 'topPlayed'. - * @param {string} filePath The path to the text file where statistics will be written. - * @param {string} statsName The name of the statistic type. - * @param {string} statsType The statistic type to be used for sorting. - * @param {string} ratingType The rating type, one of 'albumAverage', 'albumTotal', or 'albumTracks'. - * @param {string} playcountType The playcount type, one of 'albumAverage', 'albumTotal', or 'albumTracks'. - * @returns {boolean} True if writing to the text file was successful, false otherwise. - */ - write_stats_to_text_file(metadata, metadataType, filePath, statsName, statsType, ratingType, playcountType) { - const includeArtist = g_properties.playlist_stats_include_artist; - const includeAlbum = g_properties.playlist_stats_include_album; - const includeTrack = g_properties.playlist_stats_include_track; - const includeYear = g_properties.playlist_stats_include_year; - const includeGenre = g_properties.playlist_stats_include_genre; - const includeLabel = g_properties.playlist_stats_include_label; - const includeCountry = g_properties.playlist_stats_include_country; - const includeStats = g_properties.playlist_stats_include_stats; - - const metadataStats = this.get_metadata_stats(metadata); - const playlistStats = includeStats ? this.write_stats_total_top_stats(metadataStats, statsType, metadataType) : '\n'; - const playlistName = plman.GetPlaylistName(plman.ActivePlaylist); - const playlistTitle = `${playlistName} - ${statsName} statistics${metadataType.startsWith('top') ? '' : ` - sorted by ${statsType}`}`; - const playlistData = `${WriteFancyHeader(playlistTitle)}\n\n${playlistStats}`; - - const sortFunction = this.write_stats_get_sorting(statsType); - - // * Albums - const albumMetadataSorted = sortFunction ? metadata.sort(sortFunction) : metadata; - const albumMetadata = albumMetadataSorted.map((metadata) => { - const separator = includeArtist && includeAlbum ? ' - ' : ''; - const artist = includeArtist ? metadata.artist ? `${metadata.artist}${separator}` : 'NO ARTIST' : ''; - const album = includeAlbum ? metadata.album ? `${metadata.album}` : 'NO ALBUM' : ''; - const albumTracksRating = includeTrack && metadata.tracks ? `\n${metadata.tracks.map(trackRating => ` ${trackRating.trackNumber}. ${trackRating.title}${includeStats ? `: ${trackRating.rating}` : ''}`).join('\n')}` : ''; - const albumTracksPlaycount = includeTrack && metadata.tracks ? `\n${metadata.tracks.map(trackPlaycount => ` ${trackPlaycount.trackNumber}. ${trackPlaycount.title}${includeStats ? `: ${trackPlaycount.playcount}` : ''}`).join('\n')}` : ''; - const year = includeYear ? metadata.year ? `${metadata.year}` : 'NO YEAR' : ''; - const genre = includeGenre ? metadata.genre && metadata.genre.trim() !== '' && metadata.genre.trim() !== '?' ? `${metadata.genre}` : 'NO GENRE' : ''; - const label = includeLabel ? metadata.label ? `${metadata.label}` : 'NO LABEL' : ''; - const country = includeCountry ? metadata.country ? `${metadata.country}` : 'NO COUNTRY' : ''; - const includeParts = [year, genre, label, country].filter(part => part !== ''); - const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; - - const ratingTypeMeta = { - albumAverage: includeStats && metadata.albumAverageRating, - albumTotal: includeStats && metadata.albumTotalRating, - albumTracks: albumTracksRating - }; - const rating = `: ${ratingTypeMeta[ratingType]}`; - const ratingAvg = includeStats ? `: ${metadata.albumAverageRating}` : ''; - - const playcountTypeMeta = { - albumAverage: includeStats && metadata.albumAveragePlaycount, - albumTotal: includeStats && metadata.albumTotalPlaycount, - albumTracks: albumTracksPlaycount - }; - const playcount = `: ${playcountTypeMeta[playcountType]}`; - const playcountAvg = includeStats ? `: ${metadata.albumAveragePlaycount}` : ''; - - if (ratingType) { - return ratingType === 'albumTracks' ? - `${artist}${album}${include}${ratingAvg}${rating}\n` : - `${artist}${album}${include}${rating}`; - } - else if (playcountType) { - return playcountType === 'albumTracks' ? - `${artist}${album}${include}${playcountAvg}${playcount}\n` : - `${artist}${album}${include}${playcount}`; - } - else { - return `${artist}${album}${include}`; - } - }).join('\n'); - - // * Tracks - const trackMetadataMap = metadata.flatMap(album => - album.tracks.map(track => ({ - artist: album.artist, - album: album.album, - trackNumber: track.trackNumber, - track: track.title, - year: album.year, - genre: album.genre, - label: album.label, - country: album.country, - rating: track.rating, - playcount: track.playcount - })) - ); - - const trackMetadataSorted = sortFunction ? trackMetadataMap.sort(sortFunction) : trackMetadataMap; - const trackMetadata = trackMetadataSorted.map((trackData) => { - const track = includeTrack ? (trackData.trackNumber && trackData.track ? `${trackData.trackNumber}. ${trackData.track}` : 'NO TRACK') : ''; - const album = includeAlbum ? (trackData.album && trackData.album.trim() !== '' ? `${trackData.album}` : 'NO ALBUM') : ''; - const artist = includeArtist ? (trackData.artist && trackData.artist.trim() !== '' ? `${trackData.artist}` : 'NO ARTIST') : ''; - const year = includeYear ? (trackData.year && trackData.year.trim() !== '' && trackData.year.trim() !== '?' ? `${trackData.year}` : 'NO YEAR') : ''; - const genre = includeGenre ? (trackData.genre && trackData.genre.trim() !== '' && trackData.genre.trim() !== '?' ? `${trackData.genre}` : 'NO GENRE') : ''; - const label = includeLabel ? (trackData.label && trackData.label.trim() !== '' && trackData.label.trim() !== '?' ? `${trackData.label}` : 'NO LABEL') : ''; - const country = includeCountry ? (trackData.country && trackData.country.trim() !== '' && trackData.country.trim() !== '?' ? `${trackData.country}` : 'NO COUNTRY') : ''; - const includeParts = [year, genre, label, country].filter(part => part !== ''); - const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; - const rating = includeStats ? `: ${trackData.rating}` : ''; - const playcount = includeStats ? `: ${trackData.playcount}` : ''; - const separator1 = includeTrack && includeAlbum ? ' - ' : ''; - const separator2 = includeAlbum && includeArtist || includeTrack && includeArtist ? ' - ' : ''; - - return `${track}${separator1}${album}${include}${separator2}${artist}${this.write_stats_choose_statistic_by_type(statsType, rating, playcount)}`; - }).join('\n'); - - // * Write the data to text file - const data = playlistData + ( - metadataType === 'track' ? trackMetadata : - metadataType === 'album' ? albumMetadata : - metadataType === 'topRated' ? this.write_stats_top_list(metadata, true, false) : - metadataType === 'topPlayed' ? this.write_stats_top_list(metadata, false, true) : '' - ); - - return Save(filePath, data); - } -} - - -/////////////////////////// -// * SELECTION HANDLER * // -/////////////////////////// -/** - * Handles selection and manipulation of playlist items. - * @param {PlaylistContent} cnt_arg The playlist content. - * @param {number} cur_playlist_idx_arg The current playlist index. - * @class - */ -function SelectionHandler(cnt_arg, cur_playlist_idx_arg) { - /** - * @const - * @type {ContentNavigationHelper} - */ - const cnt_helper = cnt_arg.helper; - - /** - * @const - * @type {Array} - */ - // @ts-ignore - const rows = cnt_arg.rows; - - /** - * @const - * @type {number} - */ - const cur_playlist_idx = cur_playlist_idx_arg; - - /** @type {Array} */ - let selected_indexes = []; - /** @type {?number} */ - let last_single_selected_index; // undefined - - /** @type {boolean} */ - let is_dragging = false; - /** @type {boolean} */ - let is_internal_drag_n_drop_active = false; - /** @type {?Row} */ - let last_hover_row; // undefined - - // private: - const that = this; - - /** - * Initializes the selection state. - */ - this.initialize_selection = () => { - selected_indexes = []; - rows.forEach((item, i) => { - if (plman.IsPlaylistItemSelected(cur_playlist_idx, item.idx)) { - selected_indexes.push(i); - } - }); - }; - - /** - * Updates the selection state. - * @param {Row|BaseHeader} item The item to update the selection state for. - * @param {boolean=} [ctrl_pressed=false] Whether CTRL key is pressed. - * @param {boolean=} [shift_pressed=false] Whether SHIFT key is pressed. - */ - this.update_selection = (item, ctrl_pressed, shift_pressed) => { - if (!item) { - return; - } - Assert(item != null, LogicError, 'update_selection was called with undefined item'); - - if (!ctrl_pressed && !shift_pressed) { - selected_indexes = []; - last_single_selected_index = undefined; - } - - const visible_item = cnt_helper.is_item_visible(item) ? item : cnt_helper.get_visible_parent(item); - if (visible_item instanceof BaseHeader) { - update_selection_with_header(visible_item, ctrl_pressed, shift_pressed); - } - else { - update_selection_with_row(/** @type {Row}*/ visible_item, ctrl_pressed, shift_pressed); - } - - selected_indexes.sort(numeric_ascending_sort); - }; - - /** - * Selects all playlist items. - */ - this.select_all = () => { - if (!rows.length) { - return; - } - - selected_indexes = Range(rows[0].idx, Last(rows).idx + 1); - last_single_selected_index = rows[0].idx; - - plman.SetPlaylistSelection(cur_playlist_idx, selected_indexes, true); - }; - - /** - * Clears current selection on the playlist item. - */ - this.clear_selection = () => { - if (!selected_indexes.length) { - return; - } - selected_indexes = []; - last_single_selected_index = undefined; - plman.ClearPlaylistSelection(cur_playlist_idx); - }; - - /** - * Whether there are any selected playlist items. - * @returns {boolean} True if any items are selected. - */ - this.has_selected_items = () => !!selected_indexes.length; - - /** - * Gets the total number of selected playlist items. - * @returns {number} The number of selected items. - */ - this.selected_items_count = () => selected_indexes.length; - - /** - * Gets the indexes of selected playlist items. - * @returns {number} The indexes of the selected items. - */ - this.get_selected_items = () => selected_indexes; - - /** - * Performs internal drag and drop of the selected playlist items inside the panel, i.e when reordering. - */ - this.perform_internal_drag_n_drop = function () { - this.enable_drag(); - is_internal_drag_n_drop_active = true; - - const cur_playlist_size = plman.PlaylistItemCount(cur_playlist_idx); - const cur_playlist_selection = plman.GetPlaylistSelectedItems(cur_playlist_idx); - const cur_selected_indexes = selected_indexes; - - const effect = fb.DoDragDrop(window.ID, cur_playlist_selection, g_drop_effect.copy | g_drop_effect.move | g_drop_effect.link); - - is_internal_drag_n_drop_active = false; - - if (is_dragging) { - // If drag operation was not cancelled, then it means that nor on_drag_drop, nor on_drag_leave event handlers - // were triggered, which means that the items were most likely dropped inside the panel - // (and relevant methods were not called because of async event processing) - return; - } - - /** - * Checks if two arrays are equal in terms of length and the values at each index. - * @param {Array} a The first array. - * @param {Array} b The second array. - * @returns {boolean} True if the arrays are equal, false otherwise. - */ - function arraysEqual(a, b) { - if (a === b) return true; - if (a == null || b == null || a.length !== b.length) return false; - - for (let i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) return false; - } - return true; - } - - /** - * Checks if the playlist is in the same state and if the selected indexes are equal. - * This is necessary to ensure that we can handle the 'move drop' properly. - * @returns {boolean} True or false. - */ - function can_handle_move_drop() { - // We can handle the 'move drop' properly only when playlist is still in the same state - return cur_playlist_size === plman.PlaylistItemCount(cur_playlist_idx) - && arraysEqual(cur_selected_indexes, selected_indexes); - } - - if (g_drop_effect.none === effect && can_handle_move_drop()) { - // DROPEFFECT_NONE needs special handling, because on NT it - // is returned for some move operations, instead of DROPEFFECT_MOVE. - // See Q182219 for the details. - - const items_to_remove = []; - const playlist_items = plman.GetPlaylistItems(cur_playlist_idx); - cur_selected_indexes.forEach((idx) => { - const cur_item = playlist_items[idx]; - if (cur_item.RawPath.startsWith('file://') && !fso.FileExists(cur_item.Path)) { - items_to_remove.push(idx); - } - }); - - if (items_to_remove.length) { - plman.ClearPlaylistSelection(cur_playlist_idx); - plman.SetPlaylistSelection(cur_playlist_idx, items_to_remove, true); - plman.RemovePlaylistSelection(cur_playlist_idx); - } - } - else if (g_drop_effect.move === effect && can_handle_move_drop()) { - plman.RemovePlaylistSelection(cur_playlist_idx); - } - }; - - /** - * Enables dragging. - */ - this.enable_drag = () => { - clear_last_hover_row(); - is_dragging = true; - }; - - /** - * Disables dragging. - */ - this.disable_drag = () => { - clear_last_hover_row(); - is_dragging = false; - }; - - /** - * Enables external dragging. - */ - this.enable_external_drag = function () { - this.enable_drag(); - is_internal_drag_n_drop_active = false; - }; - - /** - * Checks whether dragging is active. - * @returns {boolean} True or false. - */ - this.is_dragging = () => is_dragging; - - /** - * Checks whether the internal drag and drop is active. - * @returns {boolean} True or false. - */ - this.is_internal_drag_n_drop_active = () => is_internal_drag_n_drop_active; - - /** - * Updates the hover row's drop state, also calls repaint. - * @param {?Row} hover_row The hover row. - * @param {boolean} is_above Whether the hover row is above the dragged item. - */ - this.drag = (hover_row, is_above, idx) => { - if (hover_row == null) { - clear_last_hover_row(); - return; - } - - if (plman.IsPlaylistLocked(cur_playlist_idx)) { - return; - } - - let is_drop_top_selected = is_above; - let is_drop_bottom_selected = !is_above; - const is_drop_boundary_reached = hover_row.idx === 0 || (!is_above && hover_row.idx === rows.length - 1); - - if (is_internal_drag_n_drop_active && !utils.IsKeyPressed(VK_CONTROL)) { - // Can't move selected item on itself - const is_item_above_selected = hover_row.idx !== 0 && rows[hover_row.idx - 1].is_selected(); - const is_item_below_selected = hover_row.idx !== (rows.length - 1) && rows[hover_row.idx + 1].is_selected(); - is_drop_top_selected = is_drop_top_selected && !hover_row.is_selected() && !is_item_above_selected; - is_drop_bottom_selected = is_drop_bottom_selected && !hover_row.is_selected() && !is_item_below_selected; - } - - let needs_repaint = false; - if (last_hover_row) { - if (last_hover_row.idx === hover_row.idx) { - needs_repaint = last_hover_row.is_drop_top_selected !== is_drop_top_selected - || last_hover_row.is_drop_bottom_selected !== is_drop_bottom_selected - || last_hover_row.is_drop_boundary_reached !== is_drop_boundary_reached; - } - else { - clear_last_hover_row(); - needs_repaint = true; - } - } - else { - needs_repaint = true; - } - - hover_row.is_drop_top_selected = is_drop_top_selected; - hover_row.is_drop_bottom_selected = is_drop_bottom_selected; - hover_row.is_drop_boundary_reached = is_drop_boundary_reached; - - if (needs_repaint) { - hover_row.repaint(); - } - - last_hover_row = hover_row; - playlistDropIndex = is_drop_bottom_selected ? hover_row.idx + 1 : hover_row.idx; // * Needed for librarySplitDragDrop - }; - - /** - * Checks whether the playlist is in a state where it can accept a drop. - * @returns {boolean} True or false. - */ - this.can_drop = () => { - let playlistIndex = false; - return () => { - if (plman.PlaylistCount > 0 && cur_playlist_idx >= 0 && cur_playlist_idx < plman.PlaylistCount && !plman.IsPlaylistLocked(cur_playlist_idx)) { - return true; - } else { - if (!playlistIndex) { // If no playlist exists, create a new one. - const playlist_idx = plman.CreatePlaylist(0, 'Default'); - plman.ActivePlaylist = playlist_idx; - playlistIndex = true; - } - return false; - } - }; - }; - - /** - * Handles a drop event. - * @param {boolean} copy_selection Whether to copy the selection instead of moving it. - */ - this.drop = (copy_selection) => { - if (!is_dragging) { - return; - } - - is_dragging = false; - if (!selected_indexes.length || !last_hover_row) { - return; - } - - if (!last_hover_row.is_drop_top_selected && !last_hover_row.is_drop_bottom_selected) { - clear_last_hover_row(); - return; - } - - let drop_idx = last_hover_row.idx; - if (last_hover_row.is_drop_bottom_selected) { - ++drop_idx; - } - - clear_last_hover_row(); - - if (!copy_selection) { - move_selection(drop_idx); - } - else { - plman.UndoBackup(cur_playlist_idx); - - const cur_selection = plman.GetPlaylistSelectedItems(cur_playlist_idx); - plman.ClearPlaylistSelection(cur_playlist_idx); - plman.InsertPlaylistItems(cur_playlist_idx, drop_idx, cur_selection, true); - plman.SetPlaylistFocusItem(cur_playlist_idx, drop_idx); - } - }; - - /** - * Handles external drag and drop. - * @param {DropTargetAction} action The drag and drop action. - */ - this.external_drop = function (action) { - plman.ClearPlaylistSelection(cur_playlist_idx); - - let playlist_idx; - if (!plman.PlaylistCount) { - playlist_idx = plman.CreatePlaylist(0, 'Default'); - plman.ActivePlaylist = playlist_idx; - } - else { - playlist_idx = cur_playlist_idx; - plman.UndoBackup(cur_playlist_idx); - } - - action.Playlist = playlist_idx; - action.ToSelect = true; - - if (last_hover_row) { - let drop_idx = last_hover_row.idx; - if (last_hover_row.is_drop_bottom_selected) { - ++drop_idx; - } - action.Base = drop_idx; - } - else { - action.Base = plman.PlaylistCount; - } - - this.disable_drag(); - }; - - /** - * Copies the selected playlist items to the clipboard. - */ - this.copy = () => { - if (!selected_indexes.length) { - return fb.CreateHandleList(); - } - - const selected_items = plman.GetPlaylistSelectedItems(cur_playlist_idx); - fb.CopyHandleListToClipboard(selected_items); - }; - - /** - * Cuts the selected playlist items to the clipboard. - */ - this.cut = () => { - if (!selected_indexes.length) { - return fb.CreateHandleList(); - } - - const selected_items = plman.GetPlaylistSelectedItems(cur_playlist_idx); - - if (fb.CopyHandleListToClipboard(selected_items)) { - plman.UndoBackup(cur_playlist_idx); - plman.RemovePlaylistSelection(cur_playlist_idx); - } - }; - - /** - * Pastes the contents of the clipboard to the playlist. - */ - this.paste = () => { - if (!fb.CheckClipboardContents()) { - return; - } - - const metadb_list = fb.GetClipboardContents(window.ID); - if (!metadb_list || !metadb_list.Count) { - return; - } - - let insert_idx; - if (selected_indexes.length) { - insert_idx = is_selection_contiguous() ? Last(selected_indexes) + 1 : plman.GetPlaylistFocusItemIndex(cur_playlist_idx) + 1; - } - else { - const focused_idx = plman.GetPlaylistFocusItemIndex(cur_playlist_idx); - insert_idx = (focused_idx !== -1) ? (focused_idx + 1) : rows.length; - } - - plman.UndoBackup(cur_playlist_idx); - plman.ClearPlaylistSelection(cur_playlist_idx); - plman.InsertPlaylistItems(cur_playlist_idx, insert_idx, metadb_list, true); - plman.SetPlaylistFocusItem(cur_playlist_idx, insert_idx); - }; - - /** - * Sends the selected playlist items to the specified playlist. - * @param {number} playlist_idx The current playlist index. - */ - this.send_to_playlist = (playlist_idx) => { - plman.UndoBackup(playlist_idx); - plman.ClearPlaylistSelection(playlist_idx); - plman.InsertPlaylistItems(playlist_idx, plman.PlaylistItemCount(playlist_idx), plman.GetPlaylistSelectedItems(cur_playlist_idx), true); - }; - - /** - * Moves the selection up one row. - */ - this.move_selection_up = () => { - if (!selected_indexes.length) { - return; - } - - move_selection(Math.max(0, selected_indexes[0] - 1)); - }; - - /** - * Moves the selection down one row. - */ - this.move_selection_down = () => { - if (!selected_indexes.length) { - return; - } - - move_selection(Math.min(rows.length, Last(selected_indexes) + 2)); - }; - - /** - * Updates the selection state of the playlist according to the given row. - * @param {Row} row The row to update the selection state for. - * @param {boolean} ctrl_pressed Whether or not the Ctrl key is pressed. - * @param {boolean} shift_pressed Whether or not the Shift key is pressed. - */ - function update_selection_with_row(row, ctrl_pressed, shift_pressed) { - if (shift_pressed) { - selected_indexes = get_shift_selection(row.idx); - - // plman.ClearPlaylistSelection(cur_playlist_idx); // * Disabled to enable contiguous Ctrl+shift selection - plman.SetPlaylistSelection(cur_playlist_idx, selected_indexes, true); - } - else if (ctrl_pressed) { - const is_selected = selected_indexes.find((idx) => row.idx === idx); - - if (is_selected) { - selected_indexes = selected_indexes.filter(idx => idx !== row.idx); - } - else { - selected_indexes.push(row.idx); - } - - last_single_selected_index = row.idx; - - plman.SetPlaylistSelectionSingle(cur_playlist_idx, row.idx, !is_selected); - } - else { - selected_indexes.push(row.idx); - last_single_selected_index = row.idx; - - plman.ClearPlaylistSelection(cur_playlist_idx); - plman.SetPlaylistSelectionSingle(cur_playlist_idx, row.idx, true); - } - - plman.SetPlaylistFocusItem(cur_playlist_idx, row.idx); - } - - /** - * Updates the selection state of the playlist according to the given header. - * @param {BaseHeader} header The header to update the selection state for. - * @param {boolean} ctrl_pressed Whether or not the Ctrl key is pressed. - * @param {boolean} shift_pressed Whether or not the Shift key is pressed. - */ - function update_selection_with_header(header, ctrl_pressed, shift_pressed) { - const row_indexes = header.get_row_indexes(); - - if (shift_pressed) { - selected_indexes = Union(get_shift_selection(row_indexes[0]), row_indexes); - } - else { - if (ctrl_pressed) { - const is_selected = Difference(row_indexes, selected_indexes).length === 0; - if (is_selected) { - that.clear_selection(); // _.pullAll(selected_indexes, row_indexes); - } - else { - selected_indexes = Union(selected_indexes, row_indexes); - } - } - else { - selected_indexes = row_indexes; - } - last_single_selected_index = row_indexes[0]; - } - - plman.ClearPlaylistSelection(cur_playlist_idx); - plman.SetPlaylistSelection(cur_playlist_idx, selected_indexes, true); - if (row_indexes.length) { - plman.SetPlaylistFocusItem(cur_playlist_idx, row_indexes[0]); - } - } - - /** - * Gets the indexes of the rows that should be selected when the Shift key is pressed. - * @param {number} selected_idx The index of the row that was selected. - * @returns {Range} The range of indexes that should be selected. - */ - function get_shift_selection(selected_idx) { - let a = 0; - let b = 0; - - if (last_single_selected_index == null) { - last_single_selected_index = plman.GetPlaylistFocusItemIndex(cur_playlist_idx); - if (last_single_selected_index === -1) { - last_single_selected_index = 0; - } - } - - if (cnt_helper.is_item_visible(rows[last_single_selected_index])) { - if (last_single_selected_index < selected_idx) { - a = last_single_selected_index; - b = selected_idx; - } - else { - a = selected_idx; - b = last_single_selected_index; - } - } - else { - const last_selected_header = cnt_helper.get_visible_parent(rows[last_single_selected_index]); - if (last_single_selected_index < selected_idx) { - a = last_selected_header.get_row_indexes()[0]; - b = selected_idx; - } - else { - a = selected_idx; - b = Last(last_selected_header.get_row_indexes()); - } - } - - return Range(a, b + 1); - } - - /** - * Clears the last hover row. - */ - function clear_last_hover_row() { - if (last_hover_row) { - last_hover_row.is_drop_bottom_selected = false; - last_hover_row.is_drop_top_selected = false; - last_hover_row.is_drop_boundary_reached = false; - last_hover_row.repaint(); - } - } - - /** - * Moves the selection to the given index. - * @param {number} new_idx The new index of the selection. - */ - function move_selection(new_idx) { - plman.UndoBackup(cur_playlist_idx); - let move_delta; - - if (is_selection_contiguous()) { - const focus_idx = plman.GetPlaylistFocusItemIndex(cur_playlist_idx); - - move_delta = new_idx < focus_idx ? -(selected_indexes[0] - new_idx) : new_idx - (Last(selected_indexes) + 1); - } - else { - const item_count_before_drop_idx = selected_indexes.filter(idx => idx < new_idx).length; - - move_delta = -(plman.PlaylistItemCount(cur_playlist_idx) - selected_indexes.length - (new_idx - item_count_before_drop_idx)); - - // Move to the end to make it contiguous, then back to drop_idx - plman.MovePlaylistSelection(cur_playlist_idx, plman.PlaylistItemCount(cur_playlist_idx)); - } - plman.MovePlaylistSelection(cur_playlist_idx, move_delta); - } - - /** - * Checks if the selection is contiguous. - * @returns {boolean} True or false. - */ - function is_selection_contiguous() { - return selected_indexes.every((item, i) => { - if (i !== 0 && (selected_indexes[i] - selected_indexes[i - 1]) !== 1) { - return false; - } - return true; - }); - } - - /** - * Sorts an array of numbers in ascending order. - * @param {number} a The first number to compare. - * @param {number} b The second number to compare. - * @returns {number} The difference between `a` and `b`. - */ - function numeric_ascending_sort(a, b) { - return (a - b); - } - - this.initialize_selection(); -} - - -////////////////////////// -// * COLLAPSE HANDLER * // -////////////////////////// -/** - * @param {PlaylistContent} cnt_arg - * @class - */ -function CollapseHandler(cnt_arg) { - /** @type {boolean} */ - this.changed = false; - - const that = this; - - /** @type {Array} */ - let headers = cnt_arg.sub_items; - /** @type {?function} */ - let on_collapse_change_callback; // undefined - - /** - * Callback for when the content changes. - * @param {Object} cnt_arg The content argument. - */ - this.on_content_change = () => { - headers = cnt_arg.sub_items; - this.changed = false; - - if (g_properties.show_header && g_properties.collapse_on_playlist_switch) { - if (g_properties.auto_collapse) { - this.collapse_all_but_now_playing(); - } - else { - this.collapse_all(); - } - } - }; - - /** - * Toggles the collapse state of a playlist item. - * @param {BaseHeader} item The item to toggle. - */ - this.toggle_collapse = function (item) { - this.changed = true; - set_collapsed_state_recursive(item, !item.is_collapsed); - - trigger_callback(); - }; - - /** - * Collapses a playlist item. - * @param {BaseHeader} item The item to collapse. - */ - this.collapse = function (item) { - this.changed = set_collapsed_state_recursive(item, true); - - trigger_callback(); - }; - - /** - * Expands a playlist item. - * @param {BaseHeader} item The item to expand. - */ - this.expand = function (item) { - this.changed = set_collapsed_state_recursive(item, false); - - trigger_callback(); - }; - - /** - * Collapses all playlist items. - */ - this.collapse_all = function () { - this.changed = false; - headers.forEach((item) => { - this.changed = set_collapsed_state_recursive(item, true) || this.changed; - }); - - trigger_callback(); - }; - - /** - * Collapses all playlist items except the now playing track. - */ - this.collapse_all_but_now_playing = function () { - this.changed = false; - headers.forEach((item) => { - if (item.is_playing()) { - this.changed = set_collapsed_state_recursive(item, false) || this.changed; - return; - } - this.changed = set_collapsed_state_recursive(item, true) || this.changed; - }); - - trigger_callback(); - }; - - /** - * Expands all playlist items. - */ - this.expand_all = function () { - this.changed = false; - headers.forEach((item) => { - this.changed = set_collapsed_state_recursive(item, false) || this.changed; - }); - - trigger_callback(); - }; - - /** - * Sets the callback that will be called when the collapse state of a playlist item changes. - * @param {function} on_collapse_change_callback_arg The callback. - */ - this.set_callback = (on_collapse_change_callback_arg) => { - on_collapse_change_callback = on_collapse_change_callback_arg; - }; - - /** - * Triggers the callback if the collapse state has changed. - * @private - */ - function trigger_callback() { - if (that.changed && on_collapse_change_callback) { - on_collapse_change_callback(); - } - } - - /** - * Sets the collapsed state of the header and all its sub-items recursively. - * @param {BaseHeader} header The header to set the collapsed state of. - * @param {boolean} is_collapsed Whether the header should be collapsed or not. - * @returns {boolean} Whether the collapsed state of any header was changed. - */ - function set_collapsed_state_recursive(header, is_collapsed) { - let changed = header.is_collapsed !== is_collapsed; - header.is_collapsed = is_collapsed; - - const sub_items = header.sub_items; - if (sub_items[0] instanceof Row) { - return changed; - } - - sub_items.forEach(item => { - changed = set_collapsed_state_recursive(item, is_collapsed) || changed; - }); - - return changed; - } -} - - -/////////////////////// -// * QUEUE HANDLER * // -/////////////////////// -/** - * Manages the playback queue for a playlist by adding, removing, and checking the status of queued items. - */ -class QueueHandler { - /** - * @param {Array} rows_arg - * @param {number} cur_playlist_idx_arg - * @class - */ - constructor(rows_arg, cur_playlist_idx_arg) { - /** - * @const - * @type {number} - */ - this.cur_playlist_idx = cur_playlist_idx_arg; - /** - * @const - * @type {Array} - */ - this.rows = rows_arg; - - /** @type {Array} */ - this.queued_rows = []; - - this.initialize_queue(); - } - - /** - * Initializes the playback queue contents and adding the queued items to the appropriate rows. - */ - initialize_queue() { - if (this.queued_rows.length) { - this.reset_queued_status(); - } - - const queue_contents = plman.GetPlaybackQueueContents(); - if (!queue_contents.length) { - return; - } - - queue_contents.forEach((queued_item, i) => { - if (queued_item.PlaylistIndex !== this.cur_playlist_idx || queued_item.PlaylistItemIndex === -1) { - return; - } - - const cur_queued_row = this.rows[queued_item.PlaylistItemIndex]; - if (!cur_queued_row) return; - const has_row = this.queued_rows.find(queued_row => queued_row.idx === cur_queued_row.idx); - - if (!has_row) { - cur_queued_row.queue_indexes = [i + 1]; - cur_queued_row.queue_idx_count = 1; - } - else { - cur_queued_row.queue_indexes.push(i + 1); - cur_queued_row.queue_idx_count++; - } - - this.queued_rows.push(cur_queued_row); - }); - } - - /** - * Adds a playlist item to the playback queue. - * @param {Row} row - */ - add_row(row) { - if (!row) { - return; - } - - plman.AddPlaylistItemToPlaybackQueue(this.cur_playlist_idx, row.idx); - } - - /** - * Removes a row from the playback queue. - * @param {Row} row - */ - remove_row(row) { - if (!row) { - return; - } - - const idx = plman.FindPlaybackQueueItemIndex(row.metadb, this.cur_playlist_idx, row.idx); - if (idx !== -1) { - plman.RemoveItemFromPlaybackQueue(idx); - } - } - - /** - * Flushes the playback queue. - */ - flush() { - plman.FlushPlaybackQueue(); - } - - /** - * Checks if there are any playlist items in the playback queue. - * @returns {boolean} True or false. - */ - has_items() { - return !!plman.GetPlaybackQueueHandles().Count; - } - - /** - * Resets the queue indexes and count for each item in the `queued_rows` array and clears the array. - */ - reset_queued_status() { - if (!this.queued_rows.length) { - return; - } - - this.queued_rows.forEach((item) => { - item.queue_indexes = undefined; - item.queue_idx_count = 0; - }); - - this.queued_rows = []; - } -} - - -////////////////////////// -// * GROUPING HANDLER * // -////////////////////////// -/** - * Manages the grouping settings and behavior for playlists. - * @class - */ -function GroupingHandler() { - /** - * @const - * @type {GroupingHandler.Settings} - */ - const settings = new GroupingHandler.Settings(); - - /** @type {?Array} */ - let playlists = []; - /** @type {string} */ - let cur_playlist_name = ''; - /** @type {?GroupingHandler.Settings.Group} */ - let cur_group; // undefined - /** @type {?Array} */ - let group_by_name; - - /** - * Callback for when the playlists have changed. - * @private - */ - this.on_playlists_changed = () => { - const playlist_count = plman.PlaylistCount; - const new_playlists = []; - for (let i = 0; i < playlist_count; ++i) { - new_playlists.push(plman.GetPlaylistName(i)); - } - - let save_needed = false; - - if (playlists.length > playlist_count) { - // Removed - - const playlists_to_remove = Difference(playlists, new_playlists); - playlists_to_remove.forEach((playlist_name) => { - delete settings.playlist_group_data[playlist_name]; - delete settings.playlist_custom_group_data[playlist_name]; - }); - - save_needed = true; - } - else if (playlists.length === playlist_count) { - // May be renamed? - - const playlist_difference_new = Difference(new_playlists, playlists); - const playlist_difference_old = Difference(playlists, new_playlists); - if (playlist_difference_old.length === 1) { - // playlist_difference_new.length > 0 and playlist_difference_old.length === 0 means that - // playlists contained multiple items of the same name (one of which was changed) - const old_name = playlist_difference_old[0]; - const new_name = playlist_difference_new[0]; - - const group_name = settings.playlist_group_data[old_name]; - const custom_group = settings.playlist_custom_group_data[old_name]; - - settings.playlist_group_data[new_name] = group_name; - if (custom_group) { - settings.playlist_custom_group_data[new_name] = custom_group; - } - - delete settings.playlist_group_data[old_name]; - delete settings.playlist_custom_group_data[old_name]; - - save_needed = true; - } - } - - playlists = new_playlists; - if (save_needed) { - settings.save(); - } - }; - - /** - * Sets the active playlist. - * @param {string} cur_playlist_name_arg The name of the playlist to make active. - */ - this.set_active_playlist = (cur_playlist_name_arg) => { - cur_playlist_name = cur_playlist_name_arg; - let group_name = settings.playlist_group_data[cur_playlist_name]; - - cur_group = null; - if (group_name) { - if (group_name === 'user_defined') { - cur_group = settings.playlist_custom_group_data[cur_playlist_name]; - } - else if (group_by_name.includes(group_name)) { - cur_group = settings.group_presets[group_by_name.indexOf(group_name)]; - } - - if (!cur_group) { - delete settings.playlist_group_data[cur_playlist_name]; - group_name = ''; - } - } - - if (!cur_group) { - group_name = settings.default_group_name; - cur_group = settings.group_presets[group_by_name.indexOf(group_name)]; - } - - Assert(cur_group != null, ArgumentError, 'group_name', group_name); - }; - - /** - * Gets the query for the active playlist. - * @returns {string} The query for the active playlist. - */ - this.get_query = () => cur_group.group_query; - - /** - * Gets the title query for the active playlist. - * @returns {string} The title query for the active playlist. - */ - this.get_title_query = () => cur_group.title_query; - - /** - * Gets the sub-title query for the active playlist. - * @returns {string} The sub-title query for the active playlist. - */ - this.get_sub_title_query = () => cur_group.sub_title_query; - - /** - * Gets the name of the current query. - * @returns {string} The name of the current query. - */ - this.get_query_name = () => cur_group.name; - - /** - * Whether to show the disc for the current query. - * @returns {boolean} True or false. - */ - this.show_disc = () => cur_group.show_disc; - - /** - * Whether to show the date for the current query. - * @returns {boolean} True or false. - */ - this.show_date = () => cur_group.show_date; - - /** - * Appends the menu to the given parent menu. - * @param {ContextMenu} parent_menu The parent menu to append to. - * @param {function} on_execute_callback_fn The callback function to call when an item is executed. - */ - this.append_menu_to = (parent_menu, on_execute_callback_fn) => { - const group = new ContextMenu('Grouping'); - parent_menu.append(group); - - group.append_item('Manage presets', () => { - manage_groupings(on_execute_callback_fn); - }); - - group.append_separator(); - - group.append_item('Reset to default', () => { - delete settings.playlist_custom_group_data[cur_playlist_name]; - delete settings.playlist_group_data[cur_playlist_name]; - - cur_group = settings.group_presets[group_by_name.indexOf(settings.default_group_name)]; - - settings.save(); - settings.send_sync(); - settings.load(); - - config.addConfigurationObject(themePlaylistGroupingPresetsSchema, themePlaylistGroupingPresets); - config.writeConfiguration(); - - on_execute_callback_fn(); - }); - - group.append_separator(); - - let group_by_text = 'by...'; - if (cur_group.name === 'user_defined') { - group_by_text += ` [${this.get_query()}]`; - } - group.append_item(group_by_text, () => { - request_user_query(on_execute_callback_fn); - }, { is_radio_checked: cur_group.name === 'user_defined' }); - - settings.group_presets.forEach((group_item) => { - let group_by_text = group_item.description; - if (group_item.name === settings.default_group_name) { - group_by_text += ' [default]'; - } - - group.append_item(group_by_text, () => { - cur_group = group_item; - - delete settings.playlist_custom_group_data[cur_playlist_name]; - - settings.playlist_group_data[cur_playlist_name] = group_item.name; - settings.save(); - settings.send_sync(); - - on_execute_callback_fn(); - }, { is_radio_checked: cur_group.name === group_item.name }); - }); - }; - - /** - * Called when the sync state is changed. - * Updates the settings object with the new state and reinitializes the name to preset map. - * It also sets the active playlist to the current playlist name. - * @param {?} value The new sync state. - */ - this.sync_state = function (value) { - settings.receive_sync(value); - initalize_name_to_preset_map(); - this.set_active_playlist(cur_playlist_name); - }; - - /** - * Opens a dialog box that allows the user to enter a custom grouping query. - * The function also takes a callback function that is called when the user clicks the OK button. - * @param {function} on_execute_callback_fn The callback function to call when the user clicks the OK button. - */ - function request_user_query(on_execute_callback_fn) { - const on_ok_fn = (ret_val) => { - const custom_group = new GroupingHandler.Settings.Group('user_defined', '', ret_val[0], ret_val[1]); - cur_group = custom_group; - - settings.playlist_group_data[cur_playlist_name] = 'user_defined'; - settings.playlist_custom_group_data[cur_playlist_name] = custom_group; - - settings.save(); - settings.send_sync(); - - on_execute_callback_fn(); - }; - - const parsed_query = cur_group.name === 'user_defined' ? [cur_group.group_query, cur_group.title_query] : ['', '[%album artist%]']; - const htmlCode = qwr_utils.prepare_html_file(`${fb.ProfilePath}georgia-reborn\\scripts\\playlist\\assets\\html\\MsgBox.html`); - utils.ShowHtmlDialog(window.ID, htmlCode, { width: 650, height: 425, data: ['Foobar2000: Group by', ['Grouping Query', 'Title Query'], parsed_query, on_ok_fn] }); - } - - /** - * Opens the group presets manager dialog. - * @param {function} on_execute_callback_fn The function to call when the dialog is closed. - */ - function manage_groupings(on_execute_callback_fn) { - const on_ok_fn = (ret_val_json) => { - const ret_val = JSON.parse(ret_val_json); - - settings.group_presets = ret_val[0]; - settings.default_group_name = ret_val[2]; - initalize_name_to_preset_map(); - - cur_group = settings.group_presets[group_by_name.indexOf(ret_val[1])]; - settings.playlist_group_data[cur_playlist_name] = ret_val[1]; - - delete settings.playlist_custom_group_data[cur_playlist_name]; - - settings.save(); - settings.send_sync(); - - config.addConfigurationObject(themePlaylistGroupingPresetsSchema, settings.group_presets); - config.writeConfiguration(); - - on_execute_callback_fn(); - }; - - const htmlCode = qwr_utils.prepare_html_file(`${fb.ProfilePath}georgia-reborn\\scripts\\playlist\\assets\\html\\GroupPresetsMngr.html`); - utils.ShowHtmlDialog(window.ID, htmlCode, { width: 650, height: 425, data: [JSON.stringify([settings.group_presets, cur_group.name, settings.default_group_name]), on_ok_fn] }); - } - - /** - * Initializes the list of playlists. - */ - function initialize_playlists() { - playlists = []; - const playlist_count = plman.PlaylistCount; - for (let i = 0; i < playlist_count; ++i) { - playlists.push(plman.GetPlaylistName(i)); - } - } - - /** - * Removes any playlists that are no longer present from the settings. - */ - function cleanup_settings() { - for (const i in settings.playlist_group_data) { - console.log(i); - if (!playlists.includes(i)) { - delete settings.playlist_group_data[i]; - } - } - - for (const i in settings.playlist_custom_group_data) { - if (!playlists.includes(i)) { - delete settings.playlist_custom_group_data[i]; - } - } - - settings.save(); - } - - /** - * Initializes the map from group name to group object. - */ - function initalize_name_to_preset_map() { - group_by_name = settings.group_presets.map((item) => item.name); - } - - initalize_name_to_preset_map(); - initialize_playlists(); - cleanup_settings(); -} - - -/** - * Manages and manipulates settings related to grouping playlists. - * @class - */ -GroupingHandler.Settings = function () { - /** @typedef {GroupingHandler.Settings.Group} */ - const CtorGroupData = GroupingHandler.Settings.Group; - /** - * Reads the configuration from the `config` object and assigns it to the `prefs` variable. - * @type {ReturnType} - */ - const prefs = config.readConfiguration(); - - /** @type {Object} */ - this.playlist_group_data = {}; - /** @type {Object} */ - this.playlist_custom_group_data = {}; - /** @type {string} */ - this.default_group_name = ''; - /** @type {Array} */ - this.group_presets = []; - - /** - * Loads settings from the properties object. - */ - this.load = function () { - this.playlist_group_data = JSON.parse(g_properties.playlist_group_data); - this.playlist_custom_group_data = JSON.parse(g_properties.playlist_custom_group_data); - this.default_group_name = g_properties.default_group_name; - this.group_presets = prefs.themePlaylistGroupingPresets || JSON.parse(g_properties.group_presets); - }; - - /** - * Saves settings to the properties object. - */ - this.save = function () { - g_properties.playlist_group_data = JSON.stringify(this.playlist_group_data); - g_properties.playlist_custom_group_data = JSON.stringify(this.playlist_custom_group_data); - g_properties.default_group_name = this.default_group_name; - g_properties.group_presets = JSON.stringify(this.group_presets); - }; - - /** - * Sends the sync data to the other clients. - */ - this.send_sync = () => { - const syncData = { - g_playlist_group_data: g_properties.playlist_group_data, - g_playlist_custom_group_data: g_properties.playlist_custom_group_data, - g_default_group_name: g_properties.default_group_name, - g_group_presets: g_properties.group_presets - }; - - window.NotifyOthers('sync_group_query_state', syncData); - }; - - /** - * Receives the sync data from the other clients. - * @param {{g_playlist_group_data, g_playlist_custom_group_data, g_default_group_name, g_group_presets}} settings_data The sync data. - */ - this.receive_sync = function (settings_data) { - g_properties.playlist_group_data = settings_data.g_playlist_group_data; - g_properties.playlist_custom_group_data = settings_data.g_playlist_custom_group_data; - g_properties.default_group_name = settings_data.g_default_group_name; - g_properties.group_presets = settings_data.g_group_presets; - - this.load(); - }; - - /** - * Checks and fixes the values of certain properties in the `g_properties` object. - */ - function fixup_g_properties() { - if (!g_properties.playlist_group_data || !IsObject(JSON.parse(g_properties.playlist_group_data))) { - g_properties.playlist_group_data = JSON.stringify({}); - } - - if (!g_properties.playlist_custom_group_data || !IsObject(JSON.parse(g_properties.playlist_custom_group_data))) { - g_properties.playlist_custom_group_data = JSON.stringify({}); - } - - if (!g_properties.group_presets || !Array.isArray(JSON.parse(g_properties.group_presets))) { - g_properties.group_presets = JSON.stringify([ - new CtorGroupData('artist', 'by artist', '%album artist%', undefined, ''), - new CtorGroupData('artist_album', 'by artist / album', '%album artist%%album%', undefined, undefined, { - show_date: true - }), - new CtorGroupData('artist_album_disc', 'by artist / album / disc number', '%album artist%%album%%discnumber%', undefined, undefined, { - show_date: true, - show_disc: true - }), - new CtorGroupData('artist_album_disc_edition', 'by artist / album / disc number / edition / codec', '%album artist%%album%%discnumber%%edition%%codec%', undefined, undefined, { - show_date: true, - show_disc: true - }), - new CtorGroupData('path', 'by path', '$directory_path(%path%)', undefined, undefined, { - show_date: true - }), - new CtorGroupData('date', 'by date', '%date%', undefined, undefined, { - show_date: true - }) - ]); - } - - if (!g_properties.default_group_name || !IsString(g_properties.default_group_name)) { - g_properties.default_group_name = prefs.themePlaylistGroupingPresets[3].name || prefs.themePlaylistGroupingPresets[0].name || 'artist_album_disc_edition'; - } - } - - fixup_g_properties(); - this.load(); -}; - - -/** - * A constructor function called `Group` in the `GroupingHandler.Settings` namespace. - * @param {string} name - * @param {string} description - * @param {?string=} [group_query=''] - * @param {?string=} [title_query='[%album artist%]'] - * @param {?string=} [sub_title_query="[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']"] - * @param {object} [options={}] - * @param {boolean=} [options.show_date=false] - * @param {boolean=} [options.show_disc=false] - * @class - * @struct - */ -GroupingHandler.Settings.Group = function (name, description, group_query, title_query, sub_title_query, options) { - /** @type {string} */ - this.name = name; - /** @type {string} */ - this.description = description; - /** @type {string} */ - this.group_query = group_query || ''; - /** @type {string} */ - this.title_query = title_query || '[%album artist%]'; - /** @type {string} */ - this.sub_title_query = sub_title_query || '[%album%[ \'(\'%albumsubtitle%\')\']][ - \'[\'%edition%\']\']'; - /** @type {boolean} */ - this.show_date = !!(options && options.show_date); - /** @type {boolean} */ - this.show_disc = !!(options && options.show_disc); -}; - -/** @type {GroupingHandler} */ -Header.grouping_handler = new GroupingHandler(); - - -////////////////////////// -// * PLAYLIST HISTORY * // -////////////////////////// -/** - * Manages the history of playlist states, allowing users to navigate back and forward - * between previous states and updates the playlist accordingly. - */ -class PlaylistHistory { - /** - * Initializes a playlist with a maximum number of states. - * @param {number} maxStates The maximum number of states that can be stored in the history array. - * @class - */ - constructor(maxStates = 10) { - this.maxStates = maxStates; - /** @private PlaylistState[] */ this.history = []; - /** @private */ this.stateIndex = 0; - /** @private */ this.updatingPlaylist = false; - - this.playlistAltered(PlaylistMutation.Init); - } - - /** - * Gets the length of the playlist history. - * @returns {number} The length of the "history" array. - */ - get length() { - return this.history.length; - } - - /** - * Checks if there is a previous state in the playlist history. - * @returns {boolean} True or false. - */ - canBack() { - return this.stateIndex > 0; - } - - /** - * Checks if there is a next state available to navigate forward to. - * @returns {boolean} True or false. - */ - canForward() { - return this.stateIndex < this.length - 1; - } - - /** - * Gets whether the history should ignore upcoming mutations and changes to the playlist. - * @returns {boolean} Whether to ignore playlist mutations. - */ - get ignorePlaylistMutations() { - return this.ignorePlaylistMutations; - } - - /** - * Sets whether the history should ignore upcoming mutations and changes to the playlist. - * - * Playlist updates are synchronous, but notifications are async. If setting to false, we - * update that value async as well to hopefully happen after all callbacks have called, - * and then manually call playlistAltered in case the playlist state has changed. - * @param {boolean} ignore Whether to ignore playlist mutations. - */ - set ignorePlaylistMutations(ignore) { - if (!ignore) { - setTimeout(() => { - this.updatingPlaylist = false; - this.playlistAltered(PlaylistMutation.Switch); - }, 1); - } else { - this.updatingPlaylist = true; - } - } - - /** - * Decreases the state index and sets the playlist state accordingly. - */ - back() { - this.stateIndex--; - if (this.stateIndex <= 0) { - this.stateIndex = 0; - } - DebugLog('playlistHistory back =>', this.stateIndex); - this.setPlaylistState(); - } - - /** - * Increments the state index and sets the playlist state, ensuring that the state index - * does not exceed the length of the playlist. - */ - forward() { - this.stateIndex++; - if (this.stateIndex >= this.length) { - this.stateIndex = this.length - 1; - } - DebugLog('playlistHistory forward =>', this.stateIndex); - this.setPlaylistState(); - } - - /** - * Clears the playlist history. Should always be called from on_playlists_changed - * because all saved playlistIds have been invalidated. - */ - reset() { - this.history = []; - this.playlistAltered(PlaylistMutation.Init); - } - - /** - * Sets and updates the state of a playlist by adding or removing items based on the current playback state. - * @private - */ - setPlaylistState() { - playlistHistoryUsed = true; - this.updatingPlaylist = true; - /** @type {PlaylistState} */ const activeState = this.history[this.stateIndex]; - const pbQueue = plman.GetPlaybackQueueContents(); - const playingItem = plman.GetPlayingItemLocation(); - const plIndex = activeState.playlistId - plman.UndoBackup(plIndex); - plman.ActivePlaylist = plIndex; - - if (!activeState.locked) { - if (!playingItem.IsValid || playingItem.PlaylistIndex !== plIndex) { - plman.ClearPlaylist(plIndex); - plman.InsertPlaylistItems(plIndex, 0, activeState.playlistEntries); - } else { - const handles = plman.GetPlaylistItems(plIndex); - const index = handles.Find(fb.GetNowPlaying()); - const stateHandles = activeState.playlistEntries.Clone(); - const stateIndex = stateHandles.Find(fb.GetNowPlaying()); - const stateHandlesClone = stateHandles.Clone(); - console.log('>>> Playlist now playing index:', index); - // Remove everything in playlist except currently playing song - plman.ClearPlaylistSelection(plIndex); - plman.SetPlaylistSelection(plIndex, [playingItem.PlaylistItemIndex], true); - plman.RemovePlaylistSelection(plIndex, true); - plman.ClearPlaylistSelection(plIndex); - try { - stateHandles.RemoveById(stateIndex); - } catch (e) { - plman.InsertPlaylistItems(plIndex, plman.PlaylistItemCount(plIndex), stateHandlesClone); - } - if (stateIndex > 0) { - stateHandles.RemoveRange(stateIndex, stateHandles.Count); - plman.InsertPlaylistItems(plIndex, 0, stateHandles); - } - if (stateIndex < stateHandlesClone.Count) { - stateHandlesClone.RemoveRange(0, stateIndex); - plman.InsertPlaylistItems(plIndex, plman.PlaylistItemCount(plIndex), stateHandlesClone); - } - // Remove currently playing song duplicate - plman.SetPlaylistSelection(plIndex, [playingItem.PlaylistItemIndex], true); - plman.RemovePlaylistSelection(plIndex); - } - } - - this.restorePlaybackQueue(pbQueue); - - // * Wait for callbacks to be called - setTimeout(() => { - playlistHistoryUsed = false; - this.updatingPlaylist = false; - }, 1); - - // * Scroll to now playing when auto-scroll is active and playlist has now playing - const playing_item_location = plman.GetPlayingItemLocation(); - const playlistNowPlaying = playing_item_location.PlaylistIndex === plman.ActivePlaylist; - if (playlistNowPlaying && (pref.playlistAutoScrollNowPlaying || fb.PlaybackFollowCursor || fb.CursorFollowPlayback)) { - setTimeout(() => { // * Wait until new album art / disc art loaded and other things finished for smoother auto-scrolling - playlist.show_now_playing(); - }, newTrackFetchingDone + 200); - } - } - - /** - * Restores a playback queue by adding items to the queue based on their playlist and index, - * or by adding them directly if no playlist or index is specified. - * @param {FbPlaybackQueueItem[]} pbQueue The playback queue state to restore. - * @private Attempts to re-mark playbackQueue items after setting playlist state. - */ - restorePlaybackQueue(pbQueue) { - plman.FlushPlaybackQueue(); - pbQueue.forEach((queueItem) => { - const itemPlaylist = queueItem.PlaylistIndex; - const itemIndex = queueItem.PlaylistItemIndex; - if (itemPlaylist !== -1 && itemIndex !== -1) { - const plContents = {}; - if (!plContents[itemPlaylist]) { - plContents[itemPlaylist] = plman.GetPlaylistItems(itemPlaylist); - } - /** FbMetadbHandleList */ const playlistHandles = plContents[itemPlaylist]; - if (playlistHandles && playlistHandles[itemIndex] && playlistHandles[itemIndex].Path === queueItem.Handle.Path) { - plman.AddPlaylistItemToPlaybackQueue(itemPlaylist, itemIndex); - } else { - const index = plContents[itemPlaylist].Find(queueItem.Handle); - if (index >= 0) { - plman.AddPlaylistItemToPlaybackQueue(itemPlaylist, index); - } else { - plman.AddItemToPlaybackQueue(queueItem.Handle); - } - } - } else { - plman.AddItemToPlaybackQueue(queueItem.Handle); - } - }); - } - - /** - * Logs the type of playlist mutation and checks if the playlist should be added to the history of playlist states. - * Notify the PlaylistHistory that a playlist was altered. - * @param {string} mutationType The type of mutation that occurred. - */ - playlistAltered(mutationType) { - // ignore playlist alterations when changing states - console.log(mutationType); - if (!this.updatingPlaylist && plman.ActivePlaylist >= 0) { - const plItems = plman.GetPlaylistItems(plman.ActivePlaylist); - if (this.shouldAddState(plman.ActivePlaylist, plItems, mutationType)) { - if (this.stateIndex < this.length - 1) { - this.history = this.history.slice(0, this.stateIndex + 1); - } - if (this.length >= this.maxStates) { - this.history.shift(); - } - this.history.push(new PlaylistState(plman.ActivePlaylist, plItems)); - this.stateIndex = this.length - 1; - if (btns.back) { - btns.back.repaint(); - btns.forward.repaint(); - } - DebugLog('stateIndex:', this.stateIndex, ' new items count:', plItems.Count, this.stateIndex); - } - } - } - - /** - * Checks if a new playlist state should be added to the history based on the playlist ID, new items, and mutation type. - * @param {number} playlistId The playlist id. - * @param {FbMetadbHandleList} newItems The list of handles of playlist items. - * @param {string} mutationType The type of mutation that occurred. - * @returns {boolean} True or false. - * @private - */ - shouldAddState(playlistId, newItems, mutationType) { - const start = Date.now(); - const currState = this.history[this.stateIndex]; - if (!currState) { - // init'ing playlist history - return true - } - - // if playlist ID is unchanged, and playlist is locked, don't save - if (playlistId === currState.playlistId && plman.IsPlaylistLocked(playlistId)) { - return false; - } - if (playlistId !== currState.playlistId || - currState.locked || plman.IsPlaylistLocked(playlistId) || - newItems.Count !== currState.playlistEntries.Count) { - return true; - } - for (let i = 0; i < newItems.Count; i++) { - if (newItems[i].RawPath !== currState.playlistEntries[i].RawPath) { - // console.log(newItems[i].RawPath, currState.playlistEntries[i].RawPath); - return true; - } - } - DebugLog(`Checking for duplicate playlist states took: ${Date.now() - start}ms`); - return false; - } -} - - -/** - * Manages the state of an active playlist, including its ID, lock status, - * and a list of playlist entries if it is not locked. - */ -class PlaylistState { - /** - * @param {number} playlistId The ID of the playlist. - * @param {FbMetadbHandleList} plItems The playlist items in the current playlist. - * @public - * @class - */ - constructor(playlistId, plItems) { - /** - * @type {number} - * @public - */ - this.playlistId = playlistId; - /** - * @type {boolean} - * @public - */ - this.locked = plman.IsPlaylistLocked(playlistId); - if (!this.locked) { - // don't need to save items if playlist is locked, we'll just switch to it - /** @type {FbMetadbHandleList} */ this.playlistEntries = plItems; - } - } -} - - -////////////////////////// -// * PLAYLIST MANAGER * // -////////////////////////// -/** - * The playlist manager at the top of the panel acts like a drop down menu that contains various options. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @class - */ -function PlaylistManager(x, y, w, h) { - // public: - this.x = x; - this.y = y; - this.w = w; - this.h = h; - - /** @enum {number} */ - const state = { - normal: 0, - hovered: 1, - pressed: 2 - }; - - /** @type {state} */ - this.panel_state = state.normal; - /** @type {number} */ - this.hover_alpha = 0; - - // private: - const that = this; - - /** @type {?string} */ - let info_text; // undefined - - const throttled_repaint = Throttle(() => { - window.RepaintRect(this.x, this.y, this.w, this.h); - }, 1000 / 60); - - /** - * Animates the alpha of the playlist manager text button at the top. - * @param {Array} items_arg The list of items to animate. - * @param {function(PlaylistItem): boolean} hover_predicate_arg The predicate to determine if an item is hovered. - */ - const _alpha_timer = function (items_arg, hover_predicate_arg) { - let alpha_timer_internal = null; - - this.start = function () { - const hover_in_step = 50; - const hover_out_step = 15; - - if (!alpha_timer_internal) { - alpha_timer_internal = setInterval(() => { - items_arg.forEach((item) => { - const saved_alpha = item.hover_alpha; - item.hover_alpha = hover_predicate_arg(item) ? Math.min(255, item.hover_alpha += hover_in_step) : Math.max(0, item.hover_alpha -= hover_out_step); - - if (saved_alpha !== item.hover_alpha) { - item.repaint(); - } - }); - - const alpha_in_progress = items_arg.some(item => item.hover_alpha > 0 && item.hover_alpha < 255); - - if (!alpha_in_progress) { - this.stop(); - } - }, 25); - } - }; - - /** - * Stops the animation of the button. - */ - this.stop = () => { - if (alpha_timer_internal) { - clearInterval(alpha_timer_internal); - alpha_timer_internal = null; - } - }; - }; - - /** - * The animation timer for the playlist manager text button. - */ - const alpha_timer = new _alpha_timer([this], (item) => item.panel_state === state.hovered); - - /** @type {?GdiBitmap} */ - let image_normal = null; - /** @type {?GdiBitmap} */ - let image_hovered = null; - - let cur_playlist_idx; // undefined - - // #region Callback Implementation - - /** - * Draws the playlist manager text button at the top. - * @param {GdiGraphics} gr - */ - this.on_paint = function (gr) { - if (!info_text || cur_playlist_idx !== plman.ActivePlaylist) { - cur_playlist_idx = plman.ActivePlaylist; - let metadb_list = plman.GetPlaylistSelectedItems(cur_playlist_idx); - let is_selected = true; - - if (!metadb_list.Count) { - metadb_list = plman.GetPlaylistItems(cur_playlist_idx); - is_selected = false; - } - - const track_count = metadb_list.Count; - let tracks_text = ''; - let duration_text = ''; - if (track_count > 0) { - tracks_text = track_count.toString() + (track_count > 1 ? ' tracks' : ' track'); - if (is_selected) { - tracks_text += ' selected'; - } - - const duration = Math.round(metadb_list.CalcTotalDuration()); - if (duration) { - duration_text = utils.FormatDuration(duration); - } - } - - info_text = plman.GetPlaylistName(cur_playlist_idx); - if (tracks_text) { - info_text += `: ${tracks_text}`; - } - if (duration_text) { - info_text += `, Length: ${duration_text}`; - } - } - - if (this.panel_state === state.pressed - || (this.panel_state === state.normal && !this.hover_alpha) - || (this.panel_state === state.hovered && this.hover_alpha === 255)) { - if (image_normal) { - image_normal = null; - } - if (image_hovered) { - image_hovered = null; - } - - draw_on_image(gr, this.x, this.y, this.w, this.h, this.panel_state); - } - else { - if (!image_normal) { - const image = gdi.CreateImage(this.w, this.h); - const image_gr = image.GetGraphics(); - - draw_on_image(image_gr, 0, 0, this.w, this.h, state.normal); - - image.ReleaseGraphics(image_gr); - image_normal = image; - } - - if (!image_hovered) { - const image = gdi.CreateImage(this.w, this.h); - const image_gr = image.GetGraphics(); - - draw_on_image(image_gr, 0, 0, this.w, this.h, state.hovered); - - image.ReleaseGraphics(image_gr); - image_hovered = image; - } - - gr.DrawImage(image_normal, this.x, this.y, this.w, this.h, 0, 0, this.w, this.h, 0, 255); - gr.DrawImage(image_hovered, this.x, this.y, this.w, this.h, 0, 0, this.w, this.h, 0, this.hover_alpha); - } - }; - - /** - * Handles playlist modified events and clears the button name. - */ - this.on_playlist_modified = function () { - info_text = undefined; - this.repaint(); - }; - - /** - * Handles the button state of the playlist manager text button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - */ - this.on_mouse_move = function (x, y, m) { - if (this.panel_state === state.pressed) { - return; - } - - change_state(this.trace(x, y) ? state.hovered : state.normal); - }; - - /** - * Handles left mouse button down events and changes the playlist manager text button state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - this.on_mouse_lbtn_down = function (x, y, m) { - if (btns.back && btns.back.mouseInThis(x, y) || btns.forward && btns.forward.mouseInThis(x, y)) { - return; // Handled in back forward buttons - } - if (!this.trace(x, y)) { - return; - } - - change_state(state.pressed); - }; - - /** - * Handles left mouse button up events and opens the playlist manager drop down list menu. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - this.on_mouse_lbtn_up = function (x, y, m) { - const was_pressed = this.panel_state === state.pressed; - - if (btns.back && btns.back.mouseInThis(x, y) || btns.forward && btns.forward.mouseInThis(x, y)) { - return; // Handled in back forward buttons - } - if (!this.trace(x, y)) { - change_state(state.normal); - return; - } - else { - change_state(state.hovered); - if (!was_pressed) { - return; - } - } - - topMenuPlaylists(x, y); - - this.repaint(); - }; - - /** - * Handles right mouse button down events and changes the playlist manager text button state. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - this.on_mouse_rbtn_down = function (x, y, m) { - if (!this.trace(x, y)) { - return true; - } - - change_state(state.pressed); - }; - - /** - * Handles right mouse button up events and opens the playlist manager. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} m The mouse mask. - * @returns {boolean} True or false. - */ - this.on_mouse_rbtn_up = function (x, y, m) { - const was_pressed = this.panel_state === state.pressed; - - if (!this.trace(x, y)) { - change_state(state.normal); - return true; - } - else { - change_state(state.hovered); - if (!was_pressed) { - return true; - } - } - - const cmm = new ContextMainMenu(); - - fb.RunMainMenuCommand('View/Playlist Manager'); // PlaylistManager.append_playlist_info_visibility_context_menu_to(cmm); - - if (utils.IsKeyPressed(VK_SHIFT)) { - qwr_utils.append_default_context_menu_to(cmm); - } - - activeMenu = true; - cmm.execute(x, y); - activeMenu = false; - - return true; - }; - - /** - * Handles mouse leave events and resets the playlist manager text button state. - * @override - */ - this.on_mouse_leave = () => { - change_state(state.normal); - }; - - // #endregion - - /** - * Reinitializes the playlist manager text button state. - */ - this.reinitialize = function () { - info_text = undefined; - this.panel_state = state.normal; - this.hover_alpha = 0; - }; - - /** - * Sets the width of the playlist manager text button. - * @param {number} w The width of the button. - */ - this.set_w = function (w) { - this.w = w; - }; - - /** - * Sets the size and position of the playlist manager text button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width of the button. - */ - this.set_xywh = function (x, y, w) { - this.x = x; - this.y = y; - this.w = w; - this.h = SCALE(g_properties.row_h + 4); - }; - - /** - * Traces mouse movement and checks when mouse is within the boundaries of the playlist manager text button. - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @returns {boolean} True or false. - */ - this.trace = function (x, y) { - return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; - }; - - /** - * Registers key actions and handles key presses. - * @param {KeyActionHandler} key_handler The KeyActionHandler object. - */ - this.register_key_actions = (key_handler) => { - key_handler.register_key_action(VK_KEY_N, - (modifiers) => { - if (modifiers.ctrl) { - plman.CreatePlaylist(plman.PlaylistCount, ''); - plman.ActivePlaylist = plman.PlaylistCount - 1; - } - }); - - key_handler.register_key_action(VK_KEY_M, - (modifiers) => { - if (modifiers.ctrl) { - fb.RunMainMenuCommand('View/Playlist Manager'); - } - }); - }; - - /** - * Updates the playlist manager text button state via repaint. - */ - this.repaint = () => { - throttled_repaint(); - }; - - /** - * Draws the playlist manager text button and defines its states. - * @param {GdiGraphics} gr - * @param {number} x The x-coordinate. - * @param {number} y The y-coordinate. - * @param {number} w The width. - * @param {number} h The height. - * @param {state} panel_state The state of the playlist manager text button. - */ - function draw_on_image(gr, x, y, w, h, panel_state) { - const headerFontSize = pref[`playlistHeaderFontSize_${pref.layout}`]; - const showPlaylistManager = pref[`showPlaylistManager_${pref.layout}`]; - let text_color; - let bg_color; - - switch (panel_state) { - case state.normal: { - text_color = pref.styleBlend && pref.autoHidePlman || !showPlaylistManager ? '' : g_pl_colors.plman_text_normal; - bg_color = g_pl_colors.plman_bg; - break; - } - case state.hovered: { - text_color = g_pl_colors.plman_text_hovered; - bg_color = g_pl_colors.plman_bg; - break; - } - case state.pressed: { - text_color = g_pl_colors.plman_text_pressed; - bg_color = g_pl_colors.plman_bg; - break; - } - } - - if (!pref.styleBlend) gr.FillSolidRect(x, y, w, h, bg_color); // Playlist Manager Hide Top Rows that shouldn't be visible - // * Need to apply text rendering AntiAliasGridFit when using style Blend or when using custom theme fonts with larger font sizes - gr.SetTextRenderingHint(pref.styleBlend || pref.customThemeFonts && headerFontSize > 18 ? TextRenderingHint.AntiAliasGridFit : TextRenderingHint.ClearTypeGridFit); - - if (plman.ActivePlaylist !== -1 && plman.IsPlaylistLocked(plman.ActivePlaylist)) { - // Position above scrollbar for eye candy - // const sbar_x = x + w - playlist_geo.scrollbar_w - playlist_geo.scrollbar_right_pad; - const lock_x = ww - SCALE(29); - const lock_text = '\uf023'; - const lock_w = Math.ceil( - /** @type {!number} */ - gr.MeasureString(lock_text, g_pl_fonts.font_awesome, 0, 0, 0, 0).Width - ); - gr.DrawString(lock_text, g_pl_fonts.font_awesome, text_color, lock_x, y, lock_w, h, g_string_format.align_center); - - // right_pad += lock_w; // Deactivated -> PLM text should be always centered - } - - const info_text_format = g_string_format.align_center | g_string_format.trim_ellipsis_char | g_string_format.no_wrap; - gr.DrawString(info_text, g_pl_fonts.title_selected, text_color, x, y, w, h, info_text_format); - - // * Playlist history buttons - const yCorr = - headerFontSize === 22 ? RES_4K ? 14 : 7 : - headerFontSize === 20 ? RES_4K ? 12 : 5 : - headerFontSize === 18 ? RES_4K ? 10 : 3 : - headerFontSize === 17 ? RES_4K ? 8 : 2 : - headerFontSize === 16 ? RES_4K ? 7 : 1 : - headerFontSize === 15 ? RES_4K ? 4 : 0 : - headerFontSize === 14 ? RES_4K ? 4 : 0 : - headerFontSize === 13 ? RES_4K ? 2 : -1 : - headerFontSize === 12 ? RES_4K ? 2 : -1 : - headerFontSize === 10 ? RES_4K ? -2 : -3 : ''; - - const noAlbumArtSize = wh - geo.topMenuHeight - geo.lowerBarHeight; - const info_w = gr.CalcTextWidth(info_text, g_pl_fonts.title_selected); - const btn_x = Math.round((pref.playlistLayout === 'normal' ? pref.panelWidthAuto ? displayLibrarySplit() ? noAlbumArtSize : albumArtSize.x + albumArtSize.w : ww * 0.5 : 0) + (w - info_w) * 0.5); - const btn_y = geo.topMenuHeight + yCorr; - const btns_w = Math.round(h); - const hasPlaylistHistory = playlistHistory.canBack() || playlistHistory.canForward(); - const showBtns = (pref.autoHidePlman && (panel_state !== state.normal) || !pref.autoHidePlman); - - if (pref.showPlaylistHistory && hasPlaylistHistory && showPlaylistManager) { - btns.back = new Button(showBtns ? btn_x - btns_w : 9999, btn_y, h, h, 'Back', btnImg.Back, null, playlistHistory.canBack.bind(playlistHistory)); - btns.forward = new Button(showBtns ? btn_x + info_w + btns_w * SCALE(0.15) : 9999, btn_y, h, h, 'Forward', btnImg.Forward, null, playlistHistory.canForward.bind(playlistHistory)); - } - } - - /** - * Changes the state of the playlist manager text button. - * @param {state} new_state The new state of the button. - */ - function change_state(new_state) { - if (that.panel_state === new_state) { - return; - } - - const old_state = that.panel_state; - that.panel_state = new_state; - - if (old_state === state.pressed) { - // Mouse click action opens context menu, which triggers on_mouse_leave, thus causing weird hover animation - that.hover_alpha = 0; - } - if (new_state === state.hovered || new_state === state.normal) { - alpha_timer.start(); - } - - that.repaint(); - } -} - - -/** - * Appends a context menu item to the given parent menu that allows the user - * to toggle the visibility of the playlist manager text button. - * @param {ContextMenu} parent_menu The parent menu to append the item to. - */ -PlaylistManager.append_playlist_info_visibility_context_menu_to = (parent_menu) => { - const showPlaylistManager = pref[`showPlaylistManager_${pref.layout}`]; - parent_menu.append_item('Show playlist manager', () => { - // g_properties.show_playlist_info = !g_properties.show_playlist_info; - if (pref.layout === 'compact') { - pref.showPlaylistManager_compact = !pref.showPlaylistManager_compact; - } else if (pref.layout === 'artwork') { - pref.showPlaylistManager_artwork = !pref.showPlaylistManager_artwork; - } else { - pref.showPlaylistManager_default = !pref.showPlaylistManager_default; - } - playlist.on_size(ww, wh); - }, { is_checked: showPlaylistManager }); -}; +//////////////////////////////////////////////// +// ! ALL FILES LOADED IN GR-ASYNC-LOADER.JS ! // +//////////////////////////////////////////////// +// /** @global @type {boolean} */ +// const loadAsync = window.GetProperty('Panel Playlist - System: Load.playlist.asynchronously', true); +// /** @global @type {string} */ +// const playlistPath = `${grPath.base}scripts\\playlist\\scripts\\`; + +// async function readFiles(files) { +// for (const file of files) { +// if (window.ID) { // fix pss issue +// await include(`${playlistPath}${file}`); +// } +// } +// } + +// /** @global @type {string[]} */ +// const files = [ +// 'pl-setup.js', +// 'pl-helpers.js', +// 'pl-properties.js', +// 'pl-components.js', +// 'pl-controls.js', +// 'pl-list.js', +// 'pl-list-content.js', +// 'pl-list-header.js', +// 'pl-list-row.js', +// 'pl-panel.js', +// 'pl-playlist.js' +// ]; + +// if (loadAsync) { +// readFiles(files).then(() => { +// if (!window.ID) return; // fix pss issue +// pl.call.on_size(grMain.ui.ww, grMain.ui.wh); +// window.Repaint(); +// }); +// } else { +// for (const file of files) include(`${playlistPath}${file}`); +// } /////////////// @@ -9473,7 +69,7 @@ PlaylistManager.append_playlist_info_visibility_context_menu_to = (parent_menu) // * If error crash "class constructors must be invoked with 'new' with generate_first_item_to_draw" throws, // * happens when playlist scroll wants to scroll to a position more than actual scrollable lines (i.e to a non-valid position) that do not exist in the active playlist // * This bug has been fixed and hopefully will not occour again. -// * If not, look for "g_properties.scroll_pos" in Control_List.js and here and "this.scroll" in Control_Scrollbar.js. +// * If not, look for "plSet.scrollbar_pos" in Control_List.js and here and "this.scroll" in Control_Scrollbar.js. // TODO: Grouping presets manager: other EsPlaylist grouping features - sorting, playlist association // TODO: Think about on_visibility_change callback diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-callbacks.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-callbacks.js new file mode 100644 index 00000000..e63ca2ba --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-callbacks.js @@ -0,0 +1,1153 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Callbacks * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +//////////////////////////// +// * PLAYLIST CALLBACKS * // +//////////////////////////// +/** + * A class that contains all playlist callbacks. + */ +class PlaylistCallbacks { + /** + * Creates the `PlaylistPanel` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + constructor(x, y) { + /** @public @type {number} */ + this.x = x; + /** @public @type {number} */ + this.y = y; + /** @public @type {number} */ + this.w = 0; + /** @public @type {number} */ + this.h = 0; + /** @public @type {boolean} */ + this.is_activated = window.IsVisible; + + pl.plman = new PlaylistManager(this.x, this.y, 0, SCALE(plSet.row_h)); + pl.playlist = new Playlist(this.x, this.y + (plSet.show_plman ? SCALE(plSet.row_h) + SCALE(4) : 0)); + } + + // * CALLBACKS * // + // #region Callbacks + /** + * Draws the items in the playlist and a scrollbar if necessary. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + on_paint(gr) { + gr.FillSolidRect(this.x, this.y, this.w, this.h, pl.col.bg); // Main bg + + if (!this.is_activated) { + this.is_activated = true; + + if (plSet.show_plman) { + pl.plman.reinitialize(); + } + pl.playlist.reinitialize(); + } + + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended && (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork)) { + gr.DrawImage(grCol.imgBlended, grm.ui.displayLibrarySplit() ? grSet.panelWidthAuto ? this.x : grm.ui.ww * 0.5 : 0, 0, grm.ui.ww, grm.ui.wh, grm.ui.displayLibrarySplit() ? grSet.panelWidthAuto ? grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grm.ui.ww * 0.5 : 0, 0, grCol.imgBlended.Width, grCol.imgBlended.Height); + } + + pl.playlist.on_paint(gr); + + // * Hide rows that shouldn't be visible + gr.SetSmoothingMode(SmoothingMode.None); + gr.FillSolidRect(this.x, 0, this.w, grm.ui.topMenuHeight, grCol.bg); // Hide alpha overlapping at the top + gr.FillSolidRect(this.x, grm.ui.topMenuHeight, this.w, this.plman_h, pl.col.bg); // Hide alpha overlapping at the top + gr.FillSolidRect(this.x, this.y + this.h - pl.geo.row_h, this.w, pl.geo.row_h + grm.ui.lowerBarHeight, pl.col.bg); // Hide alpha overlapping at the bottom + gr.FillSolidRect(this.x, this.y + this.h, this.w, grm.ui.lowerBarHeight, grCol.bg); // Hide alpha overlapping at the bottom + + if (UIHacks.Aero.Effect === 2) gr.DrawLine(this.x, 0, grm.ui.ww, 0, 1, grCol.bg); // UIHacks aero glass shadow frame fix - needed for style Blend + + if (grSet.styleBlend && grm.ui.albumArt && grCol.imgBlended) { + gr.DrawImage(grCol.imgBlended, this.x, this.y - this.h - grm.ui.topMenuHeight - grm.ui.lowerBarHeight + this.plman_h, grm.ui.ww, grm.ui.wh, this.x, this.y - this.h - grm.ui.topMenuHeight - grm.ui.lowerBarHeight + this.plman_h, grCol.imgBlended.Width, grCol.imgBlended.Height); + gr.DrawImage(grCol.imgBlended, this.x, this.y + this.h - pl.geo.row_h, grm.ui.ww, grm.ui.wh, this.x, this.y + this.h - pl.geo.row_h, grCol.imgBlended.Width, grCol.imgBlended.Height); + } + + if (plSet.show_plman) { + pl.plman.on_paint(gr); + } + } + + /** + * Handles resizing of the playlist and updating its size and position. + * @param {number} w - The width. + * @param {number} h - The height. + */ + on_size(w, h) { + PlaylistRescale(); + + const x = PlaylistSetX(); + const y = grm.ui.topMenuHeight; + const playlist_w = w - x; + const playlist_h = Math.max(0, h - grm.ui.lowerBarHeight - y); + const showPlaylistManager = grSet[`showPlaylistManager_${grSet.layout}`]; + + this.x = x; + this.y = y; + this.h = playlist_h; + this.w = playlist_w; + this.plman_h = SCALE(plSet.row_h); + + pl.playlist.was_on_size_called = true; + pl.playlist.on_size(playlist_w, playlist_h - (this.plman_h * 2), x, y + this.plman_h + SCALE(4)); + pl.plman.set_xywh(x, y, showPlaylistManager ? this.w : 0); // Hide Playlist manager + + if (plSet.show_header && (plSet.auto_collapse || plSet.collapse_on_start)) { + pl.playlist.collapse_handler.collapse_all_but_now_playing(); + } + + if (fb.IsPlaying && (grSet.playlistAutoScrollNowPlaying || fb.PlaybackFollowCursor || fb.CursorFollowPlayback)) { + this.on_playback_new_track(); + } + } + + /** + * Handles drag and drop events and checks if dragging is allowed. Also handles internal and external drops. + * @param {DropTargetAction} action - The drag action that is being performed. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_drag_drop(action, x, y, m) { + pl.playlist.mouse_down = false; ///< Because on_drag_drop suppresses on_mouse_lbtn_up call + pl.playlist.stop_drag_scroll(); + + if (!pl.playlist.selection_handler.is_dragging() || !pl.playlist.trace_list(x, y) || !pl.playlist.selection_handler.can_drop()) { + pl.playlist.selection_handler.disable_drag(); + action.Effect = PlaylistDropEffect.none; + return; + } + + const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); + + if (action.IsInternal) { + const copy_drop = ctrl_pressed && ((action.Effect & 1) || (action.Effect & 4)); + pl.playlist.selection_handler.drop(!!copy_drop); + + // Suppress native drop, since we've handled it ourselves + action.Effect = PlaylistDropEffect.none; + } + else { + action.Effect = PlaylistDropEffect.copy; // * Wine/Linux drag n drop fix // action.Effect = this.filter_effect_by_modifiers(action.Effect); + if (PlaylistDropEffect.none !== action.Effect) { + pl.playlist.selection_handler.external_drop(action); + } + else { + pl.playlist.selection_handler.disable_drag(); + } + } + } + + /** + * Handles drag enter events and checks if the mouse is inside the element, sets the mouse down state, + * enables dragging based on the action type, and determines the drop effect based on the trace list and selection handler. + * @param {DropTargetAction} action - The drag action that is being performed. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. + */ + on_drag_enter(action, x, y, mask) { + pl.playlist.mouse_in = true; + pl.playlist.mouse_down = true; + pl.playlist.drag_event_invoked = true; + + if (!pl.playlist.selection_handler.is_dragging()) { + if (action.IsInternal) { + pl.playlist.selection_handler.enable_drag(); + } + else { + pl.playlist.selection_handler.enable_external_drag(); + } + } + + if (!pl.playlist.trace_list(x, y) || !pl.playlist.selection_handler.can_drop()) { + action.Effect = PlaylistDropEffect.none; + } + else { + action.Effect = (action.Effect & PlaylistDropEffect.move) + || (action.Effect & PlaylistDropEffect.copy) + || (action.Effect & PlaylistDropEffect.link); + } + } + + /** + * Handles drag leave events when the mouse leaves a draggable item. + */ + on_drag_leave() { + if (pl.playlist.selection_handler.is_dragging()) { + pl.playlist.stop_drag_scroll(); + pl.playlist.selection_handler.disable_drag(); + } + + pl.playlist.drag_event_invoked = false; + pl.playlist.mouse_in = false; + pl.playlist.mouse_down = false; + + pl.playlist.repaint(); + } + + /** + * Handles drag over events and allows for dragging and dropping of rows. + * @param {DropTargetAction} action - The drag action that is being performed. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} mask - The mouse mask. + * @returns {number} The value of the `action.Effect` property. + */ + on_drag_over(action, x, y, mask) { + if (!pl.playlist.selection_handler.can_drop()) { + action.Effect = PlaylistDropEffect.none; + return action.Effect; + } + + const drop_info = pl.playlist._get_drop_row_info(x, y); + const row = drop_info.row; + + if (pl.playlist.drag_scroll_in_progress) { + if (!row || (y >= (pl.playlist.list_y + pl.playlist.row_h * 2) && y <= (pl.playlist.list_y + pl.playlist.list_h - pl.playlist.row_h * 2))) { + pl.playlist.stop_drag_scroll(); + } + } + else if (row) { + if (pl.playlist.collapse_handler) { + pl.playlist.collapse_handler.expand(row.parent); + if (pl.playlist.collapse_handler.changed) { + // * Fix to restore drag scroll to last row item at bottom when header is collapsed + pl.playlist.scrollbar.is_scrolled_down = false; + pl.playlist.repaint(); + } + } + + pl.playlist.selection_handler.drag(row, drop_info.is_above); + + if (pl.playlist.is_scrollbar_available) { + if (y < (pl.playlist.list_y + pl.playlist.row_h * 2) && !pl.playlist.scrollbar.is_scrolled_up) { + pl.playlist.selection_handler.drag(null, false); // To clear last hover row + pl.playlist.start_drag_scroll('up'); + } + if (y > (pl.playlist.list_y + pl.playlist.list_h - pl.playlist.row_h * 2) && !pl.playlist.scrollbar.is_scrolled_down) { + pl.playlist.selection_handler.drag(null, false); // To clear last hover row + pl.playlist.start_drag_scroll('down'); + } + } + } + + pl.playlist.last_hover_item = /** @type {?PlaylistBaseHeader|?PlaylistRow} */ pl.playlist.get_item_under_mouse(x, y); + + if (!pl.playlist.trace_list(x, y)) { + action.Effect = PlaylistDropEffect.none; + } + else { + action.Effect = PlaylistDropEffect.copy; // * Wine/Linux drag n drop fix // action.Effect = this.filter_effect_by_modifiers(action.Effect); + } + + return action.Effect; + } + + /** + * Updates the focus state of an item and triggers a repaint if necessary. + * @param {boolean} is_focused - Whether the playlist is focused. + */ + on_focus(is_focused) { + if (!this.is_activated) { + return; + } + + if (pl.playlist.focused_item) { + pl.playlist.focused_item.is_focused = is_focused; + pl.playlist.focused_item.repaint(); + } + pl.playlist.is_in_focus = is_focused; + } + + /** + * Assigns album art to a header item and repaints if the art is not already loaded. + * @param {FbMetadbHandle} metadb - The metadb of the track. + * @param {number} art_id - The id for the album art is used to associate the album art with the specific header. + * @param {GdiBitmap} image - The album art image that was retrieved. + * @param {string} image_path - The file path where the album art image is stored. + */ + on_get_album_art_done(metadb, art_id, image, image_path) { + if (!this.is_activated) { + return; + } + + if (!image) { + image = null; + } + + /** @type {Array} */ + const items = pl.playlist.items_to_draw; + for (const item of items) { + if (item instanceof PlaylistHeader && (!item.is_art_loaded() && item.get_first_row().metadb.Compare(metadb))) { + item.assign_art(image); + item.repaint(); + } + } + } + + /** + * Handles changes in focus between items in the playlist, updating the focused item and scrolling to it if necessary. + * @param {number} playlist_idx - The index of the playlist that the focus change event is occurring in. + * @param {number} from_idx - The index of the previously focused item in the playlist. + * @param {number} to_idx - The index of the item that is gaining focus in the playlist. + */ + on_item_focus_change(playlist_idx, from_idx, to_idx) { + if (!this.is_activated) { + return; + } + + if (playlist_idx !== pl.playlist.cur_playlist_idx || pl.playlist.focused_item && pl.playlist.focused_item.idx === to_idx) { + return; + } + + if (pl.playlist.focused_item) { + pl.playlist.focused_item.is_focused = false; + } + + if (to_idx === -1) { + pl.playlist.focused_item = undefined; + } + else if (pl.playlist.cnt.rows.length && to_idx >= 0 && to_idx < pl.playlist.cnt.rows.length) { + to_idx = Math.min(to_idx, pl.playlist.cnt.rows.length - 1); + pl.playlist.focused_item = pl.playlist.cnt.rows[to_idx]; + pl.playlist.focused_item.is_focused = true; + } + + if (pl.playlist.focused_item) { + const from_row = from_idx === -1 ? null : pl.playlist.cnt.rows[from_idx]; + const playing_item_location = plman.GetPlayingItemLocation(); + if (!playing_item_location.IsValid || pl.playlist.on_playlist_items_removed) { // * Prevent scroll jump when removing items + return; + } + pl.playlist.scroll_to_row(from_row, pl.playlist.focused_item); + } + + pl.playlist.repaint(); + } + + /** + * Handles key down events when a key on the keyboard is pressed down. + * @param {number} vkey - The virtual key code. + */ + on_key_down(vkey) { + pl.playlist.key_down = true; + + const modifiers = { + ctrl: utils.IsKeyPressed(VK_CONTROL), + alt: utils.IsKeyPressed(VK_MENU), + shift: utils.IsKeyPressed(VK_SHIFT) + }; + pl.playlist.key_handler.invoke_key_action(vkey, modifiers); + } + + /** + * Handles key up events when a key on the keyboard is pressed up. + * @param {number} vkey - The virtual key code. + */ + on_key_up(vkey) { + pl.playlist.key_down = false; + } + + /** + * Handles the metadb changed event and updates the header and queried data. + * @param {FbMetadbHandleList} handle_list - The metadb of the tracks. + * @param {boolean} fromhook - Whether the `on_metadb_changed` event was triggered by a hook or not. + */ + on_metadb_changed(handle_list, fromhook) { + if (!this.is_activated) { + return; + } + + if (plSet.show_plman) { + pl.plman.on_playlist_modified(); + } + + const changedHandlesSet = new Set(handle_list.Convert().map(handle => handle.RawPath)); + + for (const item of pl.playlist.cnt.sub_items) { // Playlist headers + const shouldUpdate = item.header_image || item.hyperlinks_initialized; + const firstRowMetadbPath = item.get_first_row().metadb.RawPath; + if (shouldUpdate && changedHandlesSet.has(firstRowMetadbPath)) { + item.header_image = null; + item.reset_hyperlinks(); + } + } + + for (const item of pl.playlist.cnt.rows) { // Playlist rows + if (changedHandlesSet.has(item.metadb.RawPath)) { + item.reset_queried_data(); + } + } + } + + /** + * Handles mouse double click events. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_dblclk(x, y, m) { + if (BaseList.prototype.on_mouse_lbtn_dblclk.apply(pl.playlist, [x, y, m])) { + return true; + } + + const item = pl.playlist.get_item_under_mouse(x, y); + if (!item) { + return true; + } + + if (item instanceof PlaylistBaseHeader) { + if (item instanceof PlaylistDiscHeader) { + item.on_mouse_lbtn_dblclk(pl.playlist.collapse_handler); + } else { + item.on_mouse_lbtn_dblclk(x, y, m); + } + pl.playlist.repaint(); + } else if (item instanceof PlaylistRow) { + if (plSet.show_rating && item.rating_trace(x, y)) { + item.rating_click(x, y); + item.repaint(); + } + else { + plman.ExecutePlaylistDefaultAction(pl.playlist.cur_playlist_idx, item.idx); + } + } + + return true; + } + + /** + * Handles left mouse button down events and performs various actions based on the mouse position and key presses. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_down(x, y, m) { + if (plSet.show_plman) { + pl.plman.on_mouse_lbtn_down(x, y, m); + } + + if (BaseList.prototype.on_mouse_lbtn_down.apply(pl.playlist, [x, y, m])) { + return true; + } + + const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); + const shift_pressed = utils.IsKeyPressed(VK_SHIFT); + + /** @type {PlaylistBaseHeader|PlaylistRow} */ + // @ts-ignore + const item = pl.playlist.trace_list(x, y) ? pl.playlist.get_item_under_mouse(x, y) : undefined; + pl.playlist.last_hover_item = item; + pl.playlist.last_pressed_coord.x = x; + pl.playlist.last_pressed_coord.y = y; + + if (item) { + if ((!grSet.hyperlinksCtrlClick || ctrl_pressed) && item instanceof PlaylistHeader) { + if (item.on_mouse_lbtn_down(x, y, m)) { + return true; // Was handled by hyperlinks + } + } + if (ctrl_pressed && shift_pressed && item instanceof PlaylistBaseHeader) { + pl.playlist.collapse_handler.toggle_collapse(item); + pl.playlist.mouse_down = false; + } + else if (shift_pressed + || (item instanceof PlaylistRow && !item.is_selected() + || item instanceof PlaylistBaseHeader && !item.is_completely_selected())) { + pl.playlist.selection_handler.update_selection(item, ctrl_pressed, shift_pressed); + } + else { + // Indicates the need to update selection on on_mouse_lbtn_up + pl.playlist.mouse_on_item = true; + } + } + else { + pl.playlist.selection_handler.clear_selection(); + } + + pl.playlist.repaint(); + + return true; + } + + /** + * Handles mouse left button up events and updates the selection of an item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_up(x, y, m) { + if (plSet.show_plman) { + pl.plman.on_mouse_lbtn_up(x, y, m); + } + + const was_double_clicked = pl.playlist.mouse_double_clicked; + + if (BaseList.prototype.on_mouse_lbtn_up.apply(pl.playlist, [x, y, m])) { + return true; + } + + pl.playlist.last_pressed_coord = { + x: undefined, + y: undefined + }; + + if (was_double_clicked) { + return true; + } + + pl.playlist.last_hover_item = undefined; + + // Drag is handled in on_drag_drop + if (!pl.playlist.selection_handler.is_dragging() && pl.playlist.mouse_on_item) { + const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); + const shift_pressed = utils.IsKeyPressed(VK_SHIFT); + /** @type {PlaylistRow|PlaylistBaseHeader} */ + // @ts-ignore + const item = pl.playlist.get_item_under_mouse(x, y); + if (item) { + pl.playlist.selection_handler.update_selection(item, ctrl_pressed, shift_pressed); + } + } + + pl.playlist.mouse_on_item = false; + pl.playlist.repaint(); + + return true; + } + + /** + * Handles mouse leave events and checks if an internal drag and drop operation is active. + * @override + */ + on_mouse_leave() { + if (plSet.show_plman) { + pl.plman.on_mouse_leave(); + } + + if (pl.playlist.selection_handler.is_internal_drag_n_drop_active() + && pl.playlist.selection_handler.is_dragging() + && !pl.playlist.drag_event_invoked) { + // Workaround for the following issues: + // #1 if you move too fast out of the panel, then drag_enter is not invoked (thus we need to clear mouse state here). + // #2 on_mouse_leave sometimes generated during internal drag_over (so we need to ignore it). + pl.playlist.selection_handler.disable_drag(); + pl.playlist.drag_event_invoked = false; + pl.playlist.mouse_in = false; + pl.playlist.mouse_down = false; + pl.playlist.repaint(); + } + + BaseList.prototype.on_mouse_leave.apply(pl.playlist); + + if (pl.playlist.scrollbar.b_is_dragging) { + pl.playlist.scrollbar.b_is_dragging = false; + pl.playlist.scrollbar.desiredScrollPosition = undefined; + } + } + + /** + * Handles mouse movement events and performs various actions based on the position of the mouse. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_move(x, y, m) { + if (plSet.show_plman) { + pl.plman.on_mouse_move(x, y, m); + } + + if (BaseList.prototype.on_mouse_move.apply(pl.playlist, [x, y, m])) { + return true; + } + + const right_spacing = SCALE(20); + const item = pl.playlist.get_item_under_mouse(x - right_spacing, y); + + if (item instanceof PlaylistHeader) { + if (item.on_mouse_move(x, y, m)) return true; + } + else if (item instanceof PlaylistRow) { + item.rowTooltip(x, y); + if (item.on_mouse_move(x, y, m)) return true; + } + else grm.ui.styledTooltipReady = false; + + if (!pl.playlist.mouse_down) { + return true; + } + + if (!pl.playlist.selection_handler.is_dragging() && pl.playlist.last_hover_item) { + const drag_diff = Math.sqrt(((pl.playlist.last_pressed_coord.x - x) ** 2 + (pl.playlist.last_pressed_coord.y - y) ** 2)); + if (drag_diff >= 7) { + pl.playlist.last_pressed_coord = { + x: undefined, + y: undefined + }; + pl.playlist.last_hover_item = pl.playlist.get_item_under_mouse(x, y); + + pl.playlist.selection_handler.perform_internal_drag_n_drop(); + } + } + + return true; + } + + /** + * Handles right mouse button down events and updates the selection. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_rbtn_down(x, y, m) { + if (plSet.show_plman) { + pl.plman.on_mouse_rbtn_down(x, y, m); + } + + if (!pl.playlist.cnt.rows.length) { + return; + } + + if (pl.playlist.is_scrollbar_visible && pl.playlist.scrollbar.trace(x, y)) { + return; + } + + const item = pl.playlist.trace_list(x, y) ? pl.playlist.get_item_under_mouse(x, y) : undefined; + if (!item) { + pl.playlist.selection_handler.clear_selection(); + } + else if (item instanceof PlaylistRow && !item.is_selected() + || item instanceof PlaylistBaseHeader && !item.is_completely_selected()) { + pl.playlist.selection_handler.update_selection(item); + } + + pl.playlist.repaint(); + } + + /** + * Handles right mouse button up events and displays a context menu with various options. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_rbtn_up(x, y, m) { + const was_plman_displayed = plSet.show_plman; + + if (was_plman_displayed !== plSet.show_plman) { + /** + * Adjusts the size and position of a playlist based on whether or not the playlist manager is being shown. + * @param {boolean} show - Whether to show the playlist manager or not. + * @private + */ + const toggle_plman = (show) => { + const plman_gap_h = SCALE(plSet.row_h) + SCALE(4); + const new_playlist_h = show ? (pl.playlist.h - plman_gap_h) : (pl.playlist.h + plman_gap_h); + pl.playlist.y = this.y + (show ? plman_gap_h : 0); + pl.playlist.on_size(pl.playlist.w, new_playlist_h, pl.playlist.x, pl.playlist.y); + window.Repaint(); // Easier to repaint everything + } + toggle_plman(plSet.show_plman); + } + + if (plSet.show_plman) { + pl.plman.on_mouse_rbtn_up(x, y, m); + } + + if (pl.playlist.trace(x, y)) { + if (BaseList.prototype.on_mouse_rbtn_up.apply(pl.playlist, [x, y, m])) { + return true; + } + + const metadb = utils.IsKeyPressed(VK_CONTROL) ? (fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem()) : fb.GetFocusItem(); + const has_selected_item = pl.playlist.selection_handler.has_selected_items(); + const is_cur_playlist_empty = !pl.playlist.cnt.rows.length; + const cmm = new ContextMainMenu(); + + // * Top menu options Playlist submenu + cmm.appendItem('Playlist options menu', () => { + if (grm.ui.displayPlaylist || grm.ui.displayPlaylistArtwork) { + grm.topMenu.topMenuOptions(grm.ui.state.mouse_x, grm.ui.state.mouse_y, true, true); + } + }); + cmm.separator(); + + if (grSet.layout === 'default' && grSet.theme.startsWith('custom')) { + cmm.appendItem('Edit custom theme', () => { + grm.ui.displayCustomThemeMenu = true; + grm.ui.displayPanel('playlist'); + grm.cthMenu.initCustomThemeMenu('pl_bg'); + window.Repaint(); + }); + cmm.separator(); + } + + if (grSet.layout === 'default' && !grm.ui.displayLibrarySplit()) { + if (grm.ui.displayPlaylist && !grm.ui.displayBiography && !grm.ui.displayLyrics) { + cmm.appendItem(grm.ui.displayPlaylist && grSet.playlistLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { + grSet.playlistLayout = grSet.playlistLayout === 'normal' ? 'full' : 'normal'; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + pl.call.on_size(grm.ui.ww, grm.ui.wh); + grm.jSearch.on_size(); + window.Repaint(); + }); + cmm.separator(); + } + else if (grm.ui.displayBiography && grm.ui.displayPlaylist) { + cmm.appendItem(grm.ui.displayPlaylist && grm.ui.displayBiography && grSet.biographyLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { + if (grSet.biographyLayout === 'normal') { + grSet.biographyLayout = 'full'; + grm.ui.displayPlaylist = false; + } else { + grSet.biographyLayout = 'normal'; + grm.ui.displayPlaylist = true; + } + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + grm.ui.initBiographyLayout(); + }); + cmm.separator(); + } + else if (grm.ui.displayLyrics) { + cmm.appendItem(grm.ui.displayLyrics && grSet.lyricsLayout === 'normal' ? 'Change layout to full' : 'Change layout to normal', () => { + grSet.lyricsLayout = grSet.lyricsLayout === 'normal' ? 'full' : 'normal'; + grm.ui.displayPlaylist = !grm.ui.displayPlaylist; + if (grSet.panelWidthAuto) { + grm.ui.initPanelWidthAuto(); + } + grm.ui.resizeArtwork(true); + window.Repaint(); + }); + cmm.separator(); + } + } + + if (fb.IsPlaying) { + cmm.appendItem('Show now playing', () => { + pl.playlist.show_now_playing(); + }); + } + + if (pl.history.canBack()) { + cmm.appendItem('Previous playlist state', () => { + pl.history.back(); + }); + } + if (pl.history.canForward()) { + cmm.appendItem('Next playlist state', () => { + pl.history.forward(); + }); + } + + if (!is_cur_playlist_empty) { + cmm.appendItem('Refresh playlist \tF5', () => { + PlaylistHeader.img_cache.clear(); + pl.playlist.initialize_list(); + pl.playlist.scroll_to_focused(); + }); + + if (pl.playlist.queue_handler && pl.playlist.queue_handler.has_items()) { + cmm.appendItem('Flush playback queue \tCtrl+Shift+Q', () => { + pl.playlist.queue_handler.flush(); + }); + } + cmm.separator(); + } + + pl.playlist.ctx_menu_pltools(cmm); + + pl.playlist.ctx_menu_edit(cmm); + + if (!is_cur_playlist_empty) { + if (!cmm.empty()) { + cmm.separator(); + } + + if (pl.playlist.collapse_handler) { + pl.playlist.ctx_menu_collapse(cmm); + } + + pl.playlist.ctx_menu_appearance(cmm); + + if (plSet.show_header) { + PlaylistHeader.grouping_handler.append_menu_to(cmm, () => { + pl.playlist.initialize_list(); + pl.playlist.scroll_to_focused_or_now_playing(); + pl.playlist.repaint(); + }); + } + + pl.playlist.ctx_menu_sort(cmm); + + if (grSet.showWeblinks) { + pl.playlist.ctx_menu_weblinks(cmm, metadb); + } + + if (has_selected_item) { + pl.playlist.ctx_menu_send(cmm); + + cmm.separator(); + cmm.appendItem('Write theme to tags', () => { + WriteThemeTags(); + }); + cmm.appendItem('Write album statistics to tags', () => { + pl.playlist.meta_handler.write_album_stats_to_tags(); + }); + pl.playlist.ctx_menu_playlist_stats(cmm); + } + } + else { + // Empty playlist + + if (!cmm.empty()) { + cmm.separator(); + } + + const appear = new ContextMenu('Appearance'); + cmm.append(appear); + + appear.appendItem('Show playlist manager', () => { + plSet.show_plman = !plSet.show_plman; + }, { is_checked: plSet.show_plman }); + + pl.playlist.append_scrollbar_visibility_context_menu_to(appear); + } + + // -------------------------------------------------------------- // + // * Context Menu Manager + + if (has_selected_item) { + if (!cmm.empty()) { + cmm.separator(); + } + + const ccmm = new ContextFoobarMenu(plman.GetPlaylistSelectedItems(pl.playlist.cur_playlist_idx)); + cmm.append(ccmm); + } + + // -------------------------------------------------------------- // + // * System + + if (utils.IsKeyPressed(VK_SHIFT)) { + grm.ctxMenu.contextMenuDefault(cmm); + } + + grm.ui.activeMenu = true; + cmm.execute(x, y); + grm.ui.activeMenu = false; + + pl.playlist.repaint(); + return true; + } + + return true; + } + + /** + * Handles mouse wheel events. + * @param {number} step - The amount of scrolling that occurred on the mouse wheel. + */ + on_mouse_wheel(step) { + pl.playlist.on_mouse_wheel(step); + } + + /** + * Handles notifications and synchronizes the state of a grouping handler. + * Called in other panels after window.NotifyOthers is executed. + * @param {string} name - The name of the notification event that triggered the function. + * @param {*} info - The information about the state of the sync group. + */ + on_notify_data(name, info) { + if (name === 'sync_group_query_state') { + if (!window.IsVisible) { + // Need to reinitialize grouping_handler manually, since most of the callbacks are ignored when panel is hidden + PlaylistHeader.grouping_handler.on_playlists_changed(); + PlaylistHeader.grouping_handler.set_active_playlist(plman.GetPlaylistName(plman.ActivePlaylist)); + PlaylistHeader.grouping_handler.sync_state(info); + } + else { + PlaylistHeader.grouping_handler.sync_state(info); + + pl.playlist.initialize_list(); + pl.playlist.scroll_to_focused_or_now_playing(); + } + } + } + + /** + * Handles when Per-track dynamic info (stream track titles etc) changes and resets queried data + * and hyperlinks for each row and header, and then updates the playlist. + */ + on_playback_dynamic_info_track() { + if (!this.is_activated) { + return; + } + + for (const item of pl.playlist.cnt.rows) { + item.reset_queried_data(); + } + + for (const header of pl.playlist.cnt.sub_items) { + header.reset_hyperlinks(); + } + + pl.playlist.repaint(); + } + + /** + * Updates the currently playing track in the playlist and performs actions such as clearing the title text, + * setting the track as playing, collapsing the playlist if necessary, and scrolling to the currently playing track. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + on_playback_new_track(metadb) { + if (!this.is_activated) { + return; + } + + pl.plman.hover_alpha = 0; // * Prevent Playlist manager bg flashing when bg color changes + + if (pl.playlist.playing_item) { + pl.playlist.playing_item.is_playing = false; + pl.playlist.playing_item.clear_title_text(); + pl.playlist.playing_item = undefined; + } + + const playing_item_location = plman.GetPlayingItemLocation(); + if (playing_item_location.IsValid && playing_item_location.PlaylistIndex === pl.playlist.cur_playlist_idx) { + pl.playlist.playing_item = pl.playlist.cnt.rows[playing_item_location.PlaylistItemIndex]; + if (pl.playlist.playing_item) { + pl.playlist.playing_item.is_playing = true; + pl.playlist.playing_item.clear_title_text(); + } + + if (pl.playlist.collapse_handler && plSet.show_header && (plSet.auto_collapse || plSet.collapse_on_start)) { + pl.playlist.selection_handler.clear_selection(); + pl.playlist.collapse_handler.collapse_all_but_now_playing(); + pl.playlist.scroll_to_now_playing(); + } + } + + if (grSet.playlistAutoScrollNowPlaying || fb.PlaybackFollowCursor || fb.CursorFollowPlayback) { + setTimeout(() => { // * Wait until new album art / disc art loaded and other things finished for smoother auto-scrolling + pl.playlist.show_now_playing(); + }, grm.ui.newTrackFetchingDone + 200); + } + + pl.playlist.repaint(); + } + + /** + * Handles when playback pauses and repaints the currently playing item. + * @param {boolean} state - The new playback state. + */ + on_playback_pause(state) { + if (!this.is_activated) { + return; + } + + if (pl.playlist.playing_item) { + pl.playlist.playing_item.repaint(); + } + } + + /** + * Handles when playback items are send in queue, initializes the queue handler and updates the playlist. + * @param {number} origin - The origin of the change. + */ + on_playback_queue_changed(origin) { + if (!this.is_activated) { + return; + } + + if (!pl.playlist.queue_handler) return; + pl.playlist.queue_handler.initialize_queue(); + pl.playlist.repaint(); + } + + /** + * Handles playback stop events and clears the title text of the currently playing item and updates its track number. + * @param {number} reason - The type of playback stop. + */ + on_playback_stop(reason) { + if (!this.is_activated) { + return; + } + + if (!pl.playlist.playing_item) return; + pl.playlist.playing_item.clear_title_text(); + pl.playlist.playing_item.is_playing = false; + pl.playlist.playing_item.repaint(); + } + + /** + * Handles and ensures that a playlist item is visible in the playlist if it is not already. + * @param {number} playlist_idx - The current playlist index. + * @param {number} playlistItemIndex - The playlist item index that needs to be ensured visible in the playlist. + */ + on_playlist_item_ensure_visible(playlist_idx, playlistItemIndex) { + if (!this.is_activated) { + return; + } + + if (playlist_idx !== pl.playlist.cur_playlist_idx) { + return; + } + + const row = pl.playlist.cnt.rows[playlistItemIndex]; + if (!row) { + return; + } + + if (!grm.ui.displayLibrarySplit()) pl.playlist.scroll_to_row(null, row); // * Prevent scroll to focused after drag and drop in split layout + } + + /** + * Handles when playlist items are added and sets the playlist sort order and updates the playlist. + * @param {number} playlist_idx - The current playlist index. + */ + on_playlist_items_added(playlist_idx) { + if (!this.is_activated) { + return; + } + + if (plSet.show_plman) { + pl.plman.on_playlist_modified(); + } + + if (playlist_idx !== pl.playlist.cur_playlist_idx) { + return; + } + if (grSet.playlistSortOrderAuto) grm.ui.setPlaylistSortOrder(); + + pl.playlist.debounced_initialize_and_repaint_list(); + } + + /** + * Handles when playlist items are reordered and updates the playlist. + * @param {number} playlist_idx - The current playlist index. + */ + on_playlist_items_reordered(playlist_idx) { + if (!this.is_activated) { + return; + } + + if (playlist_idx !== pl.playlist.cur_playlist_idx) { + return; + } + + pl.playlist.debounced_initialize_and_repaint_list(true); + } + + /** + * Handles when playlist items are removed and updates the playlist. + * @param {number} playlist_idx - The current playlist index. + */ + on_playlist_items_removed(playlist_idx) { + if (!this.is_activated) { + return; + } + + if (plSet.show_plman) { + pl.plman.on_playlist_modified(); + } + + if (playlist_idx !== pl.playlist.cur_playlist_idx) { + return; + } + + pl.playlist.debounced_initialize_and_repaint_list(); + } + + /** + * Handles when playlist item selection has been changed and updates the selection. + */ + on_playlist_items_selection_change() { + if (!this.is_activated) { + return; + } + + if (plSet.show_plman) { + pl.plman.on_playlist_modified(); + } + + if (!pl.playlist.mouse_in && !pl.playlist.key_down) { + pl.playlist.selection_handler.initialize_selection(); + } + } + + /** + * Handles when active playlist changes to another playlist index. + */ + on_playlist_switch() { + if (!this.is_activated) { + return; + } + + if (plSet.show_plman) { + pl.plman.on_playlist_modified(); + } + + if (pl.playlist.cur_playlist_idx !== plman.ActivePlaylist) { + plSet.scrollbar_pos = pl.playlist.scroll_pos_list[plman.ActivePlaylist] == null ? 0 : pl.playlist.scroll_pos_list[plman.ActivePlaylist]; + } + + pl.playlist.initialize_and_repaint_list(); + + if (pl.playlist.collapse_handler && plSet.show_header && (plSet.auto_collapse || plSet.collapse_on_start)) { + pl.playlist.collapse_handler.collapse_all_but_now_playing(); + } + } + + /** + * Handles changes in the playlists when content is added/removed/reordered/renamed. + */ + on_playlists_changed() { + if (!this.is_activated) { + return; + } + + if (plSet.show_plman) { + pl.plman.on_playlist_modified(); + } + + if ((plman.ActivePlaylist > plman.PlaylistCount || plman.ActivePlaylist === -1) && plman.PlaylistCount > 0) { + plman.ActivePlaylist = plman.PlaylistCount - 1; + } + + PlaylistHeader.grouping_handler.on_playlists_changed(); + PlaylistHeader.grouping_handler.set_active_playlist(plman.GetPlaylistName(plman.ActivePlaylist)); + + if (plman.ActivePlaylist !== pl.playlist.cur_playlist_idx) { + pl.playlist.initialize_and_repaint_list(); + } + + if (pl.playlist.collapse_handler && plSet.show_header && (plSet.auto_collapse || plSet.collapse_on_start)) { + pl.playlist.collapse_handler.collapse_all_but_now_playing(); + } + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-components.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-components.js new file mode 100644 index 00000000..550ae6e0 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-components.js @@ -0,0 +1,2414 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Components * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +////////////////////////////// +// * PLAYLIST LINKED LIST * // +////////////////////////////// +/** + * A class that represents a LinkedList data structure that's generic over T. + * @template T The type of elements in the linked list. + */ +class PlaylistLinkedList { + /** + * Creates the `PlaylistLinkedList` instance. + */ + constructor() { + /** @private @type {?PlaylistLinkedListNode} */ + this._back = null; + /** @private @type {?PlaylistLinkedListNode} */ + this._front = null; + /** @private @type {number} */ + this._size = 0; + /** @private @type {PlaylistLinkedListNode} End sentinel node */ + this.end_node = new PlaylistLinkedListNode(null, null, null); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Adds a node to a linked list and updates the previous and next pointers accordingly. + * @param {PlaylistLinkedListNode} node - A node in a linked list. + * @private + */ + _add_node(node) { + if (node.prev) { + node.prev.next = node; + } + else { + this._front = node; + } + + if (node.next) { + node.next.prev = node; + } + else { + this._back = node; + } + + ++this._size; + } + + /** + * Removes a node from a linked list and updates the previous and next pointers accordingly. + * @param {?PlaylistLinkedListNode} node - A node in a linked list. + * @private + */ + _remove_node(node) { + if (!node) { + return; + } + + if (node.prev) { + node.prev.next = node.next; + } + else { + this._front = node.next; + } + + if (node.next) { + node.next.prev = node.prev; + } + else { + this._back = node.prev; + } + + --this._size; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Pushes a new value to the back of the queue. + * @param {T} value - The value to push. + */ + push_back(value) { + this._add_node(new PlaylistLinkedListNode(value, this._back, null)); + } + + /** + * Pushes a new value to the front of the queue. + * @param {T} value - The value to push. + */ + push_front(value) { + this._add_node(new PlaylistLinkedListNode(value, null, this._front)); + } + + /** + * Removes the node at the front of the linked list and returns its value. + */ + pop_front() { + this._remove_node(this._front); + } + + /** + * Removes the node at the back of the linked list and returns its value. + */ + pop_back() { + this._remove_node(this._back); + } + + /** + * Gets the value at the front of the queue. + * @returns {T} The value at the front of the queue. + */ + front() { + return this._front.value; + } + + /** + * Gets the value at the back of the queue. + * @returns {T} The value at the back of the queue. + */ + back() { + return this._back.value; + } + + /** + * Gets the number of elements in the queue. + * @returns {number} The number of elements in the queue. + */ + length() { + return this._size; + } + + /** + * Gets an iterator for the beginning of the queue. + * @returns {PlaylistLinkedListIterator} An iterator for the beginning of the queue. + */ + begin() { + return new PlaylistLinkedListIterator(this, this._front || this.end_node); + } + + /** + * Gets an iterator for the end of the queue. + * @returns {PlaylistLinkedListIterator} An iterator for the end of the queue. + */ + end() { + return new PlaylistLinkedListIterator(this, this.end_node); + } + + /** + * Clears all elements from the linked list. + * @returns {void} + */ + clear() { + this._back = null; + this._front = null; + this._size = 0; + } + + /** + * Removes the value at the given iterator. + * @param {PlaylistLinkedListIterator} iterator - The iterator to remove. + * @throws {InvalidTypeError} Throws an InvalidTypeError if the provided argument is not an instance of PlaylistLinkedListIterator. + * @throws {LogicError} Throws a LogicError if the iterator belongs to a different list. + * @throws {LogicError} Throws a LogicError if trying to remove an iterator that points to the end of the list. + */ + remove(iterator) { + if (!(iterator instanceof PlaylistLinkedListIterator)) { + throw new InvalidTypeError(iterator, typeof iterator, 'Iterator'); + } + + if (iterator.parent !== this) { + throw new LogicError('Using iterator from a different list'); + } + + if (iterator.compare(this.end())) { + throw new LogicError('Removing invalid iterator'); + } + + this._remove_node(iterator.cur_node); + + iterator.cur_node = this.end_node; + } + // #endregion +} + + +/** + * A class that encapsulates a linked list iterator and is used to iterate through the list. + */ +class PlaylistLinkedListIterator { + /** + * Creates the `PlaylistLinkedListIterator` instance. + * @param {PlaylistLinkedList} parent - The parent of the node. + * @param {PlaylistLinkedListNode} node - The node that is pointed to by the iterator. + */ + constructor(parent, node) { + /** @private @type {PlaylistLinkedList} */ + this.parent = parent; + /** @private @type {PlaylistLinkedListNode} */ + this.cur_node = node; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Gets the value of the current element. + * @returns {*} The value of the current element. + * @throws {LogicError} Throws a LogicError if trying to access the value of the end node. + */ + value() { + if (this.cur_node === this.parent.end_node) { + throw new LogicError('Accessing end node'); + } + + return this.cur_node.value; + } + + /** + * Increments the iterator to the next element. + * @returns {void} + * @throws {LogicError} Throws a LogicError if the iterator is out of bounds when incrementing. + */ + increment() { + if (this.cur_node === this.parent.end_node) { + throw new LogicError('Iterator is out of bounds'); + } + + this.cur_node = this.cur_node.next; + if (!this.cur_node) { + this.cur_node = this.parent.end_node; + } + } + + /** + * Decrements the iterator to the previous element. + * @returns {void} + * @throws {LogicError} Throws a LogicError if the iterator is out of bounds when decrementing. + */ + decrement() { + if (this.cur_node === this.parent._front) { + throw new LogicError('Iterator is out of bounds'); + } + + this.cur_node = this.cur_node === this.parent.end_node ? this.parent._back : this.cur_node.prev; + } + + /** + * Compares this iterator to another iterator to check if they are at the same position. + * @param {PlaylistLinkedListIterator} iterator - The other iterator to compare to. + * @returns {boolean} True if both iterators point to the same node, false otherwise. + * @throws {LogicError} Throws a LogicError if comparing iterators from different lists. + */ + compare(iterator) { + if (iterator.parent !== this.parent) { + throw new LogicError('Comparing iterators from different lists'); + } + return iterator.cur_node === this.cur_node; + } + // #endregion +} + + +/** + * A class that defines a Node class for the linked list. + * @template T The type of the value stored in the node. + */ +class PlaylistLinkedListNode { + /** + * Creates the `PlaylistLinkedListNode` instance. + * @param {T} value - The value of the node. + * @param {?PlaylistLinkedListNode} prev - The previous node in the linked list. + * @param {?PlaylistLinkedListNode} next - The next node in the linked list. + */ + constructor(value, prev = null, next = null) { + this.value = value; + this.prev = prev; + this.next = next; + } +} + + +//////////////////////// +// * PLAYLIST IMAGE * // +//////////////////////// +/** + * A class that manages playlist images and implements debouncing to limit the rate + * at which the fetch operation is invoked. + */ +class PlaylistImage { + /** + * Creates the `PlaylistImage` instance. + * @param {number} [debounceTime] - The number of milliseconds to delay the getHeaderArtwork call after the last invocation. + */ + constructor(debounceTime = 500) { + /** @private @type {number} */ + this.debounceTime = debounceTime; + /** @private @type {Function} */ + this.debouncedGetAlbumArt = this._debounce(this.getHeaderArtwork.bind(this)); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Creates a debounced function that delays invoking the provided function until after wait milliseconds have elapsed + * since the last time the debounced function was invoked. The debounced function comes with a cancel method to cancel delayed invocations. + * @param {Function} func - The function to debounce. + * @returns {Function} A new, debounced function. + * @private + */ + _debounce(func) { + let timeout; + return (...args) => { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, this.debounceTime); + }; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Gets album art for playlist headers and is only executed if certain conditions are met. + * @param {Array} items - The array of PlaylistHeader objects for which album art needs to be fetched. + */ + getAlbumArt(items) { + if (!plSet.show_album_art || plSet.use_compact_header) { + return; + } + this.debouncedGetAlbumArt(items); + } + + /** + * Checks if the album art is loaded for each item in the given array, and if not, + * retrieves the artwork asynchronously and assigns it to the item. Typically called in a debounce. + * @param {PlaylistHeader[]} items - The array of PlaylistHeader objects to check and potentially update with album art. + */ + getHeaderArtwork(items) { + for (const item of items) { + if (!(item instanceof PlaylistHeader)) continue; + + const { metadb } = item.get_first_row(); + const isArtLoaded = item.is_art_loaded(); + + if (isArtLoaded) continue; + + const cached_art = PlaylistHeader.img_cache.get_image_for_meta(metadb); + + if (cached_art) { + item.assign_art(cached_art); + item.repaint(); + } + else if (!pl.thumbnail_list.has(metadb)) { + // TODO: Once this has been better tested, remove on_get_album_art_done callback from this file, and probably gr-callbacks.js as well + // utils.GetAlbumArtAsync(window.ID, metadb, g_album_art_id.front); + pl.thumbnail_list.add(metadb); + utils.GetAlbumArtAsyncV2(window.ID, metadb, AlbumArtId.front).then((artResult) => { + if (!item.is_art_loaded()) { + item.assign_art(artResult.image); + item.repaint(); + } + }); + } + } + } + // #endregion +} + + +/** + * A class that manages the playlist image cache for storing images associated with metadata handles. + */ +class PlaylistImageCache { + /** + * Creates the `PlaylistImageCache` instance. + * @param {number} maxCacheSize - The maximum size of the cache. + */ + constructor(maxCacheSize) { + /** @private @type {PlaylistLinkedList} */ + this.queue = new PlaylistLinkedList(); + /** @private @type {Map} */ + this.cache = new Map(); + /** @private @type {number} */ + this.maxCacheSize = maxCacheSize; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Adds an image to the cache for the given metadata handle. + * @param {GdiBitmap} img - The image to add. + * @param {FbMetadbHandle} metadb - The metadata handle of the track. + */ + add_image_for_meta(img, metadb) { + const metadbPath = metadb.Path; + const cacheItem = this.cache.get(metadbPath); + + if (cacheItem) { + cacheItem.img = img; + this.move_item_to_top(cacheItem); + } else { + this.queue.push_front(metadb); + const queueIterator = this.queue.begin(); + + this.cache.set(metadbPath, { + metadb, + img, + queueIterator + }); + + if (this.queue.length() > this.maxCacheSize) { + const lastMetadb = this.queue.back(); + this.cache.delete(lastMetadb.Path); + this.queue.popBack(); + } + } + } + + /** + * Gets the image for the given metadata handle from the cache. + * @param {FbMetadbHandle} metadb - The metadata handle of the track. + * @returns {?GdiBitmap} The image for the given metadata handle, or undefined if not loaded. + */ + get_image_for_meta(metadb) { + const metadbPath = metadb.Path; + const cacheItem = this.cache.get(metadbPath); + + if (!cacheItem) return undefined; + + this.move_item_to_top(cacheItem); + return cacheItem.img; + } + + /** + * Clears the cache and the queue. + */ + clear() { + this.cache.clear(); + this.queue.clear(); + } + + /** + * Moves a cache item to the top of the queue. + * @param {object} cacheItem - The cache item to move. + */ + move_item_to_top(cacheItem) { + this.queue.remove(cacheItem.queueIterator); + this.queue.push_front(cacheItem.metadb); + cacheItem.queueIterator = this.queue.begin(); + } + // #endregion +} + + +//////////////////////////// +// * PLAYLIST SCROLLBAR * // +//////////////////////////// +/** + * A class that creates the scrollbar with the ScrollBarPart object and handles scrollbar events. + */ +class PlaylistScrollbar { + /** + * Creates the `PlaylistScrollbar` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} row_h - The height of the row that this scrollbar occupies. + * @param {boolean} fn_redraw - Called to redraw the list on the next draw. + */ + constructor(x, y, w, h, row_h, fn_redraw) { + /** @public @type {number} */ + this.x = x; + /** @public @type {number} */ + this.y = y; + /** @public @type {number} */ + this.w = w; + /** @public @type {number} */ + this.h = h; + + /** @private @type {number} */ + this.row_h = row_h; + /** @private @type {number} */ + this.rows_drawn = 0; // Visible list size in rows (might be float) + /** @private @type {number} */ + this.row_count = 0; // All rows in associated list + + /** @private @type {Function} Callback for list redraw */ + this.fn_redraw = fn_redraw; + /** @private @type {boolean} */ + this.draw_timer = false; + /** @private @type {object} */ + this.sb_parts = this.sb_parts || {}; + + // * Buttons + /** @private @type {number} */ + this.btn_h = 0; + /** @private @type {number} */ + this.thumb_h = 0; + /** @private @type {number} Upper y */ + this.thumb_y = 0; + + /** @public @type {boolean} */ + this.in_sbar = false; + /** @public @type {boolean} */ + this.b_is_dragging = false; + /** @public @type {boolean} */ + this.is_scrolled_down = false; + /** @public @type {boolean} */ + this.is_scrolled_up = true; + /** @private @type {number} How far should the thumb move, when the list shifts by one row. */ + this.drag_distance_per_row = 0; + /** @private @type {number} Dragging. */ + this.initial_drag_y = 0; + + /** @public @type {number} Lines shifted in list (float). */ + this.scroll = 0; + /** @public @type {number} */ + this.desiredScrollPosition = undefined; + /** @public @type {number} */ + this.lastScrollPosition = undefined; + /** @public @type {boolean} */ + this.scrollbar_wheel_scroll_page = plSet.scrollbar_wheel_scroll_page; + /** @public @type {number} Space between sb_parts (arrows). */ + this.scrollbar_h = 0; + /** @public @type {number} Not visible rows (row_count - rows_drawn). */ + this.scrollable_lines = 0; + /** @public @type {number} Space for thumb to travel (scrollbar_h - thumb_h). */ + this.scrollbar_travel = 0; + + /** @private @type {array} */ + this.scrollbar_images = {}; + /** @private @type {string} */ + this.cur_part_key = null; + /** @private @type {string} */ + this._alpha_timer_internal = null; + + // * Timers + /** @private @type {number} */ + this.throttled_scroll_y = 0; + /** @private @type {number} */ + this.timer_shift = null; + /** @private @type {number} */ + this.timer_shift_count = null; + /** @private @type {number} */ + this.timer_stop_y = -1; + /** @private @type {number} */ + this.smoothScrollTimer = null; + + // * Helpers + /** + * Applies an easing effect to a given value. + * @type {Function} + * @param {number} x - The absolute progress of the animation in the bounds of 0 (beginning of the animation) and 1 (end of animation). + * @returns {number} The interpolated value. + * @private + */ + this.easeOut = (x) => 1 - (1 - x) ** 3; + + /** + * Scrolls to the specified scroll position throttled. + * @type {Function} + * @private + */ + this.throttled_scroll_to = Throttle(() => { + this.smooth_scroll_to((this.throttled_scroll_y - this.btn_h) / this.drag_distance_per_row); + }, 1000 / 60); + + this._create_scrollbar_images(); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Starts the alpha timer. + * @private + */ + _startAlphaTimer() { + const hoverInStep = 50; + const hoverOutStep = 15; + const downOutStep = 50; + + if (!this._alpha_timer_internal) { + this._alpha_timer_internal = setInterval(() => { + for (const part in this.sb_parts) { + const item = this.sb_parts[part]; + switch (item.state) { + case 'normal': + item.hover_alpha = Math.max(0, item.hover_alpha -= hoverOutStep); + item.hot_alpha = Math.max(0, item.hot_alpha -= hoverOutStep); + item.pressed_alpha = part === 'thumb' ? Math.max(0, item.pressed_alpha -= hoverOutStep) : Math.max(0, item.pressed_alpha -= downOutStep); + break; + case 'hover': + item.hover_alpha = Math.min(255, item.hover_alpha += hoverInStep); + item.hot_alpha = Math.max(0, item.hot_alpha -= hoverOutStep); + item.pressed_alpha = Math.max(0, item.pressed_alpha -= downOutStep); + break; + case 'pressed': + item.hover_alpha = 0; + item.hot_alpha = 0; + item.pressed_alpha = 255; + break; + case 'hot': + item.hover_alpha = Math.max(0, item.hover_alpha -= hoverOutStep); + item.hot_alpha = Math.min(255, item.hot_alpha += hoverInStep); + item.pressed_alpha = Math.max(0, item.pressed_alpha -= downOutStep); + break; + } + // console.log(i, item.state, item.hover_alpha , item.pressed_alpha , item.hot_alpha); + // item.repaint(); + } + + this.repaint(); + + const alpha_in_progress = Object.values(this.sb_parts).some((item) => + (item.hover_alpha > 0 && item.hover_alpha < 255) + || (item.pressed_alpha > 0 && item.pressed_alpha < 255) + || (item.hot_alpha > 0 && item.hot_alpha < 255)); + + if (!alpha_in_progress) { + this._stopAlphaTimer(); + } + }, 25); + } + } + + /** + * Stops and clears the alpha timer. + * @private + */ + _stopAlphaTimer() { + if (this._alpha_timer_internal) { + clearInterval(this._alpha_timer_internal); + this._alpha_timer_internal = null; + } + } + + /** + * Creates images for the scrollbar up and down buttons. + * @private + */ + _create_scrollbar_images() { + if (this.scrollbar_images.length > 0) { + return; + } + + const fontSegoeUI = pl.font.scrollbar; + + const ico_back_colors = + [ + pl.col.bg, + pl.col.bg, + pl.col.bg, + pl.col.bg + ]; + + const ico_fore_colors = + [ + pl.col.sbar_btn_normal, + pl.col.sbar_btn_hovered, + pl.col.sbar_btn_hovered, + pl.col.sbar_btn_normal + ]; + + const btn = + { + lineUp: { + ico: '\uE010', + font: fontSegoeUI, + w: this.w, + h: this.w + }, + lineDown: { + ico: '\uE011', + font: fontSegoeUI, + w: this.w, + h: this.w + } + }; + + this.scrollbar_images = []; + + for (const i in btn) { + const item = btn[i]; + const { w, h } = item; + // const m = 2; + const stateImages = []; // 0=normal, 1=hover, 2=down, 3=hot; + + for (let s = 0; s < 4; s++) { + const img = gdi.CreateImage(w, h); + const grClip = img.GetGraphics(); + + const icoColor = ico_fore_colors[s]; + // const backColor = ico_back_colors[s]; + + // Don't really need this button backgrounds + // if (i === 'lineUp') { + // grClip.FillSolidRect(m, 0, w - m * 2, h - 1, backColor); + // } + // else if (i === 'lineDown') { + // grClip.FillSolidRect(m, 1, w - m * 2, h - 1, backColor); + // } + + grClip.SetSmoothingMode(SmoothingMode.HighQuality); + grClip.SetTextRenderingHint(TextRenderingHint.AntiAliasGridFit); + + const btn_format = Stringformat.h_align_center | Stringformat.v_align_far; + if (i === 'lineDown') { + grClip.DrawString(item.ico, item.font, icoColor, 0, RES._4K ? -25 : -12, w, h, btn_format); + } + else if (i === 'lineUp') { + grClip.DrawString(item.ico, item.font, icoColor, 0, 0, w, h, btn_format); + } + + img.ReleaseGraphics(grClip); + stateImages[s] = img; + } + + this.scrollbar_images[i] = + { + normal: stateImages[0], + hover: stateImages[1], + pressed: stateImages[2], + hot: stateImages[3] + }; + } + } + + /** + * Creates images for the scrollbar thumb. + * @param {number} thumb_w - The width of the scrollbar thumb. + * @param {number} thumb_h - The height of the scrollbar thumb. + * @private + */ + _create_dynamic_scrollbar_images(thumb_w, thumb_h) { + const thumb_colors = + [ + pl.col.sbar_thumb_normal, + pl.col.sbar_thumb_hovered, + pl.col.sbar_thumb_drag + ]; + + const m = 2; + const stateImages = []; // 0=normal, 1=hover, 2=down; + + for (let s = 0; s <= 2; s++) { + const img = gdi.CreateImage(thumb_w, thumb_h); + const grClip = img.GetGraphics(); + + const color = thumb_colors[s]; + grClip.FillSolidRect(m, 0, thumb_w - m * 2, thumb_h, color); + + img.ReleaseGraphics(grClip); + stateImages[s] = img; + } + + this.scrollbar_images.thumb = + { + normal: stateImages[0], + hover: stateImages[1], + pressed: stateImages[2] + }; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the scrollbar. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + paint(gr) { + gr.SetSmoothingMode(SmoothingMode.None); // Disable anti-aliasing, otherwise there will be an ugly 1px outline in style blending + + for (const part in this.sb_parts) { + const item = this.sb_parts[part]; + const { x, y, w, h } = item; + + gr.DrawImage(item.img_normal, x, y, w, h, 0, 0, w, h, 0, 255); + switch (part) { + case 'lineUp': + case 'lineDown': + gr.DrawImage(item.img_hot, x, y, w, h, 0, 0, w, h, 0, item.hot_alpha); + gr.DrawImage(item.img_hover, x, y, w, h, 0, 0, w, h, 0, item.hover_alpha); + gr.DrawImage(item.img_pressed, x, y, w, h, 0, 0, w, h, 0, item.pressed_alpha); + break; + + case 'thumb': + gr.DrawImage(item.img_hover, x, y, w, h, 0, 0, w, h, 0, item.hover_alpha); + gr.DrawImage(item.img_pressed, x, y, w, h, 0, 0, w, h, 0, item.pressed_alpha); + break; + } + } + } + + /** + * Updates the scrollbar via repaint. + */ + repaint() { + window.RepaintRect(this.x - (RES._4K ? 13 : 6), this.y, this.w, this.h); + } + + /** + * Flushes the scrollbar position. + */ + flush() { + if (this.desiredScrollPosition !== undefined) { + this.scroll_to(this.desiredScrollPosition); + this.desiredScrollPosition = undefined; + } + } + + /** + * Resets the current scroll of scrollbar. + */ + reset() { + this.flush(); + this._stopAlphaTimer(); + this.stop_shift_timer(); + this.scroll = 0; + this.calc_params(); + } + + /** + * Checks if the mouse is within the boundaries of the scrollbar. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + trace(x, y) { + return x + SCALE(10) > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; + } + + /** + * Sets the window parameters for the scrollbar. + * @param {number} rows_drawn - The number of rows drawn. + * @param {number} row_count - The total number of rows. + */ + set_window_param(rows_drawn, row_count) { + this.rows_drawn = rows_drawn; + this.row_count = row_count; + this.calc_params(); + this.create_parts(); + } + + /** + * Calculates the scrollbar parameters. + */ + calc_params() { + this.btn_h = this.w; + // * Draw info + this.scrollbar_h = this.h - this.btn_h * 2; + this.thumb_h = Math.max(Math.round(this.scrollbar_h * this.rows_drawn / this.row_count), RES._4K ? 45 : 30); + this.scrollbar_travel = this.scrollbar_h - this.thumb_h; + // * Scrolling info + this.scrollable_lines = this.row_count - this.rows_drawn; + this.thumb_y = this.btn_h + this.scroll * this.scrollbar_travel / this.scrollable_lines; + this.drag_distance_per_row = this.scrollbar_travel / this.scrollable_lines; + } + + /** + * Creates the button and thumb scrollbar parts. + */ + create_parts() { + this._create_dynamic_scrollbar_images(this.w, this.thumb_h); + + const { x, y, w, h } = this; + + this.sb_parts = { + lineUp: new PlaylistScrollbarPart(x - (RES._4K ? 13 : 6), y, w, this.btn_h, this.scrollbar_images.lineUp), + thumb: new PlaylistScrollbarPart(x, y + this.thumb_y, w - SCALE(14), this.thumb_h, this.scrollbar_images.thumb), + lineDown: new PlaylistScrollbarPart(x - (RES._4K ? 13 : 6), y + h - this.btn_h, w, this.btn_h, this.scrollbar_images.lineDown) + }; + } + + /** + * Handles mouse wheel scrolling events. + * @param {number} wheel_direction - The up or down wheel direction. + */ + wheel(wheel_direction) { + const direction = -wheel_direction; + const scrollStep = direction * grSet.playlistWheelScrollSteps; + const newScroll = this.nearestScroll(direction); + + if (this.scrollbar_wheel_scroll_page) { + this.shift_page(direction); + return; + } + + if (typeof this.desiredScrollPosition === 'undefined') { + this.desiredScrollPosition = newScroll; + } + + this.desiredScrollPosition += scrollStep; + this.desiredScrollPosition = Math.max(0, Math.min(this.desiredScrollPosition, this.scrollable_lines)); + + if (grSet.playlistSmoothScrolling) { + this.smooth_scroll_to(this.desiredScrollPosition); + } else { + this.scroll_to(this.desiredScrollPosition); + } + } + + /** + * Handles mouse leaving events over each scrollbar part. + */ + parts_leave() { + this.in_sbar = false; + this.cur_part_key = null; + + for (const part in this.sb_parts) { + this.sb_parts[part].cs('normal'); + } + this._startAlphaTimer(); + } + + /** + * Handles mouse leaving events of the scrollbar. + */ + leave() { + if (this.b_is_dragging) { + return; + } + + this.parts_leave(); + } + + /** + * Handles mouse movement of the scrollbar parts. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {string} The key of the current scrollbar part that the mouse is over, if any. + */ + parts_move(x, y) { + const hover_part_key = FindKey(this.sb_parts, (item) => item.trace(x, y)); + + const changeHotStatus = this.trace(x, y) !== this.in_sbar; + if (changeHotStatus) { + this.in_sbar = !this.in_sbar; + if (this.in_sbar) { + if (hover_part_key !== 'lineUp' && this.cur_part_key !== 'lineUp') { + this.sb_parts.lineUp.cs('hot'); + } + if (hover_part_key !== 'lineDown' && this.cur_part_key !== 'lineDown') { + this.sb_parts.lineDown.cs('hot'); + } + } + else { + if (this.cur_part_key !== 'lineUp') { + this.sb_parts.lineUp.cs('normal'); + } + if (this.cur_part_key !== 'lineDown') { + this.sb_parts.lineDown.cs('normal'); + } + } + this._startAlphaTimer(); + } + + if (this.cur_part_key === hover_part_key) { // Nothing to do: same button + return this.cur_part_key; + } + + if (this.cur_part_key) { + if (this.cur_part_key === 'thumb') { + this.sb_parts[this.cur_part_key].cs('normal'); + } + else { + if (this.sb_parts[this.cur_part_key].state === 'pressed') { + // Stop btn fast scroll + this.stop_shift_timer(); + } + + // Return prev button to normal or hot state + this.sb_parts[this.cur_part_key].cs(this.in_sbar ? 'hot' : 'normal'); + } + this._startAlphaTimer(); + } + + if (hover_part_key) { // Select current button + this.sb_parts[hover_part_key].cs('hover'); + this._startAlphaTimer(); + } + + this.cur_part_key = hover_part_key; + return this.cur_part_key; + } + + /** + * Handles mouse moving events over the scrollbar. + * @param {number} p_x - The x-coordinate. + * @param {number} p_y - The y-coordinate. + */ + move(p_x, p_y) { + if (this.b_is_dragging) { + this.throttled_scroll_y = p_y - this.y - this.initial_drag_y; + this.throttled_scroll_to(); + // this.scroll_to( (p_y - this.y - this.initial_drag_y - this.btn_h) / this.drag_distance_per_row); + return; + } + + this.parts_move(p_x, p_y); + } + + /** + * Handles left mouse button down events on the scrollbar. + */ + parts_lbtn_down() { + if (this.cur_part_key) { + this.sb_parts[this.cur_part_key].cs('pressed'); + this._startAlphaTimer(); + } + } + + /** + * Handles left mouse button up events on the scrollbar. + * @param {number} p_x - The x-coordinate. + * @param {number} p_y - The y-coordinate. + */ + lbtn_dn(p_x, p_y) { + if (!this.trace(p_x, p_y) || this.row_count <= this.rows_drawn) { + return; + } + + this.parts_lbtn_down(); + + const y = p_y - this.y; + + if (y < this.btn_h) { + this.shift_line(-1); + this.start_shift_timer(-1); + } + else if (y > this.h - this.btn_h) { + this.shift_line(1); + this.start_shift_timer(1); + } + else if (y < this.thumb_y) { + this.shift_page(-1); + this.timer_stop_y = y; + this.start_shift_timer(-this.rows_drawn); + } + else if (y > this.thumb_y + this.thumb_h) { + this.shift_page(1); + this.timer_stop_y = y; + this.start_shift_timer(this.rows_drawn); + } + else { // On bar + this.b_is_dragging = true; + this.initial_drag_y = y - this.thumb_y; + } + } + + /** + * Handles left mouse button down events on the scrollbar parts. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True if the current part was in 'pressed' state, otherwise false. + */ + parts_lbtn_up(x, y) { + if (!this.cur_part_key || this.sb_parts[this.cur_part_key].state !== 'pressed') { + return false; + } + + const new_state = this.sb_parts[this.cur_part_key].trace(x, y) ? 'hover' : 'normal'; + + this.sb_parts[this.cur_part_key].cs(new_state); + this._startAlphaTimer(); + + return true; + } + + /** + * Handles left mouse button up events on the scrollbar. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + lbtn_up(x, y) { + this.parts_lbtn_up(x, y); + if (this.b_is_dragging) { + this.b_is_dragging = false; + this.desiredScrollPosition = undefined; + } + this.initial_drag_y = 0; + + this.stop_shift_timer(); + } + + /** + * Scrolls to the start of the list. + */ + scroll_to_start() { + this.smooth_scroll_to(0); + } + + /** + * Scrolls one line up or down. + * @param {number} direction - The up or down scroll direction. + */ + shift_line(direction) { + const newScroll = this.nearestScroll(direction); + this.smooth_scroll_to(newScroll); + } + + /** + * Scrolls one page up or down. + * @param {number} direction - The up or down scroll direction. + */ + shift_page(direction) { + const newScroll = this.nearestScroll(direction); + this.smooth_scroll_to(newScroll + direction * Math.floor(Math.max(this.rows_drawn - 1, 1))); + } + + /** + * Scrolls to the end of the list. + */ + scroll_to_end() { + this.smooth_scroll_to(this.scrollable_lines); + } + + /** + * Starts a timer to shift the scrollbar. This method inserts a delay (8x45ms) when holding + * the mouse btn down before scrolling starts, after the first scroll event happens. + * @param {number} shift_amount - The number of rows to shift. + */ + start_shift_timer(shift_amount) { + if (this.timer_shift != null) return; + + this.timer_shift_count = 0; + this.timer_shift = setInterval(() => { + if (++this.timer_shift_count <= 8) return; + + const thumb_y = this.btn_h + (this.scroll + shift_amount) * this.scrollbar_travel / this.scrollable_lines; + const thumb_out_bounds = this.thumb_y <= this.btn_h || this.thumb_y + this.thumb_h >= this.h - this.btn_h; + const thumb_reached_stop = this.timer_stop_y !== -1 && ((shift_amount > 0 && thumb_y >= this.timer_stop_y) || (shift_amount < 0 && thumb_y + this.thumb_h <= this.timer_stop_y)); + + if (thumb_out_bounds || thumb_reached_stop) { + clearInterval(this.timer_shift); + this.timer_shift = null; + } else { + this.desiredScrollPosition = (this.desiredScrollPosition || this.scroll) + shift_amount; + this.smooth_scroll_to(this.desiredScrollPosition); + } + }, 45); + } + + /** + * Stops the timer that is shifting the scrollbar. + */ + stop_shift_timer() { + if (this.timer_shift != null) { + clearInterval(this.timer_shift); + this.timer_shift = undefined; + } + this.timer_stop_y = -1; + } + + /** + * Calculates the nearest scroll position to the current position. + * @param {number} direction - The direction of the scroll. + * @returns {number} The nearest scroll position. + */ + nearestScroll(direction) { + if (direction === 0) return this.scroll; + + const scrollFloor = Math.floor(this.scroll); + const scrollShift = this.scroll - scrollFloor; + const drawnShift = 1 - (this.rows_drawn - Math.floor(this.rows_drawn)); + + if (direction < 0) { + return scrollShift !== 0 ? scrollFloor : this.scroll + direction; + } + else if (Math.abs(drawnShift - scrollShift) > 0.0001) { + return (drawnShift > scrollShift ? scrollFloor : Math.ceil(this.scroll)) + drawnShift; + } + else { + return this.scroll + direction; + } + } + + /** + * Stops the scrollbar scroll and clears the timer. + */ + stopScrolling() { + clearInterval(this.smoothScrollTimer); + this.smoothScrollTimer = null; + } + + /** + * Scrolls to desired row over pref.playlistWheelScrollDuration (400ms default). + * Can be called repeatedly (during wheel or holding down arrows) to update the desired position. + * @param {number} newPosition - The new row position to scroll to. + */ + smooth_scroll_to(newPosition) { + if (!grSet.playlistSmoothScrolling) { + this.scroll_to(newPosition, false); + return; + } + + const endPos = Math.max(0, Math.min(newPosition, this.scrollable_lines)); + if (endPos === this.scroll) return; + clearInterval(this.smoothScrollTimer); + + const startPos = this.scroll; + const totalDistance = endPos - startPos; + let animationProgress = 0; // Percent of animation completion: 0 (startPos) - 100 (endPos) + + /** + * Performs a single step in a smooth scrolling animation and schedules subsequent steps. + * Continues until the target position is reached or the animation ends. + * @private + */ + const _scrollStep = () => { + animationProgress += 8; + const newVal = startPos + this.easeOut(animationProgress / 100) * totalDistance; + + const isEnding = + Math.abs(newPosition - newVal) < 0.1 + || + totalDistance > 0 && newVal >= newPosition + || + totalDistance < 0 && newVal <= newPosition; + + const scrollPos = newPosition === 0 ? 0 : Math.round(newVal * 100) / 100; + + this.scroll_to(scrollPos, false); + + if (isEnding || newPosition <= 0) { + animationProgress = 100; + this.desiredScrollPosition = undefined; + this.stopScrolling(); + return; + } + + this.smoothScrollTimer = setTimeout(_scrollStep, grSet.playlistWheelScrollDuration / 10); + }; + + _scrollStep(); + } + + /** + * Scrolls to the specified scroll position. + * @param {number} new_position - The new row position to scroll to. + * @param {boolean} scroll_wo_redraw - Calls a redraw to update the scrollbar. + */ + scroll_to(new_position, scroll_wo_redraw = false) { + const clampedPos = Math.max(0, Math.min(new_position, this.scrollable_lines)); + const invalidPos = (plSet.scrollbar_pos || clampedPos) > this.scrollable_lines; // Prevent scroll crash + if (clampedPos === this.scroll) return; + + this.scroll = invalidPos ? 0 : clampedPos; + this.thumb_y = this.btn_h + this.scroll * this.scrollbar_travel / this.scrollable_lines; + this.sb_parts.thumb.y = this.y + this.thumb_y; + + this.is_scrolled_up = (this.scroll === 0); + this.is_scrolled_down = Math.abs(this.scroll - this.scrollable_lines) < 0.0001; + + if (!scroll_wo_redraw) { + this.fn_redraw(); + } + } + + /** + * Sets the x-coordinate of the scrollbar. + * @param {number} x - The x-coordinate. + */ + set_x(x) { + this.x = x; + for (const part in this.sb_parts) { + this.sb_parts[part].x = x; + } + } + // #endregion +} + + +/** + * A class that creates scrollbar parts with specified dimensions and images. + */ +class PlaylistScrollbarPart { + /** + * Creates the `PlaylistScrollbarPart` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {GdiBitmap} img_src - The image sources for different states of the scrollbar part. + */ + constructor(x, y, w, h, img_src) { + /** @private @type {number} */ + this.x = x; + /** @private @type {number} */ + this.y = y; + /** @private @type {number} */ + this.w = w; + /** @private @type {number} */ + this.h = h; + + /** @private @type {GdiBitmap} */ + this.img_normal = undefined; + /** @private @type {GdiBitmap} */ + this.img_hover = undefined; + /** @private @type {GdiBitmap} */ + this.img_pressed = undefined; + /** @private @type {GdiBitmap} */ + this.img_hot = undefined; + + /** @private @type {number} */ + this.hover_alpha = 0; + /** @private @type {number} */ + this.hot_alpha = 0; + /** @private @type {number} */ + this.pressed_alpha = 0; + /** @private @type {string} */ + this.state = 'normal'; + + this._assign_imgs(img_src); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Assigns the images to the scrollbar part. + * @param {object} imgs - The images. + * @private + */ + _assign_imgs(imgs) { + this.img_normal = this.img_hover = this.img_hover = this.img_hover = null; + + if (imgs === undefined) return; + + this.img_normal = imgs.normal; + this.img_hover = imgs.hover ? imgs.hover : this.img_normal; + this.img_pressed = imgs.pressed ? imgs.pressed : this.img_normal; + this.img_hot = imgs.hot ? imgs.hot : this.img_normal; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Sets the state of the scrollbar part. + * @param {string} s - The state. + */ + cs(s) { + this.state = s; + this.repaint(); + } + + /** + * Updates the scrollbar part via repaint. + */ + repaint() { + window.RepaintRect(this.x, this.y, this.w, this.h); + } + + /** + * Checks if the mouse is within the boundaries of the scrollbar part. + * @param {number} x - The x coordinate. + * @param {number} y - The y coordinate. + * @returns {boolean} True if the coordinates are inside the scrollbar part. + */ + trace(x, y) { + return x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; + } + // #endregion +} + + +////////////////////////// +// * PLAYLIST HISTORY * // +////////////////////////// +/** + * A class that manages the history of playlist states, allowing users to navigate + * back and forward between previous states and updates the playlist accordingly. + */ +class PlaylistHistory { + /** + * Creates the `PlaylistHistory` instance. + * Initializes a playlist with a maximum number of states. + * @param {number} maxStates - The maximum number of states that can be stored in the history array. + */ + constructor(maxStates = 10) { + /** @private @type {number} */ + this.maxStates = maxStates; + /** @private @type {PlaylistState[]} */ + this.history = []; + /** @private @type {number} */ + this.stateIndex = 0; + /** @private @type {boolean} */ + this.updatingPlaylist = false; + + this.playlistAltered(PlaylistMutation.Init); + } + + // * GETTERS & SETTERS * // + // #region GETTERS & SETTERS + /** + * Gets the length of the playlist history. + * @returns {number} The length of the "history" array. + */ + get length() { + return this.history.length; + } + + /** + * Gets whether the history should ignore upcoming mutations and changes to the playlist. + * @returns {boolean} Whether to ignore playlist mutations. + */ + get ignorePlaylistMutations() { + return this.ignorePlaylistMutations; + } + + /** + * Sets whether the history should ignore upcoming mutations and changes to the playlist. + * + * Playlist updates are synchronous, but notifications are async. If setting to false, we + * update that value async as well to hopefully happen after all callbacks have called, + * and then manually call playlistAltered in case the playlist state has changed. + * @param {boolean} ignore - Whether to ignore playlist mutations. + */ + set ignorePlaylistMutations(ignore) { + if (!ignore) { + setTimeout(() => { + this.updatingPlaylist = false; + this.playlistAltered(PlaylistMutation.Switch); + }, 1); + } else { + this.updatingPlaylist = true; + } + } + // #endregion + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Sets and updates the state of a playlist by adding or removing items based on the current playback state. + * @private + */ + _setPlaylistState() { + pl.history_used = true; + this.updatingPlaylist = true; + /** @type {PlaylistState} */ const activeState = this.history[this.stateIndex]; + const pbQueue = plman.GetPlaybackQueueContents(); + const playingItem = plman.GetPlayingItemLocation(); + const plIndex = activeState.playlistId + plman.UndoBackup(plIndex); + plman.ActivePlaylist = plIndex; + + if (!activeState.locked) { + if (!playingItem.IsValid || playingItem.PlaylistIndex !== plIndex) { + plman.ClearPlaylist(plIndex); + plman.InsertPlaylistItems(plIndex, 0, activeState.playlistEntries); + } else { + const handles = plman.GetPlaylistItems(plIndex); + const index = handles.Find(fb.GetNowPlaying()); + const stateHandles = activeState.playlistEntries.Clone(); + const stateIndex = stateHandles.Find(fb.GetNowPlaying()); + const stateHandlesClone = stateHandles.Clone(); + console.log('>>> Playlist now playing index:', index); + // Remove everything in playlist except currently playing song + plman.ClearPlaylistSelection(plIndex); + plman.SetPlaylistSelection(plIndex, [playingItem.PlaylistItemIndex], true); + plman.RemovePlaylistSelection(plIndex, true); + plman.ClearPlaylistSelection(plIndex); + try { + stateHandles.RemoveById(stateIndex); + } catch (e) { + plman.InsertPlaylistItems(plIndex, plman.PlaylistItemCount(plIndex), stateHandlesClone); + } + if (stateIndex > 0) { + stateHandles.RemoveRange(stateIndex, stateHandles.Count); + plman.InsertPlaylistItems(plIndex, 0, stateHandles); + } + if (stateIndex < stateHandlesClone.Count) { + stateHandlesClone.RemoveRange(0, stateIndex); + plman.InsertPlaylistItems(plIndex, plman.PlaylistItemCount(plIndex), stateHandlesClone); + } + // Remove currently playing song duplicate + plman.SetPlaylistSelection(plIndex, [playingItem.PlaylistItemIndex], true); + plman.RemovePlaylistSelection(plIndex); + } + } + + this._restorePlaybackQueue(pbQueue); + + // * Wait for callbacks to be called + setTimeout(() => { + pl.history_used = false; + this.updatingPlaylist = false; + }, 1); + + // * Scroll to now playing when auto-scroll is active and playlist has now playing + const playing_item_location = plman.GetPlayingItemLocation(); + const playlistNowPlaying = playing_item_location.PlaylistIndex === plman.ActivePlaylist; + if (playlistNowPlaying && (grSet.playlistAutoScrollNowPlaying || fb.PlaybackFollowCursor || fb.CursorFollowPlayback)) { + setTimeout(() => { // * Wait until new album art / disc art loaded and other things finished for smoother auto-scrolling + pl.playlist.show_now_playing(); + }, grm.ui.newTrackFetchingDone + 200); + } + } + + /** + * Checks if a new playlist state should be added to the history based on the playlist ID, new items, and mutation type. + * @param {number} playlistId - The playlist id. + * @param {FbMetadbHandleList} newItems - The list of handles of playlist items. + * @param {string} mutationType - The type of mutation that occurred. + * @returns {boolean} True or false. + * @private + */ + _shouldAddState(playlistId, newItems, mutationType) { + const start = Date.now(); + const currState = this.history[this.stateIndex]; + if (!currState) { + // init'ing playlist history + return true + } + + // if playlist ID is unchanged, and playlist is locked, don't save + if (playlistId === currState.playlistId && plman.IsPlaylistLocked(playlistId)) { + return false; + } + if (playlistId !== currState.playlistId || + currState.locked || plman.IsPlaylistLocked(playlistId) || + newItems.Count !== currState.playlistEntries.Count) { + return true; + } + for (let i = 0; i < newItems.Count; i++) { + if (newItems[i].RawPath !== currState.playlistEntries[i].RawPath) { + // console.log(newItems[i].RawPath, currState.playlistEntries[i].RawPath); + return true; + } + } + DebugLog(`Checking for duplicate playlist states took: ${Date.now() - start}ms`); + return false; + } + + /** + * Restores a playback queue by adding items to the queue based on their playlist and index, + * or by adding them directly if no playlist or index is specified. + * @param {FbPlaybackQueueItem[]} pbQueue - The playback queue state to restore. + * @private Attempts to re-mark playbackQueue items after setting playlist state. + */ + _restorePlaybackQueue(pbQueue) { + plman.FlushPlaybackQueue(); + for (const queueItem of pbQueue) { + const itemPlaylist = queueItem.PlaylistIndex; + const itemIndex = queueItem.PlaylistItemIndex; + if (itemPlaylist !== -1 && itemIndex !== -1) { + const plContents = {}; + if (!plContents[itemPlaylist]) { + plContents[itemPlaylist] = plman.GetPlaylistItems(itemPlaylist); + } + const playlistHandles = plContents[itemPlaylist]; + if (playlistHandles && playlistHandles[itemIndex] && playlistHandles[itemIndex].Path === queueItem.Handle.Path) { + plman.AddPlaylistItemToPlaybackQueue(itemPlaylist, itemIndex); + } else { + const index = plContents[itemPlaylist].Find(queueItem.Handle); + if (index >= 0) { + plman.AddPlaylistItemToPlaybackQueue(itemPlaylist, index); + } else { + plman.AddItemToPlaybackQueue(queueItem.Handle); + } + } + } else { + plman.AddItemToPlaybackQueue(queueItem.Handle); + } + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Logs the type of playlist mutation and checks if the playlist should be added to the history of playlist states. + * Notify the PlaylistHistory that a playlist was altered. + * @param {string} mutationType - The type of mutation that occurred. + */ + playlistAltered(mutationType) { + // ignore playlist alterations when changing states + console.log(mutationType); + if (!this.updatingPlaylist && plman.ActivePlaylist >= 0) { + const plItems = plman.GetPlaylistItems(plman.ActivePlaylist); + if (this._shouldAddState(plman.ActivePlaylist, plItems, mutationType)) { + if (this.stateIndex < this.length - 1) { + this.history = this.history.slice(0, this.stateIndex + 1); + } + if (this.length >= this.maxStates) { + this.history.shift(); + } + this.history.push(new PlaylistState(plman.ActivePlaylist, plItems)); + this.stateIndex = this.length - 1; + if (grm.ui.btn.back) { + grm.ui.btn.back.repaint(); + grm.ui.btn.forward.repaint(); + } + DebugLog('stateIndex:', this.stateIndex, ' new items count:', plItems.Count, this.stateIndex); + } + } + } + + /** + * Handles the playlist history button action in the playlist manager bar. + * @param {string} btn - The playlist history back or forward button. + */ + buttons(btn) { + if (btn.isEnabled && btn.isEnabled()) { + if (btn.id === 'Back') { + this.back(); + } else { + this.forward(); + } + } + } + + /** + * Checks if there is a previous state in the playlist history. + * @returns {boolean} True or false. + */ + canBack() { + return this.stateIndex > 0; + } + + /** + * Checks if there is a next state available to navigate forward to. + * @returns {boolean} True or false. + */ + canForward() { + return this.stateIndex < this.length - 1; + } + + /** + * Decreases the state index and sets the playlist state accordingly. + */ + back() { + this.stateIndex--; + if (this.stateIndex <= 0) { + this.stateIndex = 0; + } + DebugLog('pl.history back =>', this.stateIndex); + this._setPlaylistState(); + } + + /** + * Increments the state index and sets the playlist state, ensuring that the state index + * does not exceed the length of the playlist. + */ + forward() { + this.stateIndex++; + if (this.stateIndex >= this.length) { + this.stateIndex = this.length - 1; + } + DebugLog('pl.history forward =>', this.stateIndex); + this._setPlaylistState(); + } + + /** + * Clears the playlist history. Should always be called from on_playlists_changed + * because all saved playlistIds have been invalidated. + */ + reset() { + this.history = []; + this.playlistAltered(PlaylistMutation.Init); + } + // #endregion +} + + +/** + * A class that manages the state of an active playlist, including its ID, + * lock status, and a list of playlist entries if it is not locked. + */ +class PlaylistState { + /** + * Creates the `PlaylistState` instance. + * @param {number} playlistId - The ID of the playlist. + * @param {FbMetadbHandleList} plItems - The playlist items in the current playlist. + * @public + */ + constructor(playlistId, plItems) { + /** @public @type {number} */ + this.playlistId = playlistId; + /** @public @type {boolean} */ + this.locked = plman.IsPlaylistLocked(playlistId); + + if (!this.locked) { + // don't need to save items if playlist is locked, we'll just switch to it + /** @type {FbMetadbHandleList} */ this.playlistEntries = plItems; + } + } +} + + +///////////////////////// +// * PLAYLIST RATING * // +///////////////////////// +/** + * A class that creates and draws the rating in the playlist rows. + */ +class PlaylistRating { + /** + * Creates the `PlaylistRating` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} max_w - The maximum width. + * @param {number} h - The height. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + constructor(x, y, max_w, h, metadb) { + /** @private @type {number} */ + const rowFontSize = grSet[`playlistFontSize_${grSet.layout}`]; + /** @private @type {number} */ + this.btn_w = SCALE(rowFontSize + 2); + + /** @public @type {number} */ + this.x = x; + /** @public @type {number} */ + this.y = y; + /** @public @type {number} */ + this.w = Math.min(this.btn_w * 5, max_w); + /** @public @type {number} */ + this.h = h; + + /** @private @type {FbMetadbHandle} */ + this.metadb = metadb; + /** @private @type {?number} */ + this.rating = undefined; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws stars as rating in the playlist row. + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} color - The color of the stars. + */ + draw(gr, color) { + const cur_rating = this.get_rating(); + let cur_rating_x = this.x; + const y = this.y - (RES._4K ? 3 : 1); + + for (let j = 0; j < 5; j++) { + if (j < cur_rating) { + gr.DrawString('\u2605', pl.font.rating_set, grm.ui.loadingThemeComplete ? RGBA(0, 0, 0, 100) : color, cur_rating_x, y, this.btn_w + 1, this.h + 2, Stringformat.align_center); + gr.DrawString('\u2605', pl.font.rating_set, color, cur_rating_x, y, this.btn_w, this.h, Stringformat.align_center); + } else if (grSet.showPlaylistRatingGrid) { + gr.DrawString('\u2219', pl.font.rating_not_set, pl.col.row_title_normal, cur_rating_x, y, this.btn_w, this.h, Stringformat.align_center); + } + cur_rating_x += this.btn_w; + } + } + + /** + * Sets rating in the playlist row rating area when double clicked. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + click(x, y) { + if (!this.trace(x, y)) { + return; + } + + pl.album_ratings.clear(); + const new_rating = Math.floor((x - this.x) / this.btn_w) + 1; + const current_rating = this.get_rating(); + + if (plSet.use_rating_from_tags) { + if (!this.metadb.RawPath.startsWith('http')) { + const handle = new FbMetadbHandleList(); + handle.Add(this.metadb); + handle.UpdateFileInfoFromJSON( + JSON.stringify({ + RATING: (current_rating === new_rating) ? '' : new_rating + }) + ); + } + } + else { + fb.RunContextCommandWithMetadb(`Playback Statistics/Rating/${current_rating === new_rating ? '' : new_rating}`, this.metadb); + } + + this.rating = (current_rating === new_rating) ? 0 : new_rating; + } + + /** + * Traces mouse movement and checks if mouse is within the boundaries of the clickable rating. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + trace(x, y) { + return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; + } + + /** + * Gets the rating for the current track. + * If no rating has been fetched yet, it fetches the rating using the get_track_rating method. + * @returns {number|null} The rating of the current track, or null if no rating. + */ + get_rating() { + const trackId = $('%rating%', this.metadb); + let rating = pl.track_ratings.get(trackId); + + if (rating === undefined) { + rating = this.get_track_rating(this.metadb); + pl.track_ratings.set(trackId, rating); + } + + return rating; + } + + /** + * Gets the rating for a given track. + * If no track is provided, it defaults to the current playlist row. + * @param {FbMetadbHandle} [track] - The track to get the rating for. + * @returns {number|null} The rating of the provided or default track, or null if no rating. + */ + get_track_rating(track = this.metadb) { + let currentRating; + + if (plSet.use_rating_from_tags) { + const fileInfo = track.GetFileInfo(); + const ratingIdx = fileInfo.MetaFind('RATING'); + currentRating = ratingIdx !== -1 ? fileInfo.MetaValue(ratingIdx, 0) : 0; + } else { + currentRating = $('%rating%', track); + } + return currentRating === '' ? null : Number(currentRating); + } + + /** + * Gets the average rating for an album. + * @returns {number} The average rating of all tracks in the album. + */ + get_album_rating() { + // Return cached results if available + if (pl.album_ratings.size !== 0) { + return pl.album_ratings; + } + + const albums = new Map(); + const playlistItems = plman.GetPlaylistItems(plman.ActivePlaylist).Convert(); + + // Group tracks by album + for (let i = 0; i < playlistItems.length; ++i) { + const albumName = $('%album%', playlistItems[i]); + const rating = this.get_track_rating(playlistItems[i]); + + const albumData = albums.get(albumName); + if (albumData === undefined) { + albums.set(albumName, { albumTotalRating: rating, albumTrackCount: 1 }); + } else { + albumData.albumTotalRating += rating; + albumData.albumTrackCount++; + } + } + + // Calculate average rating for each album + for (const [albumName, albumData] of albums) { + const albumAverageRating = Number((albumData.albumTotalRating / albumData.albumTrackCount).toFixed(2)); + pl.album_ratings.set(albumName, albumAverageRating); + } + + return pl.album_ratings; + } + + /** + * Resets and clears the current rating. + */ + reset_queried_data() { + this.rating = undefined; + } + // #endregion +} + + +////////////////////////// +// * PLAYLIST MANAGER * // +////////////////////////// +/** + * A class that represents the playlist manager at the top of the panel + * and acts like a drop down menu that contains various options. + */ +class PlaylistManager { + /** + * Creates the `PlaylistManager` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + */ + constructor(x, y, w, h) { + pl.history = new PlaylistHistory(); + + /** @public @type {number} */ + this.x = x; + /** @public @type {number} */ + this.y = y; + /** @public @type {number} */ + this.w = w; + /** @public @type {number} */ + this.h = h; + + /** @public @enum {number} */ + this.state = { + normal: 0, + hovered: 1, + pressed: 2 + }; + + /** @public @type {state} */ + this.panel_state = this.state.normal; + /** @public @type {number} */ + this.hover_alpha = 0; + + /** @private @type {?GdiBitmap} */ + this.image_normal = null; + /** @private @type {?GdiBitmap} */ + this.image_hovered = null; + /** @private @type {?string} */ + this.info_text = undefined; + /** @private @type {number} */ + this.cur_playlist_idx = undefined; + + /** + * Throttles a function to limit how often it can be invoked. This specific throttled function + * is used to repaint a rectangular area of a window at most once every frame at a rate of 60fps. + * @type {Function} + * @private + */ + this.throttled_repaint = Throttle(() => { + window.RepaintRect(this.x, this.y, this.w, this.h); + }, 1000 / 60); + + /** + * The animation timer for the playlist manager text button. + * This timer adjusts the alpha transparency of the button based on whether it is being hovered over. + * It uses an alpha_timer factory function to create an object with start and stop methods to control the animation. + * @type {{ start: Function, stop: Function }} + * @private + */ + this.alpha_timer = this._alpha_timer([this], (item) => item.panel_state === this.state.hovered); + } + + // * STATIC METHODS * // + // #region STATIC METHODS + /** + * Appends a context menu item to the given parent menu that allows the user + * to toggle the visibility of the playlist manager text button. + * @param {ContextMenu} parent_menu - The parent menu to append the item to. + */ + static append_playlist_info_visibility_context_menu_to(parent_menu) { + const showPlaylistManager = grSet[`showPlaylistManager_${grSet.layout}`]; + parent_menu.appendItem('Show playlist manager', () => { + // plSet.show_plman = !plSet.show_plman; + if (grSet.layout === 'compact') { + grSet.showPlaylistManager_compact = !grSet.showPlaylistManager_compact; + } else if (grSet.layout === 'artwork') { + grSet.showPlaylistManager_artwork = !grSet.showPlaylistManager_artwork; + } else { + grSet.showPlaylistManager_default = !grSet.showPlaylistManager_default; + } + pl.call.on_size(grm.ui.ww, grm.ui.wh); + }, { is_checked: showPlaylistManager }); + } + // #endregion + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Animates the alpha of the playlist manager text button at the top. + * @param {Array} items_arg - The list of items to animate. + * @param {function(PlaylistItem): boolean} hover_predicate_arg - The predicate to determine if an item is hovered. + * @returns {{start: Function, stop: Function}} An object with 'start' and 'stop' methods to control the animation. + * @private + */ + _alpha_timer(items_arg, hover_predicate_arg) { + let alpha_timer_internal = null; + const hover_in_step = 50; + const hover_out_step = 15; + + const start = () => { + if (!alpha_timer_internal) { + alpha_timer_internal = setInterval(() => { + for (const item of items_arg) { + const saved_alpha = item.hover_alpha; + item.hover_alpha = hover_predicate_arg(item) ? Math.min(255, item.hover_alpha += hover_in_step) : Math.max(0, item.hover_alpha -= hover_out_step); + + if (saved_alpha !== item.hover_alpha) { + item.repaint(); + } + } + + const alpha_in_progress = items_arg.some(item => item.hover_alpha > 0 && item.hover_alpha < 255); + + if (!alpha_in_progress) { + stop(); + } + }, 25); + } + }; + + const stop = () => { + if (alpha_timer_internal) { + clearInterval(alpha_timer_internal); + alpha_timer_internal = null; + } + }; + + return { start, stop }; + } + + /** + * Changes the state of the playlist manager text button. + * @param {state} new_state - The new state of the button. + * @private + */ + _change_state(new_state) { + if (this.panel_state === new_state) { + return; + } + + const old_state = this.panel_state; + this.panel_state = new_state; + + if (old_state === this.state.pressed) { + // Mouse click action opens context menu, which triggers on_mouse_leave, thus causing weird hover animation + this.hover_alpha = 0; + } + if (new_state === this.state.hovered || new_state === this.state.normal) { + this.alpha_timer.start(); + } + + this.repaint(); + } + + /** + * Draws the playlist manager text button and defines its states. + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {state} panel_state - The state of the playlist manager text button. + * @private + */ + _draw_on_image(gr, x, y, w, h, panel_state) { + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const showPlaylistManager = grSet[`showPlaylistManager_${grSet.layout}`]; + let text_color; + let bg_color; + + switch (panel_state) { + case this.state.normal: { + text_color = grSet.styleBlend && grSet.autoHidePlman || !showPlaylistManager ? '' : pl.col.plman_text_normal; + bg_color = pl.col.plman_bg; + break; + } + case this.state.hovered: { + text_color = pl.col.plman_text_hovered; + bg_color = pl.col.plman_bg; + break; + } + case this.state.pressed: { + text_color = pl.col.plman_text_pressed; + bg_color = pl.col.plman_bg; + break; + } + } + + if (!grSet.styleBlend) gr.FillSolidRect(x, y, w, h, bg_color); // Playlist Manager Hide Top Rows that shouldn't be visible + // * Need to apply text rendering AntiAliasGridFit when using style Blend or when using custom theme fonts with larger font sizes + gr.SetTextRenderingHint(grSet.styleBlend || grSet.customThemeFonts && headerFontSize > 18 ? TextRenderingHint.AntiAliasGridFit : TextRenderingHint.ClearTypeGridFit); + + if (plman.ActivePlaylist !== -1 && plman.IsPlaylistLocked(plman.ActivePlaylist)) { + // Position above scrollbar for eye candy + // const sbar_x = x + w - pl.geo.scrollbar_w - pl.geo.scrollbar_right_pad; + const lock_x = grm.ui.ww - SCALE(29); + const lock_text = '\uf023'; + const lock_w = Math.ceil( + /** @type {!number} */ + gr.MeasureString(lock_text, pl.font.font_awesome, 0, 0, 0, 0).Width + ); + gr.DrawString(lock_text, pl.font.font_awesome, text_color, lock_x, y, lock_w, h, Stringformat.align_center); + + // right_pad += lock_w; // Deactivated -> PLM text should be always centered + } + + const info_text_format = Stringformat.align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + gr.DrawString(this.info_text, pl.font.title_selected, text_color, x, y, w, h, info_text_format); + + // * Playlist history buttons + const yCorrSize = { + 22: { '4K': 14, 'HD': 7 }, + 20: { '4K': 12, 'HD': 5 }, + 18: { '4K': 10, 'HD': 3 }, + 17: { '4K': 8, 'HD': 2 }, + 16: { '4K': 7, 'HD': 1 }, + 15: { '4K': 4, 'HD': 0 }, + 14: { '4K': 4, 'HD': 0 }, + 13: { '4K': 2, 'HD': -1 }, + 12: { '4K': 2, 'HD': -1 }, + 10: { '4K': -2, 'HD': -3 } + }; + const yCorr = yCorrSize[headerFontSize] && yCorrSize[headerFontSize][RES._4K ? '4K' : 'HD']; + const noAlbumArtSize = grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight; + const info_w = gr.CalcTextWidth(this.info_text, pl.font.title_selected); + const btn_x = Math.round((grSet.playlistLayout === 'normal' ? grSet.panelWidthAuto ? grm.ui.displayLibrarySplit() ? noAlbumArtSize : grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : grm.ui.ww * 0.5 : 0) + (w - info_w) * 0.5); + const btn_y = grm.ui.topMenuHeight + yCorr; + const btns_w = Math.round(h); + const hasPlaylistHistory = pl.history.canBack() || pl.history.canForward(); + const showBtns = (grSet.autoHidePlman && (panel_state !== this.state.normal) || !grSet.autoHidePlman); + + if (grSet.showPlaylistHistory && hasPlaylistHistory && showPlaylistManager) { + grm.ui.btn.back = new Button(showBtns ? btn_x - btns_w : 9999, btn_y, h, h, 'Back', grm.ui.btnImg.Back, null, pl.history.canBack.bind(pl.history)); + grm.ui.btn.forward = new Button(showBtns ? btn_x + info_w + btns_w * SCALE(0.15) : 9999, btn_y, h, h, 'Forward', grm.ui.btnImg.Forward, null, pl.history.canForward.bind(pl.history)); + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Reinitializes the playlist manager text button state. + */ + reinitialize() { + this.info_text = undefined; + this.panel_state = this.state.normal; + this.hover_alpha = 0; + } + + /** + * Sets the width of the playlist manager text button. + * @param {number} w - The width of the button. + */ + set_w(w) { + this.w = w; + } + + /** + * Sets the size and position of the playlist manager text button. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width of the button. + */ + set_xywh(x, y, w) { + this.x = x; + this.y = y; + this.w = w; + this.h = SCALE(plSet.row_h + 4); + } + + /** + * Traces mouse movement and checks when mouse is within the boundaries of the playlist manager text button. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + trace(x, y) { + return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; + } + + /** + * Registers key actions and handles key presses. + * @param {PlaylistKeyActionHandler} key_handler - The PlaylistKeyActionHandler object. + */ + register_key_actions(key_handler) { + key_handler.register_key_action(VK_KEY_N, + (modifiers) => { + if (modifiers.ctrl) { + plman.CreatePlaylist(plman.PlaylistCount, ''); + plman.ActivePlaylist = plman.PlaylistCount - 1; + } + }); + + key_handler.register_key_action(VK_KEY_M, + (modifiers) => { + if (modifiers.ctrl) { + fb.RunMainMenuCommand('View/Playlist Manager'); + } + }); + } + + /** + * Updates the playlist manager text button state via repaint. + */ + repaint() { + this.throttled_repaint(); + } + // #endregion + + // * CALLBACKS * // + // #region Callbacks + /** + * Draws the playlist manager text button at the top. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + on_paint(gr) { + if (!this.info_text || this.cur_playlist_idx !== plman.ActivePlaylist) { + this.cur_playlist_idx = plman.ActivePlaylist; + let metadb_list = plman.GetPlaylistSelectedItems(this.cur_playlist_idx); + let is_selected = true; + + if (!metadb_list.Count) { + metadb_list = plman.GetPlaylistItems(this.cur_playlist_idx); + is_selected = false; + } + + const track_count = metadb_list.Count; + let tracks_text = ''; + let duration_text = ''; + if (track_count > 0) { + tracks_text = track_count.toString() + (track_count > 1 ? ' tracks' : ' track'); + if (is_selected) { + tracks_text += ' selected'; + } + + const duration = Math.round(metadb_list.CalcTotalDuration()); + if (duration) { + duration_text = utils.FormatDuration(duration); + } + } + + this.info_text = plman.GetPlaylistName(this.cur_playlist_idx); + if (tracks_text) { + this.info_text += `: ${tracks_text}`; + } + if (duration_text) { + this.info_text += `, Length: ${duration_text}`; + } + } + + if (this.panel_state === this.state.pressed + || (this.panel_state === this.state.normal && !this.hover_alpha) + || (this.panel_state === this.state.hovered && this.hover_alpha === 255)) { + if (this.image_normal) { + this.image_normal = null; + } + if (this.image_hovered) { + this.image_hovered = null; + } + + this._draw_on_image(gr, this.x, this.y, this.w, this.h, this.panel_state); + } + else { + if (!this.image_normal) { + const image = gdi.CreateImage(this.w, this.h); + const image_gr = image.GetGraphics(); + + this._draw_on_image(image_gr, 0, 0, this.w, this.h, this.state.normal); + + image.ReleaseGraphics(image_gr); + this.image_normal = image; + } + + if (!this.image_hovered) { + const image = gdi.CreateImage(this.w, this.h); + const image_gr = image.GetGraphics(); + + this._draw_on_image(image_gr, 0, 0, this.w, this.h, this.state.hovered); + + image.ReleaseGraphics(image_gr); + this.image_hovered = image; + } + + gr.DrawImage(this.image_normal, this.x, this.y, this.w, this.h, 0, 0, this.w, this.h, 0, 255); + gr.DrawImage(this.image_hovered, this.x, this.y, this.w, this.h, 0, 0, this.w, this.h, 0, this.hover_alpha); + } + } + + /** + * Handles left mouse button down events and changes the playlist manager text button state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_lbtn_down(x, y, m) { + if (grm.ui.btn.back && grm.ui.btn.back.mouseInThis(x, y) || grm.ui.btn.forward && grm.ui.btn.forward.mouseInThis(x, y)) { + return; // Handled in back forward buttons + } + if (!this.trace(x, y)) { + return; + } + + this._change_state(this.state.pressed); + } + + /** + * Handles left mouse button up events and opens the playlist manager drop down list menu. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_lbtn_up(x, y, m) { + const was_pressed = this.panel_state === this.state.pressed; + + if (grm.ui.btn.back && grm.ui.btn.back.mouseInThis(x, y) || grm.ui.btn.forward && grm.ui.btn.forward.mouseInThis(x, y)) { + return; // Handled in back forward buttons + } + if (!this.trace(x, y)) { + this._change_state(this.state.normal); + return; + } + else { + this._change_state(this.state.hovered); + if (!was_pressed) { + return; + } + } + + grm.topMenu.topMenuPlaylists(x, y); + + this.repaint(); + } + + /** + * Handles mouse leave events and resets the playlist manager text button state. + * @override + */ + on_mouse_leave () { + this._change_state(this.state.normal); + } + + /** + * Handles the button state of the playlist manager text button. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_move(x, y, m) { + if (this.panel_state === this.state.pressed) { + return; + } + + this._change_state(this.trace(x, y) ? this.state.hovered : this.state.normal); + } + + /** + * Handles right mouse button down events and changes the playlist manager text button state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_rbtn_down(x, y, m) { + if (!this.trace(x, y)) { + return true; + } + + this._change_state(this.state.pressed); + + return false; + } + + /** + * Handles right mouse button up events and opens the playlist manager. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_rbtn_up(x, y, m) { + const was_pressed = this.panel_state === this.state.pressed; + + if (!this.trace(x, y)) { + this._change_state(this.state.normal); + return true; + } + else { + this._change_state(this.state.hovered); + if (!was_pressed) { + return true; + } + } + + const cmm = new ContextMainMenu(); + + fb.RunMainMenuCommand('View/Playlist Manager'); // PlaylistManager.append_playlist_info_visibility_context_menu_to(cmm); + + if (utils.IsKeyPressed(VK_SHIFT)) { + grm.ctxMenu.contextMenuDefault(cmm); + } + + grm.ui.activeMenu = true; + cmm.execute(x, y); + grm.ui.activeMenu = false; + + return true; + } + + /** + * Handles playlist modified events and clears the button name. + */ + on_playlist_modified() { + this.info_text = undefined; + this.repaint(); + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-controls.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-controls.js new file mode 100644 index 00000000..fc96fec8 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-controls.js @@ -0,0 +1,2322 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Controls * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +////////////////////// +// * META HANDLER * // +////////////////////// +/** + * A class that handles metadata operations including retrieving album metadata, + * and writing meta tags or playlist stats to a file. + */ +class PlaylistMetaHandler { + /** + * Creates the `PlaylistMetaHandler` instance. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + constructor(metadb) { + /** @private @type {FbMetadbHandle} */ + this.metadb = metadb; + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Gets metadata statistics for artists, albums, and tracks from the current active playlist. + * Sets default values where data is missing, computes various stats like ratings, counts, playcounts, + * and sorts them to find top rated and most played entries. + * @param {object[]} metadata - An array of metadata objects for various music entities. + * Each object contains artist, album, year, genre, label, country, and tracks information. + * @returns {object} An object with aggregated metadata statistics, including: + * - Mappings of artists to genres, countries, and their ratings, counts, and playcounts. + * - Mappings of albums to artists, years, genres, labels, countries, and their ratings, counts, and playcounts. + * - Mappings of tracks to artists, albums, years, genres, labels, and their ratings, counts. + * - Sorted lists of top rated and most played artists, albums, tracks, genres, labels, and countries. + * - Total play counts for artists, albums, tracks, genres, labels, and countries. + * @private + */ + _get_metadata_stats(metadata) { + const artistGenre = new Map(); + const artistCountry = new Map(); + + const albumArtist = new Map(); + const albumYear = new Map(); + const albumGenre = new Map(); + const albumLabel = new Map(); + const albumCountry = new Map(); + + const trackArtist = new Map(); + const trackAlbum = new Map(); + const trackYear = new Map(); + const trackGenre = new Map(); + const trackLabel = new Map(); + + const artistRatings = new Map(); + const albumRatings = new Map(); + const trackRatings = new Map(); + const artistCounts = new Map(); + const albumCounts = new Map(); + const trackCounts = new Map(); + + const artistPlaycounts = new Map(); + const albumPlaycounts = new Map(); + const trackPlaycounts = new Map(); + const genrePlaycounts = new Map(); + const labelPlaycounts = new Map(); + const countryPlaycounts = new Map(); + + const SetDefaultStringValue = (value, defaultValue) => (value && value !== '?') ? value : defaultValue; + const SetDefaultStringList = (value, defaultValue) => (value && value !== '?') ? value.split(',').map(item => item.trim()) : [defaultValue]; + const SetDefaultSet = (map, key) => map.has(key) || map.set(key, new Set()); + const SetDefaultVal = (map, key, value) => map.has(key) || map.set(key, value); + const SetDefaultNum = (map, key) => map.has(key) || map.set(key, 0); + + for (const data of metadata) { + // * Set defaults + const artist = SetDefaultStringValue(data.artist, 'NO ARTIST'); + const album = SetDefaultStringValue(data.album, 'NO ALBUM'); + const year = SetDefaultStringValue(data.year, 'NO YEAR'); + const genre = SetDefaultStringList(data.genre, 'NO GENRE'); + const label = SetDefaultStringList(data.label, 'NO LABEL'); + const country = SetDefaultStringList(data.country, 'NO COUNTRY'); + + // * Set maps have entries for artist and album + SetDefaultSet(artistGenre, artist); + SetDefaultSet(artistCountry, artist); + SetDefaultVal(albumArtist, album, artist); + SetDefaultVal(albumYear, album, year); + SetDefaultSet(albumGenre, album); + SetDefaultSet(albumLabel, album); + SetDefaultSet(albumCountry, album); + + // * Set ratings, counts, and playcounts to 0 if they don't exist + SetDefaultNum(artistRatings, artist); + SetDefaultNum(albumRatings, album); + SetDefaultNum(artistCounts, artist); + SetDefaultNum(albumCounts, album); + SetDefaultNum(artistPlaycounts, artist); + SetDefaultNum(albumPlaycounts, album); + SetDefaultNum(labelPlaycounts, label); + SetDefaultNum(countryPlaycounts, country); + + // * Add genres to artist/album, labels to album, and countries to artist/album sets + for (const genreItem of genre) { + artistGenre.get(artist).add(genreItem); + albumGenre.get(album).add(genreItem); + } + for (const labelItem of label) { + albumLabel.get(album).add(labelItem); + } + for (const countryItem of country) { + artistCountry.get(artist).add(countryItem); + albumCountry.get(album).add(countryItem); + } + + // * Update all ratings, counts and playcounts + for (const trackItem of data.tracks) { + const track = SetDefaultStringValue(trackItem.title, 'NO TRACK'); + SetDefaultVal(trackArtist, track, artist); + SetDefaultVal(trackAlbum, track, album); + SetDefaultVal(trackYear, track, year); + SetDefaultSet(trackGenre, track); + SetDefaultSet(trackLabel, track); + SetDefaultNum(trackRatings, track); + SetDefaultNum(trackCounts, track); + + artistRatings.set(artist, artistRatings.get(artist) + trackItem.rating); + albumRatings.set(album, albumRatings.get(album) + trackItem.rating); + trackRatings.set(track, (trackRatings.get(track) || 0) + trackItem.rating); + + artistCounts.set(artist, artistCounts.get(artist) + 1); + albumCounts.set(album, albumCounts.get(album) + 1); + trackCounts.set(track, (trackCounts.get(track) || 0) + 1); + + artistPlaycounts.set(artist, artistPlaycounts.get(artist) + trackItem.playcount); + albumPlaycounts.set(album, albumPlaycounts.get(album) + trackItem.playcount); + trackPlaycounts.set(track, (trackPlaycounts.get(track) || 0) + trackItem.playcount); + + if (genre.length === 0 || (genre.length === 1 && genre[0] === 'NO GENRE')) { + trackGenre.get(track).add('NO GENRE'); + } else { + for (const genreItem of genre) { + trackGenre.get(track).add(genreItem); + genrePlaycounts.set(genreItem, (genrePlaycounts.get(genreItem) || 0) + trackItem.playcount); + } + } + if (label.length === 0 || (label.length === 1 && label[0] === 'NO LABEL')) { + trackLabel.get(track).add('NO LABEL'); + } else { + for (const labelItem of label) { + trackLabel.get(track).add(labelItem); + labelPlaycounts.set(labelItem, (labelPlaycounts.get(labelItem) || 0) + trackItem.playcount); + } + } + for (const countryItem of country) { + if (countryItem !== 'NO COUNTRY') { + countryPlaycounts.set(countryItem, (countryPlaycounts.get(countryItem) || 0) + trackItem.playcount); + } + } + } + } + + // * Set top rated stats + const topRatedArtists = SortKeyValuesByAvg(artistRatings, artistCounts); + const topRatedAlbums = SortKeyValuesByAvg(albumRatings, albumCounts); + const topRatedTracks = SortKeyValuesByAvg(trackRatings, trackCounts); + + // * Set top played stats + const topPlayedArtists = SortKeyValuesByDsc(artistPlaycounts); + const topPlayedAlbums = SortKeyValuesByDsc(albumPlaycounts); + const topPlayedTracks = SortKeyValuesByDsc(trackPlaycounts); + const topPlayedGenres = SortKeyValuesByDsc(genrePlaycounts); + const topPlayedLabels = SortKeyValuesByDsc(labelPlaycounts); + const topPlayedCountries = SortKeyValuesByDsc(countryPlaycounts); + + // * Set total stats + const totalArtists = new Set(metadata.map(data => data.artist)).size; + const totalAlbums = new Set(metadata.map(data => data.album)).size; + const totalTracks = metadata.reduce((sum, data) => sum + data.tracks.length, 0); + const totalYears = new Set(metadata.map(data => data.year)).size; + const totalGenres = new Set(metadata.flatMap(data => data.genre)).size; + const totalLabels = new Set(metadata.map(data => data.label)).size; + const totalCountries = new Set(metadata.map(data => data.country)).size; + const totalRatings = metadata.reduce((sum, data) => sum + data.tracks.reduce((sum, track) => sum + track.rating, 0), 0); + const totalPlaycounts = metadata.reduce((sum, data) => sum + data.tracks.reduce((sum, track) => sum + track.playcount, 0), 0); + + const totalArtistPlays = [...artistPlaycounts.values()].reduce((acc, count) => acc + count, 0); + const totalAlbumPlays = [...albumPlaycounts.values()].reduce((acc, count) => acc + count, 0); + const totalTrackPlays = [...trackPlaycounts.values()].reduce((acc, count) => acc + count, 0); + const totalGenrePlays = [...genrePlaycounts.values()].reduce((acc, count) => acc + count, 0); + const totalLabelPlays = [...labelPlaycounts.values()].reduce((acc, count) => acc + count, 0); + const totalCountryPlays = [...countryPlaycounts.values()].reduce((acc, count) => acc + count, 0); + + // * Set best rated stats + const bestRatedArtist = GetKeyByHighestAvg(artistRatings, artistCounts); + const bestRatedAlbum = GetKeyByHighestAvg(albumRatings, albumCounts); + const bestRatedTrack = GetKeyByHighestAvg(trackRatings, trackCounts); + + // * Set most listened stats + const mostPlayedArtist = GetKeyByHighestVal(artistPlaycounts); + const mostPlayedAlbum = GetKeyByHighestVal(albumPlaycounts); + const mostPlayedTrack = GetKeyByHighestVal(trackPlaycounts); + const mostPlayedGenre = GetKeyByHighestVal(genrePlaycounts); + const mostPlayedLabel = GetKeyByHighestVal(labelPlaycounts); + const mostPlayedCountry = GetKeyByHighestVal(countryPlaycounts); + + const artistPlaycount = artistPlaycounts.get(mostPlayedArtist); + const albumPlaycount = albumPlaycounts.get(mostPlayedAlbum); + const trackPlaycount = trackPlaycounts.get(mostPlayedTrack); + const genrePlaycount = genrePlaycounts.get(mostPlayedGenre); + const labelPlaycount = labelPlaycounts.get(mostPlayedLabel); + const countryPlaycount = countryPlaycounts.get(mostPlayedCountry); + + const artistPercentage = (artistPlaycount / totalArtistPlays) * 100; + const albumPercentage = (albumPlaycount / totalAlbumPlays) * 100; + const trackPercentage = (trackPlaycount / totalTrackPlays) * 100; + const genrePercentage = (genrePlaycount / totalGenrePlays) * 100; + const labelPercentage = (labelPlaycount / totalLabelPlays) * 100; + const countryPercentage = (countryPlaycount / totalCountryPlays) * 100; + + return { + artistGenre, artistCountry, + albumArtist, albumYear, albumGenre, + albumLabel, albumCountry, + trackArtist, trackAlbum, trackYear, trackGenre, trackLabel, + + artistRatings, albumRatings, trackRatings, + artistCounts, albumCounts, trackCounts, + artistPlaycounts, albumPlaycounts, trackPlaycounts, genrePlaycounts, labelPlaycounts, countryPlaycounts, + + topRatedArtists, topRatedAlbums, topRatedTracks, + topPlayedArtists, topPlayedAlbums, topPlayedTracks, topPlayedGenres, topPlayedLabels, topPlayedCountries, + + totalArtists, totalAlbums, totalTracks, totalYears, totalGenres, totalLabels, totalCountries, totalRatings, totalPlaycounts, + totalArtistPlays, totalAlbumPlays, totalTrackPlays, totalGenrePlays, totalLabelPlays, totalCountryPlays, + + bestRatedArtist, bestRatedAlbum, bestRatedTrack, + + mostPlayedArtist, mostPlayedAlbum, mostPlayedTrack, mostPlayedGenre, mostPlayedLabel, mostPlayedCountry, + artistPlaycount, albumPlaycount, trackPlaycount, genrePlaycount, labelPlaycount, countryPlaycount, + artistPercentage, albumPercentage, trackPercentage, genrePercentage, labelPercentage, countryPercentage + }; + } + + /** + * Gets the playcount for a given track. + * If no track is provided, it defaults to the current playlist row. + * @param {FbMetadbHandle} [track] - The track to get the playcount for. + * @returns {number} The playcount of the provided or default track. + * @private + */ + _get_track_playcount(track = this.metadb) { + let currentPlaycount; + + if (plSet.use_rating_from_tags) { + const fileInfo = track.GetFileInfo(); + const ratingIdx = fileInfo.MetaFind('PLAY COUNT'); + currentPlaycount = ratingIdx !== -1 ? fileInfo.MetaValue(ratingIdx, 0) : 0; + } else { + currentPlaycount = $('%play_count%', track); + } + return currentPlaycount === '' ? null : Number(currentPlaycount); + } + + /** + * Chooses and returns either the `rating` or `playcount` based on the `statsType`. + * The `statsType` string should be in the format 'property_subproperty'. + * If it has three parts ('property_subproperty_subproperty'), the third subproperty determines the return value. + * If `statsType` does not indicate 'rating' or 'trackPlaycount', the function defaults to returning the `rating`. + * @param {string} statsType - The type of stats to return, expected to be either 'rating' or 'trackPlaycount'. + * @param {number} rating - The rating value to return if `statsType` is 'rating' or invalid. + * @param {number} playcount - The playcount value to return if `statsType` is 'trackPlaycount'. + * @returns {number} Either the `rating` or `playcount` value, based on the `statsType`. + * @private + */ + _write_stats_choose_statistic_by_type(statsType, rating, playcount) { + const statsArray = statsType.split('_'); + const statsProperty = statsArray.length === 3 ? statsArray[2] : statsArray[0]; + return statsProperty === 'trackPlaycount' ? playcount : rating; + } + + /** + * Gets a sorting function based on the provided statistic type. + * The statistic type string should be in the format 'property_direction'. + * The property indicates the statistic to sort by, and the direction indicates the order ('asc' for ascending, 'dsc' for descending). + * @param {string} statsType - The statistic type to sort by. + * @returns {function(any, any): number} A sorting function. + * @private + */ + _write_stats_get_sorting(statsType) { + const sortMethods = { + artist: (a, b) => CompareValues(a.artist, b.artist), + albumTitle: (a, b) => CompareValues(a.album, b.album), + albumRating: (a, b) => CompareValues(a.albumAverageRating, b.albumAverageRating), + albumPlaycount: (a, b) => CompareValues(a.albumAveragePlaycount, b.albumAveragePlaycount), + albumPlaycountTotal: (a, b) => CompareValues(a.albumTotalPlaycount, b.albumTotalPlaycount), + albumTrackPlaycount: (a, b) => CompareValues(a.albumTotalPlaycount, b.albumTotalPlaycount), + trackTitle: (a, b) => CompareValues(a.track, b.track), + trackRating: (a, b) => CompareValues(a.rating, b.rating), + trackPlaycount: (a, b) => CompareValues(a.playcount, b.playcount), + year: (a, b) => CompareValues(a.year, b.year), + genre: (a, b) => CompareValues(a.genre, b.genre), + label: (a, b) => CompareValues(a.label, b.label), + country: (a, b) => CompareValues(a.country, b.country) + }; + + const [property, direction] = statsType.split('_'); + const sortMethod = sortMethods[property]; + + // Retrieve sortMethod from sortMethods with the given property. + // If 'dsc', sort descending by reversing arguments; otherwise, sort ascending. + return sortMethod && ((direction === 'dsc') ? (a, b) => sortMethod(b, a) : sortMethod); + } + + /** + * Generates a formatted string of total and top statistics from the current active playlist. + * @param {object} metadata - An object containing various statistics and counts. + * @param {string} statsType - A string that indicates whether to return 'rating' or 'playcount' total statistics. + * @param {string} topStatsType - A string that indicates whether to return 'topRated' or 'topPlayed' statistics. + * @returns {string} Returns a formatted string with the requested statistics. + * @private + */ + _write_stats_total_top_stats(metadata, statsType, topStatsType) { + const rating = statsType.toLowerCase().includes('rating') || statsType.toLowerCase().includes('rated'); + const playcount = statsType.toLowerCase().includes('playcount') || statsType.toLowerCase().includes('played'); + let list = ''; + + list += 'Total statistics:\n' + + ` \u00B7 Artists: ${metadata.totalArtists}\n` + + ` \u00B7 Albums: ${metadata.totalAlbums}\n` + + ` \u00B7 Tracks: ${metadata.totalTracks}\n` + + ` \u00B7 Years: ${metadata.totalYears}\n` + + ` \u00B7 Genres: ${metadata.totalGenres}\n` + + ` \u00B7 Labels: ${metadata.totalLabels}\n` + + ` \u00B7 Countries: ${metadata.totalCountries}\n` + + (rating ? ` \u00B7 Ratings: ${metadata.totalRatings}\n` : '') + + (playcount ? ` \u00B7 Playcounts: ${metadata.totalPlaycounts}\n` : '') + '\n'; + + if (topStatsType === 'topRated') { + list += 'Top statistics:\n' + + ` \u00B7 Best rated artist: ${metadata.bestRatedArtist}\n` + + ` \u00B7 Best rated album: ${metadata.bestRatedAlbum}\n` + + ` \u00B7 Best rated track: ${metadata.bestRatedTrack}\n\n\n`; + } + + if (topStatsType === 'topPlayed') { + list += 'Top statistics:\n' + + ` \u00B7 Most played artist: ${metadata.mostPlayedArtist} - ${metadata.artistPlaycount} plays (${metadata.artistPercentage.toFixed(2)}%)\n` + + ` \u00B7 Most played album: ${metadata.mostPlayedAlbum} - ${metadata.albumPlaycount} plays (${metadata.albumPercentage.toFixed(2)}%)\n` + + ` \u00B7 Most played track: ${metadata.mostPlayedTrack} - ${metadata.trackPlaycount} plays (${metadata.trackPercentage.toFixed(2)}%)\n` + + ` \u00B7 Most played genre: ${metadata.mostPlayedGenre} - ${metadata.genrePlaycount} plays (${metadata.genrePercentage.toFixed(2)}%)\n` + + ` \u00B7 Most played label: ${metadata.mostPlayedLabel} - ${metadata.labelPlaycount} plays (${metadata.labelPercentage.toFixed(2)}%)\n` + + ` \u00B7 Most played country: ${metadata.mostPlayedCountry} - ${metadata.countryPlaycount} plays (${metadata.countryPercentage.toFixed(2)}%)\n\n\n`; + } + + return list; + } + + /** + * Writes top statistics list for: + * - Top rated artists + * - Top rated albums + * - Top rated tracks + * - Top played artists + * - Top played albums + * - Top played tracks + * - Top played genres + * - Top played labels + * - Top played countries + * + * It provides a detailed ranked list from top to bottom for each category from the current active playlist. + * @param {Array} metadata - An array of objects, each object representing track metadata with all provided properties. + * @param {boolean} topRated - Writes the top rated artist and albums as the list. + * @param {boolean} topPlayed - Writes the top played artists, albums, genres, labels and countries as the list. + * @returns {string} A string formatted for display, containing the top statistics. + * @private + */ + _write_stats_top_list(metadata, topRated, topPlayed) { + const includeArtist = plSet.playlist_stats_include_artist; + const includeAlbum = plSet.playlist_stats_include_album; + const includeYear = plSet.playlist_stats_include_year; + const includeGenre = plSet.playlist_stats_include_genre; + const includeLabel = plSet.playlist_stats_include_label; + const includeCountry = plSet.playlist_stats_include_country; + const includeStats = plSet.playlist_stats_include_stats; + + const data = this._get_metadata_stats(metadata); + let list = ''; + + // * Top rated lists + if (topRated) { + list += `${WriteFancyHeader('Top rated artists')}\n`; + let topRatedArtistIndex = 0; + for (const artist of data.topRatedArtists) { + const country = includeCountry ? Array.from(data.artistCountry.get(artist) || []).join(', ') : ''; + const genre = includeGenre ? Array.from(data.artistGenre.get(artist) || []).join(', ') : ''; + const include = country || genre ? ` (${country}${country && genre ? ' \u00B7 ' : ''}${genre})` : ''; + const average = data.artistRatings.get(artist) / data.artistCounts.get(artist); + const stats = includeStats ? `: ${average.toFixed(2)}` : ''; + + list += `${topRatedArtistIndex + 1}: ${artist}${include}${stats}\n`; + topRatedArtistIndex++; + } + list += '\n\n'; + + list += `${WriteFancyHeader('Top rated albums')}\n`; + let topRatedAlbumIndex = 0; + for (const album of data.topRatedAlbums) { + const year = includeYear && data.albumYear.get(album) ? `${data.albumYear.get(album)}` : ''; + const genreSet = data.albumGenre.get(album); + const genre = includeGenre && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; + const labelSet = data.albumLabel.get(album); + const label = includeLabel && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; + const includeParts = [year, genre, label].filter(part => part !== ''); + const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; + const artist = includeArtist && data.albumArtist.get(album) ? ` - ${data.albumArtist.get(album)}` : ''; + const average = data.albumRatings.get(album) / data.albumCounts.get(album); + const stats = includeStats ? `: ${average.toFixed(2)}` : ''; + + list += `${topRatedAlbumIndex + 1}: ${album}${include}${artist}${stats}\n`; + topRatedAlbumIndex++; + } + list += '\n\n'; + + list += `${WriteFancyHeader('Top rated tracks')}\n`; + let topRatedTracksIndex = 0; + for (const track of data.topRatedTracks) { + const album = includeAlbum && data.trackAlbum.get(track) ? ` - ${data.trackAlbum.get(track)}` : ''; + const year = includeYear && data.trackYear.get(track) ? `${data.trackYear.get(track)}` : ''; + const genreSet = data.trackGenre.get(track); + const genre = includeGenre && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; + const labelSet = data.trackLabel.get(track); + const label = includeLabel && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; + const includeParts = [year, genre, label].filter(part => part !== ''); + const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; + const artist = includeArtist && data.trackArtist.get(track) ? ` - ${data.trackArtist.get(track)}` : ''; + const average = data.trackRatings.get(track); + const stats = includeStats ? `: ${average.toFixed(2)}` : ''; + + list += `${topRatedTracksIndex + 1}: ${track}${album}${include}${artist}${stats}\n`; + topRatedTracksIndex++; + } + } + + // * Top played lists + if (topPlayed) { + list += `${WriteFancyHeader('Top played artists')}\n`; + let topPlayedArtistsIndex = 0; + for (const artist of data.topPlayedArtists) { + const country = includeCountry ? Array.from(data.artistCountry.get(artist) || []).join(', ') : ''; + const genre = includeGenre ? Array.from(data.artistGenre.get(artist) || []).join(', ') : ''; + const include = country || genre ? ` (${country}${country && genre ? ' \u00B7 ' : ''}${genre})` : ''; + const playcount = data.artistPlaycounts.get(artist); + const percentage = (playcount / data.totalArtistPlays) * 100; + const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; + + list += `${topPlayedArtistsIndex + 1}: ${artist}${include}${stats}\n`; + topPlayedArtistsIndex++; + } + list += '\n\n'; + + list += `${WriteFancyHeader('Top played albums')}\n`; + let topPlayedAlbumsIndex = 0; + for (const album of data.topPlayedAlbums) { + const year = includeYear && data.albumYear.get(album) ? data.albumYear.get(album) : ''; + const genreSet = data.albumGenre.get(album); + const genre = includeGenre && genreSet && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; + const labelSet = data.albumLabel.get(album); + const label = includeLabel && labelSet && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; + const includeParts = [year, genre, label].filter(part => part !== ''); + const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; + const artist = includeArtist && data.albumArtist.get(album) ? ` - ${data.albumArtist.get(album)}` : ''; + const playcount = data.albumPlaycounts.get(album); + const percentage = (playcount / data.totalAlbumPlays) * 100; + const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; + + list += `${topPlayedAlbumsIndex + 1}: ${album}${include}${artist}${stats}\n`; + topPlayedAlbumsIndex++; + } + list += '\n\n'; + + list += `${WriteFancyHeader('Top played tracks')}\n`; + let topPlayedTracksIndex = 0; + for (const track of data.topPlayedTracks) { + const album = includeAlbum && data.trackAlbum.get(track) ? ` - ${data.trackAlbum.get(track)}` : ''; + const year = includeYear && data.trackYear.get(track) ? data.trackYear.get(track) : ''; + const genreSet = data.trackGenre.get(track); + const genre = includeGenre && genreSet && genreSet.size > 0 ? Array.from(genreSet).join(', ') : ''; + const labelSet = data.trackLabel.get(track); + const label = includeLabel && labelSet && labelSet.size > 0 ? Array.from(labelSet).join(', ') : ''; + const includeParts = [year, genre, label].filter(part => part !== ''); + const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; + const artist = includeArtist && data.trackArtist.get(track) ? ` - ${data.trackArtist.get(track)}` : ''; + const playcount = data.trackPlaycounts.get(track); + const percentage = (playcount / data.totalAlbumPlays) * 100; + const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; + + list += `${topPlayedTracksIndex + 1}: ${track}${album}${include}${artist}${stats}\n`; + topPlayedTracksIndex++; + }; + list += '\n\n'; + + list += `${WriteFancyHeader('Top played genres')}\n`; + let topPlayedGenresIndex = 0; + for (const genre of data.topPlayedGenres) { + const playcount = data.genrePlaycounts.get(genre); + const percentage = (playcount / data.totalGenrePlays) * 100; + const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; + + list += `${topPlayedGenresIndex + 1}: ${genre}${stats}\n`; + topPlayedGenresIndex++; + } + list += '\n\n'; + + list += `${WriteFancyHeader('Top played labels')}\n`; + let topPlayedLabelsIndex = 0; + for (const label of data.topPlayedLabels) { + const playcount = data.labelPlaycounts.get(label); + if (playcount <= 0) return ''; + const percentage = (playcount / data.totalLabelPlays) * 100; + const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; + + list += `${topPlayedLabelsIndex + 1}: ${label}${stats}\n`; + topPlayedLabelsIndex++; + } + list += '\n\n'; + + list += `${WriteFancyHeader('Top played countries')}\n`; + let topPlayedCountriesIndex = 0; + for (const country of data.topPlayedCountries) { + const playcount = data.countryPlaycounts.get(country); + if (playcount <= 0) return ''; + const percentage = (playcount / data.totalCountryPlays) * 100; + const stats = includeStats ? ` - ${playcount} plays (${percentage.toFixed(2)}%)` : ''; + + list += `${topPlayedCountriesIndex + 1}: ${country}${stats}\n`; + topPlayedCountriesIndex++; + } + } + + return list; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Iterates through the current active playlist and builds metadata for each album. + * @returns {Map} A map where keys are album names and values are objects with properties: + * - artist: The name of the artist of the album. + * - album: The name of the album. + * - year: The year of the album. + * - genre: The genre of the album. + * - label: The label the artist is signed to. + * - country: The country the artist is from. + * - albumTrackCount: The total number of tracks on the album. + * - albumTotalRating: The calculated total rating of all tracks on the album. + * - albumTotalPlaycount: The calculated total playcount of all tracks on the album. + * - albumAverageRating: The calculated average album rating. + * - albumAveragePlaycount: The calculated average album playcount. + * - tracks: An array of track objects, each with properties: + * - track: The track object from the playlist. + * - trackNumber: The track number. + * - title: The title of the track. + * - rating: The rating of the track. + * - playcount: The playcount of the track. + */ + get_metadata() { + const metadata = new Map(); + const getRating = new PlaylistRating(); + const playlistItems = plman.GetPlaylistItems(plman.ActivePlaylist).Convert(); + + for (let i = 0; i < playlistItems.length; ++i) { + const currentItem = playlistItems[i]; + const artist = $('$if2(%album artist%,[%artist%])', currentItem); + const album = $('%album%', currentItem); + const trackNumber = $('%tracknumber%', currentItem); + const title = $('%title%', currentItem); + const year = $('$if2(%year%,[%date%])', currentItem); + const genre = $('%genre%', currentItem); + const label = $('$if2(%label%,[%publisher%])', currentItem); + const country = $('$if2(%artistcountry%,[%country%])', currentItem); + const rating = getRating.get_track_rating(currentItem) || 0; + const playcount = this._get_track_playcount(currentItem) || 0; + + let albumData = metadata.get(album); + if (!albumData) { + albumData = { + artist, + album, + year, + genre, + label, + country, + albumTrackCount: 0, + albumTotalRating: 0, + albumTotalPlaycount: 0, + albumAverageRating: 0, + albumAveragePlaycount: 0, + tracks: [] + }; + metadata.set(album, albumData); + } + + albumData.albumTrackCount += 1; + albumData.albumTotalRating += rating; + albumData.albumTotalPlaycount += playcount; + + albumData.tracks.push({ + track: currentItem, + trackNumber, + title, + rating, + playcount + }); + } + + // * Calculate the averages after processing all tracks + for (const [album, albumData] of metadata) { + albumData.albumAverageRating = albumData.albumTrackCount > 0 ? Number((albumData.albumTotalRating / albumData.albumTrackCount).toFixed(2)) : 0; + albumData.albumAveragePlaycount = albumData.albumTrackCount > 0 ? Math.round(albumData.albumTotalPlaycount / albumData.albumTrackCount) : 0; + } + + return metadata; + } + + /** + * Calculate Peak Loudness Ratio keeping in mind replayGain 2.0 is implemented in Foobar2000. + * Reference value in Foobar 2000 is set on -18 LUFS in order to maintain backwards compatibility with RG1, RG2. + * EBU R 128 reference is -23 LUFS. + * @param {string} gain - The ReplayGain gain value for track %replaygain_track_gain% | for album %replaygain_album_gain%. + * @param {string} peak - The ReplayGain peak value for track %replaygain_track_peak_db% | for album %replaygain_album_peak_db%. + * @returns {string} The Peak Loudness Ratio. + */ + get_PLR(gain, peak) { + const lufs = -2300 - (Number(gain.replace(/[^0-9+-]/g, '')) - 500); + const tpfs = Number(peak.replace(/[^0-9+-]/g, '')); + const plr = tpfs - lufs; + const plr_value = plr % 100 > 49 ? plr + 100 : plr; + + return Math.floor(plr_value / 100); + } + + /** + * Writes calculated %ALBUMRATING%, '%ALBUMPLAYCOUNT%' and '%ALBUMPLAYCOUNTTOTAL%' values to music files via the Playlist context menu. + * - '%ALBUMRATING%': The calculated average album rating, converted from a 0-5 scale to a 0-100 scale due to Foobar2000's incompatibility with floating point numbers when sorting. + * - '%ALBUMPLAYCOUNT%': The calculated average playcount of the album. + * - '%ALBUMPLAYCOUNTTOTAL%': The calculated total playcount of all tracks on the album. + */ + write_album_stats_to_tags() { + const metadata = this.get_metadata(); + const plItems = plman.GetPlaylistSelectedItems(plman.ActivePlaylist); + const libItems = new FbMetadbHandleList(lib.pop.getHandleList('newItems')); + const items = grm.ui.displayLibrary && !grm.ui.displayPlaylist || grm.ui.displayLibrarySplit() && grm.ui.state.mouse_x < grm.ui.ww * 0.5 ? libItems : plItems; + const handleList = items === libItems ? libItems : plItems; + + if (!items || !items.Count) return; + + const albums = new Map(); + for (let i = 0; i < items.Count; i++) { + const albumName = $('%album%', items[i]); + if (!albums.has(albumName)) { + albums.set(albumName, []); + } + albums.get(albumName).push(items[i]); + } + + const albumUpdates = []; + for (const [albumName, items] of albums.entries()) { + const metadataEntry = metadata.get(albumName); + if (metadataEntry) { + const { albumAverageRating, albumAveragePlaycount, albumTotalPlaycount } = metadataEntry; + + if (albumAverageRating || albumAveragePlaycount || albumTotalPlaycount) { + const albumStats = {}; + if (albumAverageRating) { + // Convert albumAverageRating floating point number from scale 0-5 to an integer on scale 0-100. + // Foobar2000 does not handle floating point numbers well in its metadata fields when sorting, + // so we store the ratings as integers to ensure they are processed correctly. + const convertedRating = Math.round(albumAverageRating * 20); + albumStats.ALBUMRATING = convertedRating; + } + if (albumAveragePlaycount || albumTotalPlaycount) { + albumStats.ALBUMPLAYCOUNT = albumAveragePlaycount; + albumStats.ALBUMPLAYCOUNTTOTAL = albumTotalPlaycount; + } + + for (const item of items) { + albumUpdates.push(albumStats); + } + } + } + } + + if (albumUpdates.length) { + handleList.UpdateFileInfoFromJSON(JSON.stringify(albumUpdates)); + } + } + + /** + * Writes various statistics for the current playlist to a text file. + * @param {Array} metadata - The metadata array of objects with properties depending on metadataType. + * @param {string} metadataType - The type of metadata: 'album', 'track', 'topRated', or 'topPlayed'. + * @param {string} filePath - The path to the text file where statistics will be written. + * @param {string} statsName - The name of the statistic type. + * @param {string} statsType - The statistic type to be used for sorting. + * @param {string} ratingType - The rating type, one of 'albumAverage', 'albumTotal', or 'albumTracks'. + * @param {string} playcountType - The playcount type, one of 'albumAverage', 'albumTotal', or 'albumTracks'. + * @returns {boolean} True if writing to the text file was successful, false otherwise. + */ + write_stats_to_text_file(metadata, metadataType, filePath, statsName, statsType, ratingType, playcountType) { + const includeArtist = plSet.playlist_stats_include_artist; + const includeAlbum = plSet.playlist_stats_include_album; + const includeTrack = plSet.playlist_stats_include_track; + const includeYear = plSet.playlist_stats_include_year; + const includeGenre = plSet.playlist_stats_include_genre; + const includeLabel = plSet.playlist_stats_include_label; + const includeCountry = plSet.playlist_stats_include_country; + const includeStats = plSet.playlist_stats_include_stats; + + const metadataStats = this._get_metadata_stats(metadata); + const playlistStats = includeStats ? this._write_stats_total_top_stats(metadataStats, statsType, metadataType) : '\n'; + const playlistName = plman.GetPlaylistName(plman.ActivePlaylist); + const playlistTitle = `${playlistName} - ${statsName} statistics${metadataType.startsWith('top') ? '' : ` - sorted by ${statsType}`}`; + const playlistData = `${WriteFancyHeader(playlistTitle)}\n\n${playlistStats}`; + + const sortFunction = this._write_stats_get_sorting(statsType); + + // * Albums + const albumMetadataSorted = sortFunction ? metadata.sort(sortFunction) : metadata; + const albumMetadata = albumMetadataSorted.map((metadata) => { + const separator = includeArtist && includeAlbum ? ' - ' : ''; + const artist = includeArtist ? metadata.artist ? `${metadata.artist}${separator}` : 'NO ARTIST' : ''; + const album = includeAlbum ? metadata.album ? `${metadata.album}` : 'NO ALBUM' : ''; + const albumTracksRating = includeTrack && metadata.tracks ? `\n${metadata.tracks.map(trackRating => ` ${trackRating.trackNumber}. ${trackRating.title}${includeStats ? `: ${trackRating.rating}` : ''}`).join('\n')}` : ''; + const albumTracksPlaycount = includeTrack && metadata.tracks ? `\n${metadata.tracks.map(trackPlaycount => ` ${trackPlaycount.trackNumber}. ${trackPlaycount.title}${includeStats ? `: ${trackPlaycount.playcount}` : ''}`).join('\n')}` : ''; + const year = includeYear ? metadata.year ? `${metadata.year}` : 'NO YEAR' : ''; + const genre = includeGenre ? metadata.genre && metadata.genre.trim() !== '' && metadata.genre.trim() !== '?' ? `${metadata.genre}` : 'NO GENRE' : ''; + const label = includeLabel ? metadata.label ? `${metadata.label}` : 'NO LABEL' : ''; + const country = includeCountry ? metadata.country ? `${metadata.country}` : 'NO COUNTRY' : ''; + const includeParts = [year, genre, label, country].filter(part => part !== ''); + const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; + + const ratingTypeMeta = { + albumAverage: includeStats && metadata.albumAverageRating, + albumTotal: includeStats && metadata.albumTotalRating, + albumTracks: albumTracksRating + }; + const rating = `: ${ratingTypeMeta[ratingType]}`; + const ratingAvg = includeStats ? `: ${metadata.albumAverageRating}` : ''; + + const playcountTypeMeta = { + albumAverage: includeStats && metadata.albumAveragePlaycount, + albumTotal: includeStats && metadata.albumTotalPlaycount, + albumTracks: albumTracksPlaycount + }; + const playcount = `: ${playcountTypeMeta[playcountType]}`; + const playcountAvg = includeStats ? `: ${metadata.albumAveragePlaycount}` : ''; + + if (ratingType) { + return ratingType === 'albumTracks' ? + `${artist}${album}${include}${ratingAvg}${rating}\n` : + `${artist}${album}${include}${rating}`; + } + else if (playcountType) { + return playcountType === 'albumTracks' ? + `${artist}${album}${include}${playcountAvg}${playcount}\n` : + `${artist}${album}${include}${playcount}`; + } + else { + return `${artist}${album}${include}`; + } + }).join('\n'); + + // * Tracks + const trackMetadataMap = metadata.flatMap(album => + album.tracks.map(track => ({ + artist: album.artist, + album: album.album, + trackNumber: track.trackNumber, + track: track.title, + year: album.year, + genre: album.genre, + label: album.label, + country: album.country, + rating: track.rating, + playcount: track.playcount + })) + ); + + const trackMetadataSorted = sortFunction ? trackMetadataMap.sort(sortFunction) : trackMetadataMap; + const trackMetadata = trackMetadataSorted.map((trackData) => { + const track = includeTrack ? (trackData.trackNumber && trackData.track ? `${trackData.trackNumber}. ${trackData.track}` : 'NO TRACK') : ''; + const album = includeAlbum ? (trackData.album && trackData.album.trim() !== '' ? `${trackData.album}` : 'NO ALBUM') : ''; + const artist = includeArtist ? (trackData.artist && trackData.artist.trim() !== '' ? `${trackData.artist}` : 'NO ARTIST') : ''; + const year = includeYear ? (trackData.year && trackData.year.trim() !== '' && trackData.year.trim() !== '?' ? `${trackData.year}` : 'NO YEAR') : ''; + const genre = includeGenre ? (trackData.genre && trackData.genre.trim() !== '' && trackData.genre.trim() !== '?' ? `${trackData.genre}` : 'NO GENRE') : ''; + const label = includeLabel ? (trackData.label && trackData.label.trim() !== '' && trackData.label.trim() !== '?' ? `${trackData.label}` : 'NO LABEL') : ''; + const country = includeCountry ? (trackData.country && trackData.country.trim() !== '' && trackData.country.trim() !== '?' ? `${trackData.country}` : 'NO COUNTRY') : ''; + const includeParts = [year, genre, label, country].filter(part => part !== ''); + const include = includeParts.length > 0 ? ` (${includeParts.join(' \u00B7 ')})` : ''; + const rating = includeStats ? `: ${trackData.rating}` : ''; + const playcount = includeStats ? `: ${trackData.playcount}` : ''; + const separator1 = includeTrack && includeAlbum ? ' - ' : ''; + const separator2 = includeAlbum && includeArtist || includeTrack && includeArtist ? ' - ' : ''; + + return `${track}${separator1}${album}${include}${separator2}${artist}${this._write_stats_choose_statistic_by_type(statsType, rating, playcount)}`; + }).join('\n'); + + // * Write the data to text file + const data = playlistData + ( + metadataType === 'track' ? trackMetadata : + metadataType === 'album' ? albumMetadata : + metadataType === 'topRated' ? this._write_stats_top_list(metadata, true, false) : + metadataType === 'topPlayed' ? this._write_stats_top_list(metadata, false, true) : '' + ); + + return Save(filePath, data); + } + // #endregion +} + + +////////////////////////// +// * KEYBOARD HANDLER * // +////////////////////////// +/** + * A class that handles key action events to determine whether a key is pressed. + */ +class PlaylistKeyActionHandler { + /** + * Creates the `PlaylistKeyActionHandler` instance. + * The actions registry is an object that maps keys to their respective action callbacks. + */ + constructor() { + /** @private @type {{[key: string]: Function}} */ + this.actions = {}; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Registers a key action. + * @param {string|number} key - The key to register. + * @param {Function} action_callback - The callback to run when the key is pressed. + * @throws {ArgumentError} If the action callback is not a function. + * @throws {ArgumentError} If the key is already used. + */ + register_key_action(key, action_callback) { + if (!action_callback) { + throw new ArgumentError('action_callback', action_callback); + } + + if (this.actions[key]) { + throw new ArgumentError('key', key.toString(), 'This key is already used'); + } + + this.actions[key] = action_callback; + } + + /** + * Invokes a key action. + * @param {string} key - The key to invoke. + * @param {object} [key_modifiers] - The modifiers for the key passed to key action callback. + * @param {boolean} [key_modifiers.ctrl] - The option to disable the CTRL key. + * @param {boolean} [key_modifiers.alt] - The option to disable the ALT key. + * @param {boolean} [key_modifiers.shift] - The option to disable the SHIFT key. + * @returns {boolean} True or false. + */ + invoke_key_action(key, key_modifiers) { + const key_action = this.actions[key]; + if (!this.actions[key]) { + return false; + } + + key_action(key_modifiers || {}); + + return true; + } + // #endregion +} + + +/////////////////////////// +// * SELECTION HANDLER * // +/////////////////////////// +/** + * A class that handles selection and manipulation of playlist items. + */ +class PlaylistSelectionHandler { + /** + * Creates the `PlaylistSelectionHandler` instance. + * @param {PlaylistContent} cnt_arg - The playlist content. + * @param {number} cur_playlist_idx_arg - The current playlist index. + */ + constructor(cnt_arg, cur_playlist_idx_arg) { + /** @private @constant @type {PlaylistContentNavigation} */ + this.cnt_helper = cnt_arg.helper; + /** @private @constant @type {Array} */ + this.rows = cnt_arg.rows; + + /** @private @constant @type {number} */ + this.cur_playlist_idx = cur_playlist_idx_arg; + /** @private @type {Array} */ + this.selected_indexes = []; + /** @private @type {?number} */ + this.last_single_selected_index = undefined; + + /** @public @type {boolean} */ + this.dragging = false; + /** @public @type {boolean} */ + this.internal_drag_n_drop_active = false; + /** @public @type {?PlaylistRow} */ + this.last_hover_row = undefined; + + /** + * Sorts an array of numbers in ascending order. + * @param {number} a - The first number to compare. + * @param {number} b - The second number to compare. + * @returns {number} The difference between `a` and `b`. + */ + this.numeric_ascending_sort = (a, b) => (a - b); + + /** + * Checks if two arrays are equal in terms of length and the values at each index. + * @param {Array} a - The first array. + * @param {Array} b - The second array. + * @returns {boolean} True if the arrays are equal, false otherwise. + */ + this.arraysEqual = (a, b) => { + if (a === b) return true; + if (a == null || b == null || a.length !== b.length) return false; + + for (let i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + }; + + this.initialize_selection(); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Updates the selection state of the playlist according to the given header. + * @param {PlaylistBaseHeader} header - The header to update the selection state for. + * @param {boolean} ctrl_pressed - Whether or not the Ctrl key is pressed. + * @param {boolean} shift_pressed - Whether or not the Shift key is pressed. + * @private + */ + _update_selection_with_header(header, ctrl_pressed, shift_pressed) { + const row_indexes = header.get_row_indexes(); + + if (shift_pressed) { + this.selected_indexes = Union(this._get_shift_selection(row_indexes[0]), row_indexes); + } + else { + if (ctrl_pressed) { + const is_selected = Difference(row_indexes, this.selected_indexes).length === 0; + if (is_selected) { + this.clear_selection(); + } + else { + this.selected_indexes = Union(this.selected_indexes, row_indexes); + } + } + else { + this.selected_indexes = row_indexes; + } + this.last_single_selected_index = row_indexes[0]; + } + + plman.ClearPlaylistSelection(this.cur_playlist_idx); + plman.SetPlaylistSelection(this.cur_playlist_idx, this.selected_indexes, true); + if (row_indexes.length) { + plman.SetPlaylistFocusItem(this.cur_playlist_idx, row_indexes[0]); + } + } + + /** + * Updates the selection state of the playlist according to the given row. + * @param {PlaylistRow} row - The row to update the selection state for. + * @param {boolean} ctrl_pressed - Whether or not the Ctrl key is pressed. + * @param {boolean} shift_pressed - Whether or not the Shift key is pressed. + * @private + */ + _update_selection_with_row(row, ctrl_pressed, shift_pressed) { + if (shift_pressed) { + this.selected_indexes = this._get_shift_selection(row.idx); + + // plman.ClearPlaylistSelection(this.cur_playlist_idx); // * Disabled to enable contiguous Ctrl+shift selection + plman.SetPlaylistSelection(this.cur_playlist_idx, this.selected_indexes, true); + } + else if (ctrl_pressed) { + const is_selected = this.selected_indexes.find((idx) => row.idx === idx); + + if (is_selected) { + this.selected_indexes = this.selected_indexes.filter(idx => idx !== row.idx); + } + else { + this.selected_indexes.push(row.idx); + } + + this.last_single_selected_index = row.idx; + + plman.SetPlaylistSelectionSingle(this.cur_playlist_idx, row.idx, !is_selected); + } + else { + this.selected_indexes.push(row.idx); + this.last_single_selected_index = row.idx; + + plman.ClearPlaylistSelection(this.cur_playlist_idx); + plman.SetPlaylistSelectionSingle(this.cur_playlist_idx, row.idx, true); + } + + plman.SetPlaylistFocusItem(this.cur_playlist_idx, row.idx); + } + + /** + * Gets the indexes of the rows that should be selected when the Shift key is pressed. + * @param {number} selected_idx - The index of the row that was selected. + * @returns {Range} The range of indexes that should be selected. + * @private + */ + _get_shift_selection(selected_idx) { + let a = 0; + let b = 0; + + if (this.last_single_selected_index == null) { + this.last_single_selected_index = plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx); + if (this.last_single_selected_index === -1) { + this.last_single_selected_index = 0; + } + } + + if (this.cnt_helper.is_item_visible(this.rows[this.last_single_selected_index])) { + if (this.last_single_selected_index < selected_idx) { + a = this.last_single_selected_index; + b = selected_idx; + } + else { + a = selected_idx; + b = this.last_single_selected_index; + } + } + else { + const last_selected_header = this.cnt_helper.get_visible_parent(this.rows[this.last_single_selected_index]); + if (this.last_single_selected_index < selected_idx) { + a = last_selected_header.get_row_indexes()[0]; + b = selected_idx; + } + else { + a = selected_idx; + b = Last(last_selected_header.get_row_indexes()); + } + } + + return Range(a, b + 1); + } + + /** + * Checks if the selection is contiguous. + * @returns {boolean} True or false. + * @private + */ + _is_selection_contiguous() { + for (let i = 1; i < this.selected_indexes.length; i++) { + if (this.selected_indexes[i] - this.selected_indexes[i - 1] !== 1) { + return false; + } + } + return true; + } + + /** + * Moves the selection to the given index. + * @param {number} new_idx - The new index of the selection. + * @private + */ + _move_selection(new_idx) { + plman.UndoBackup(this.cur_playlist_idx); + let move_delta; + + if (this._is_selection_contiguous()) { + const focus_idx = plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx); + + move_delta = new_idx < focus_idx ? -(this.selected_indexes[0] - new_idx) : new_idx - (Last(this.selected_indexes) + 1); + } + else { + const item_count_before_drop_idx = this.selected_indexes.filter(idx => idx < new_idx).length; + + move_delta = -(plman.PlaylistItemCount(this.cur_playlist_idx) - this.selected_indexes.length - (new_idx - item_count_before_drop_idx)); + + // Move to the end to make it contiguous, then back to drop_idx + plman.MovePlaylistSelection(this.cur_playlist_idx, plman.PlaylistItemCount(this.cur_playlist_idx)); + } + plman.MovePlaylistSelection(this.cur_playlist_idx, move_delta); + } + + /** + * Clears the last hover row. + * @private + */ + _clear_last_hover_row() { + if (!this.last_hover_row) return; + this.last_hover_row.is_drop_bottom_selected = false; + this.last_hover_row.is_drop_top_selected = false; + this.last_hover_row.is_drop_boundary_reached = false; + this.last_hover_row.repaint(); + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Initializes the selection state. + */ + initialize_selection() { + this.selected_indexes = []; + let i = 0; + for (const item of this.rows) { + if (plman.IsPlaylistItemSelected(this.cur_playlist_idx, item.idx)) { + this.selected_indexes.push(i); + } + i++; + } + } + + /** + * Updates the selection state. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to update the selection state for. + * @param {boolean} [ctrl_pressed] - Whether CTRL key is pressed. + * @param {boolean} [shift_pressed] - Whether SHIFT key is pressed. + */ + update_selection(item, ctrl_pressed, shift_pressed) { + if (!item) { + return; + } + Assert(item != null, LogicError, 'update_selection was called with undefined item'); + + if (!ctrl_pressed && !shift_pressed) { + this.selected_indexes = []; + this.last_single_selected_index = undefined; + } + + const visible_item = this.cnt_helper.is_item_visible(item) ? item : this.cnt_helper.get_visible_parent(item); + if (visible_item instanceof PlaylistBaseHeader) { + this._update_selection_with_header(visible_item, ctrl_pressed, shift_pressed); + } + else { + this._update_selection_with_row(/** @type {PlaylistRow}*/ visible_item, ctrl_pressed, shift_pressed); + } + + this.selected_indexes.sort(this.numeric_ascending_sort); + } + + /** + * Moves the selection up one row. + */ + move_selection_up() { + if (!this.selected_indexes.length) { + return; + } + + this._move_selection(Math.max(0, this.selected_indexes[0] - 1)); + } + + /** + * Moves the selection down one row. + */ + move_selection_down() { + if (!this.selected_indexes.length) { + return; + } + + this._move_selection(Math.min(this.rows.length, Last(this.selected_indexes) + 2)); + } + + /** + * Selects all playlist items. + */ + select_all() { + if (!this.rows.length) { + return; + } + + this.selected_indexes = Range(this.rows[0].idx, Last(this.rows).idx + 1); + this.last_single_selected_index = this.rows[0].idx; + + plman.SetPlaylistSelection(this.cur_playlist_idx, this.selected_indexes, true); + } + + /** + * Clears current selection on the playlist item. + */ + clear_selection() { + if (!this.selected_indexes.length) { + return; + } + this.selected_indexes = []; + this.last_single_selected_index = undefined; + plman.ClearPlaylistSelection(this.cur_playlist_idx); + } + + /** + * Gets the indexes of selected playlist items. + * @returns {number} The indexes of the selected items. + */ + get_selected_items() { + return this.selected_indexes; + } + + /** + * Whether there are any selected playlist items. + * @returns {boolean} True if any items are selected. + */ + has_selected_items() { + return !!this.selected_indexes.length; + } + + /** + * Gets the total number of selected playlist items. + * @returns {number} The number of selected items. + */ + selected_items_count() { + return this.selected_indexes.length; + } + + /** + * Performs internal drag and drop of the selected playlist items inside the panel, i.e when reordering. + */ + perform_internal_drag_n_drop() { + this.enable_drag(); + this.internal_drag_n_drop_active = true; + + const cur_playlist_size = plman.PlaylistItemCount(this.cur_playlist_idx); + const cur_playlist_selection = plman.GetPlaylistSelectedItems(this.cur_playlist_idx); + const cur_selected_indexes = this.selected_indexes; + + const effect = fb.DoDragDrop(window.ID, cur_playlist_selection, PlaylistDropEffect.copy | PlaylistDropEffect.move | PlaylistDropEffect.link); + + this.internal_drag_n_drop_active = false; + + if (this.dragging) { + // If drag operation was not cancelled, then it means that nor on_drag_drop, nor on_drag_leave event handlers + // were triggered, which means that the items were most likely dropped inside the panel + // (and relevant methods were not called because of async event processing) + return; + } + + /** + * Checks if the playlist is in the same state and if the selected indexes are equal. + * This is necessary to ensure that we can handle the 'move drop' properly. + * @returns {boolean} True or false. + */ + const can_handle_move_drop = function() { + // We can handle the 'move drop' properly only when playlist is still in the same state + return cur_playlist_size === plman.PlaylistItemCount(this.cur_playlist_idx) + && this.arraysEqual(cur_selected_indexes, this.selected_indexes); + }; + + if (PlaylistDropEffect.none === effect && can_handle_move_drop()) { + // DROPEFFECT_NONE needs special handling, because on NT it + // is returned for some move operations, instead of DROPEFFECT_MOVE. + // See Q182219 for the details. + + const items_to_remove = []; + const playlist_items = plman.GetPlaylistItems(this.cur_playlist_idx); + for (const idx of cur_selected_indexes) { + const cur_item = playlist_items[idx]; + if (cur_item.RawPath.startsWith('file://') && !fso.FileExists(cur_item.Path)) { + items_to_remove.push(idx); + } + } + + if (items_to_remove.length) { + plman.ClearPlaylistSelection(this.cur_playlist_idx); + plman.SetPlaylistSelection(this.cur_playlist_idx, items_to_remove, true); + plman.RemovePlaylistSelection(this.cur_playlist_idx); + } + } + else if (PlaylistDropEffect.move === effect && can_handle_move_drop()) { + plman.RemovePlaylistSelection(this.cur_playlist_idx); + } + } + + /** + * Enables dragging. + */ + enable_drag() { + this._clear_last_hover_row(); + this.dragging = true; + } + + /** + * Disables dragging. + */ + disable_drag() { + this._clear_last_hover_row(); + this.dragging = false; + } + + /** + * Enables external dragging. + */ + enable_external_drag() { + this.enable_drag(); + this.internal_drag_n_drop_active = false; + } + + /** + * Checks whether dragging is active. + * @returns {boolean} True or false. + */ + is_dragging() { + return this.dragging; + } + + /** + * Checks whether the internal drag and drop is active. + * @returns {boolean} True or false. + */ + is_internal_drag_n_drop_active() { + return this.internal_drag_n_drop_active; + } + + /** + * Updates the hover row's drop state, also calls repaint. + * @param {?PlaylistRow} hover_row - The hover row. + * @param {boolean} is_above - Whether the hover row is above the dragged item. + */ + drag(hover_row, is_above) { + if (hover_row == null) { + this._clear_last_hover_row(); + return; + } + + if (plman.IsPlaylistLocked(this.cur_playlist_idx)) { + return; + } + + let is_drop_top_selected = is_above; + let is_drop_bottom_selected = !is_above; + const is_drop_boundary_reached = hover_row.idx === 0 || (!is_above && hover_row.idx === this.rows.length - 1); + + if (this.internal_drag_n_drop_active && !utils.IsKeyPressed(VK_CONTROL)) { + // Can't move selected item on itself + const is_item_above_selected = hover_row.idx !== 0 && this.rows[hover_row.idx - 1].is_selected(); + const is_item_below_selected = hover_row.idx !== (this.rows.length - 1) && this.rows[hover_row.idx + 1].is_selected(); + is_drop_top_selected = is_drop_top_selected && !hover_row.is_selected() && !is_item_above_selected; + is_drop_bottom_selected = is_drop_bottom_selected && !hover_row.is_selected() && !is_item_below_selected; + } + + let needs_repaint = false; + if (this.last_hover_row) { + if (this.last_hover_row.idx === hover_row.idx) { + needs_repaint = this.last_hover_row.is_drop_top_selected !== is_drop_top_selected + || this.last_hover_row.is_drop_bottom_selected !== is_drop_bottom_selected + || this.last_hover_row.is_drop_boundary_reached !== is_drop_boundary_reached; + } + else { + this._clear_last_hover_row(); + needs_repaint = true; + } + } + else { + needs_repaint = true; + } + + hover_row.is_drop_top_selected = is_drop_top_selected; + hover_row.is_drop_bottom_selected = is_drop_bottom_selected; + hover_row.is_drop_boundary_reached = is_drop_boundary_reached; + + if (needs_repaint) { + hover_row.repaint(); + } + + this.last_hover_row = hover_row; + } + + /** + * Checks whether the playlist is in a state where it can accept a drop. + * @returns {boolean} True or false. + */ + can_drop() { + let playlistIndex = false; + return () => { + if (plman.PlaylistCount > 0 && this.cur_playlist_idx >= 0 && this.cur_playlist_idx < plman.PlaylistCount && !plman.IsPlaylistLocked(this.cur_playlist_idx)) { + return true; + } else { + if (!playlistIndex) { // If no playlist exists, create a new one. + const playlist_idx = plman.CreatePlaylist(0, 'Default'); + plman.ActivePlaylist = playlist_idx; + playlistIndex = true; + } + return false; + } + }; + } + + /** + * Handles a drop event. + * @param {boolean} copy_selection - Whether to copy the selection instead of moving it. + */ + drop(copy_selection) { + if (!this.dragging) { + return; + } + + this.dragging = false; + if (!this.selected_indexes.length || !this.last_hover_row) { + return; + } + + if (!this.last_hover_row.is_drop_top_selected && !this.last_hover_row.is_drop_bottom_selected) { + this._clear_last_hover_row(); + return; + } + + let drop_idx = this.last_hover_row.idx; + if (this.last_hover_row.is_drop_bottom_selected) { + ++drop_idx; + } + + this._clear_last_hover_row(); + + if (!copy_selection) { + this._move_selection(drop_idx); + } + else { + plman.UndoBackup(this.cur_playlist_idx); + + const cur_selection = plman.GetPlaylistSelectedItems(this.cur_playlist_idx); + plman.ClearPlaylistSelection(this.cur_playlist_idx); + plman.InsertPlaylistItems(this.cur_playlist_idx, drop_idx, cur_selection, true); + plman.SetPlaylistFocusItem(this.cur_playlist_idx, drop_idx); + } + } + + /** + * Handles external drag and drop. + * @param {DropTargetAction} action - The drag and drop action. + */ + external_drop(action) { + plman.ClearPlaylistSelection(this.cur_playlist_idx); + + let playlist_idx; + if (!plman.PlaylistCount) { + playlist_idx = plman.CreatePlaylist(0, 'Default'); + plman.ActivePlaylist = playlist_idx; + } + else { + playlist_idx = this.cur_playlist_idx; + plman.UndoBackup(this.cur_playlist_idx); + } + + action.Playlist = playlist_idx; + action.ToSelect = true; + + if (this.last_hover_row) { + let drop_idx = this.last_hover_row.idx; + if (this.last_hover_row.is_drop_bottom_selected) { + ++drop_idx; + } + action.Base = drop_idx; + } + else { + action.Base = plman.PlaylistCount; + } + + this.disable_drag(); + } + + /** + * Copies the selected playlist items to the clipboard. + * @returns {void} + */ + copy() { + if (!this.selected_indexes.length) { + return fb.CreateHandleList(); + } + + const selected_items = plman.GetPlaylistSelectedItems(this.cur_playlist_idx); + fb.CopyHandleListToClipboard(selected_items); + } + + /** + * Cuts the selected playlist items to the clipboard. + * @returns {void} + */ + cut() { + if (!this.selected_indexes.length) { + return fb.CreateHandleList(); + } + + const selected_items = plman.GetPlaylistSelectedItems(this.cur_playlist_idx); + + if (fb.CopyHandleListToClipboard(selected_items)) { + plman.UndoBackup(this.cur_playlist_idx); + plman.RemovePlaylistSelection(this.cur_playlist_idx); + } + } + + /** + * Pastes the contents of the clipboard to the playlist. + */ + paste() { + if (!fb.CheckClipboardContents()) { + return; + } + + const metadb_list = fb.GetClipboardContents(window.ID); + if (!metadb_list || !metadb_list.Count) { + return; + } + + let insert_idx; + if (this.selected_indexes.length) { + insert_idx = this._is_selection_contiguous() ? Last(this.selected_indexes) + 1 : plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx) + 1; + } + else { + const focused_idx = plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx); + insert_idx = (focused_idx !== -1) ? (focused_idx + 1) : this.rows.length; + } + + plman.UndoBackup(this.cur_playlist_idx); + plman.ClearPlaylistSelection(this.cur_playlist_idx); + plman.InsertPlaylistItems(this.cur_playlist_idx, insert_idx, metadb_list, true); + plman.SetPlaylistFocusItem(this.cur_playlist_idx, insert_idx); + } + + /** + * Sends the selected playlist items to the specified playlist. + * @param {number} playlist_idx - The current playlist index. + */ + send_to_playlist(playlist_idx) { + plman.UndoBackup(playlist_idx); + plman.ClearPlaylistSelection(playlist_idx); + plman.InsertPlaylistItems(playlist_idx, plman.PlaylistItemCount(playlist_idx), plman.GetPlaylistSelectedItems(this.cur_playlist_idx), true); + } + // #endregion +} + + +////////////////////////// +// * COLLAPSE HANDLER * // +////////////////////////// +/** + * A class that handles the folding of the playlist headers. + */ +class PlaylistCollapseHandler { + /** + * Creates the `PlaylistCollapseHandler` instance. + * @param {PlaylistContent} cnt_arg - The playlist content instance. + */ + constructor(cnt_arg) { + /** @private @type {object} */ + this.cnt_arg = cnt_arg; + /** @private @type {boolean} */ + this.changed = false; + /** @private @type {Array} */ + this.headers = cnt_arg.sub_items; + /** @private @type {?Function} */ + this.on_collapse_change_callback = undefined; + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Sets the collapsed state of the header and all its sub-items recursively. + * @param {PlaylistBaseHeader} header - The header to set the collapsed state of. + * @param {boolean} is_collapsed - Whether the header should be collapsed or not. + * @returns {boolean} Whether the collapsed state of any header was changed. + * @private + */ + _set_collapsed_state_recursive(header, is_collapsed) { + let changed = header.is_collapsed !== is_collapsed; + header.is_collapsed = is_collapsed; + + const sub_items = header.sub_items; + if (sub_items[0] instanceof PlaylistRow) { + return changed; + } + + for (const item of sub_items) { + changed = this._set_collapsed_state_recursive(item, is_collapsed) || changed; + } + + return changed; + } + + /** + * Triggers the callback if the collapse state has changed. + * @private + */ + _trigger_callback() { + if (this.changed && this.on_collapse_change_callback) { + this.on_collapse_change_callback(); + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBILC METHODS + /** + * Collapses a playlist item. + * @param {PlaylistBaseHeader} item - The item to collapse. + */ + collapse(item) { + this.changed = this._set_collapsed_state_recursive(item, true); + this._trigger_callback(); + } + + /** + * Collapses all playlist items. + */ + collapse_all() { + this.changed = false; + for (const item of this.headers) { + this.changed = this._set_collapsed_state_recursive(item, true) || this.changed; + } + this._trigger_callback(); + } + + /** + * Collapses all playlist items except the now playing track. + */ + collapse_all_but_now_playing() { + this.changed = false; + for (const item of this.headers) { + if (item.is_playing()) { + this.changed = this._set_collapsed_state_recursive(item, false) || this.changed; + continue; + } + this.changed = this._set_collapsed_state_recursive(item, true) || this.changed; + } + this._trigger_callback(); + } + + /** + * Toggles the collapse state of a playlist item. + * @param {PlaylistBaseHeader} item - The item to toggle. + */ + toggle_collapse(item) { + this.changed = true; + this._set_collapsed_state_recursive(item, !item.is_collapsed); + this._trigger_callback(); + } + + /** + * Expands a playlist item. + * @param {PlaylistBaseHeader} item - The item to expand. + */ + expand(item) { + this.changed = this._set_collapsed_state_recursive(item, false); + this._trigger_callback(); + } + + /** + * Expands all playlist items. + */ + expand_all() { + this.changed = false; + for (const item of this.headers) { + this.changed = this._set_collapsed_state_recursive(item, false) || this.changed; + } + this._trigger_callback(); + } + + /** + * Callback for when the content changes. + */ + on_content_change() { + this.headers = this.cnt_arg.sub_items; + this.changed = false; + + if (plSet.show_header && plSet.collapse_on_playlist_switch) { + if (plSet.auto_collapse) { + this.collapse_all_but_now_playing(); + } + else { + this.collapse_all(); + } + } + } + + /** + * Sets the callback that will be called when the collapse state of a playlist item changes. + * @param {Function} on_collapse_change_callback_arg - The callback. + */ + set_callback(on_collapse_change_callback_arg) { + this.on_collapse_change_callback = on_collapse_change_callback_arg; + } + // #endregion +} + + +/////////////////////// +// * QUEUE HANDLER * // +/////////////////////// +/** + * A class that handles the playback queue for a playlist by adding, removing, and checking the status of queued items. + */ +class PlaylistQueueHandler { + /** + * Creates the `PlaylistQueueHandler` instance. + * @param {Array} rows_arg - The initial set of playlist rows to be handled. + * @param {number} cur_playlist_idx_arg - The current index of the playlist being processed. + */ + constructor(rows_arg, cur_playlist_idx_arg) { + /** @private @constant @type {number} */ + this.cur_playlist_idx = cur_playlist_idx_arg; + /** @private @constant @type {Array} */ + this.rows = rows_arg; + /** @private @type {Array} */ + this.queued_rows = []; + + this.initialize_queue(); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Resets the queue indexes and count for each item in the `queued_rows` array and clears the array. + * @private + */ + _reset_queued_status() { + if (!this.queued_rows.length) { + return; + } + + for (const item of this.queued_rows) { + item.queue_indexes = undefined; + item.queue_idx_count = 0; + } + + this.queued_rows = []; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Initializes the playback queue contents and adding the queued items to the appropriate rows. + */ + initialize_queue() { + if (this.queued_rows.length) { + this._reset_queued_status(); + } + + const queue_contents = plman.GetPlaybackQueueContents(); + if (!queue_contents.length) { + return; + } + + let i = 0; + for (const queued_item of queue_contents) { + if (queued_item.PlaylistIndex !== this.cur_playlist_idx || queued_item.PlaylistItemIndex === -1) { + continue; + } + + const cur_queued_row = this.rows[queued_item.PlaylistItemIndex]; + if (!cur_queued_row) continue; + const has_row = this.queued_rows.find(queued_row => queued_row.idx === cur_queued_row.idx); + + if (!has_row) { + cur_queued_row.queue_indexes = [i + 1]; + cur_queued_row.queue_idx_count = 1; + } + else { + cur_queued_row.queue_indexes.push(i + 1); + cur_queued_row.queue_idx_count++; + } + + this.queued_rows.push(cur_queued_row); + i++; + } + } + + /** + * Checks if there are any playlist items in the playback queue. + * @returns {boolean} True or false. + */ + has_items() { + return !!plman.GetPlaybackQueueHandles().Count; + } + + /** + * Adds a playlist item to the playback queue. + * @param {PlaylistRow} row - The playlist row to add to the playback queue. + */ + add_row(row) { + if (!row) return; + + plman.AddPlaylistItemToPlaybackQueue(this.cur_playlist_idx, row.idx); + } + + /** + * Removes a row from the playback queue. + * @param {PlaylistRow} row - The playlist row to remove from the playback queue. + */ + remove_row(row) { + if (!row) return; + + const idx = plman.FindPlaybackQueueItemIndex(row.metadb, this.cur_playlist_idx, row.idx); + if (idx !== -1) { + plman.RemoveItemFromPlaybackQueue(idx); + } + } + + /** + * Flushes the playback queue. + */ + flush() { + plman.FlushPlaybackQueue(); + } + // #endregion +} + + +////////////////////////// +// * GROUPING HANDLER * // +////////////////////////// +/** + * A class that handles the grouping settings and behavior for playlists. + */ +class PlaylistGroupingHandler { + /** + * Creates the `PlaylistGroupingHandler` instance. + */ + constructor() { + /** @private @type {PlaylistGroupingSettings} */ + this.settings = new PlaylistGroupingSettings(); + /** @private @type {?Array} */ + this.playlists = []; + /** @private @type {string} */ + this.cur_playlist_name = ''; + /** @private @type {?PlaylistGroupingSettings} */ + this.cur_group = undefined; + /** @private @type {?Array} */ + this.group_by_name = undefined; + + this._initialize_name_to_preset_map(); + this._initialize_playlists(); + this._cleanup_settings(); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Initializes the list of playlists. + * @private + */ + _initialize_playlists() { + this.playlists = []; + const playlist_count = plman.PlaylistCount; + for (let i = 0; i < playlist_count; ++i) { + this.playlists.push(plman.GetPlaylistName(i)); + } + } + + /** + * Initializes the map from group name to group object. + * @private + */ + _initialize_name_to_preset_map() { + this.group_by_name = this.settings.group_presets.map((item) => item.name); + } + + /** + * Opens the group presets manager dialog. + * @param {Function} on_execute_callback_fn - The function to call when the dialog is closed. + * @private + */ + _manage_groupings(on_execute_callback_fn) { + const on_ok_fn = (ret_val_json) => { + const ret_val = JSON.parse(ret_val_json); + + this.settings.group_presets = ret_val[0]; + this.settings.default_group_name = ret_val[2]; + this._initialize_name_to_preset_map(); + + this.cur_group = this.settings.group_presets[this.group_by_name.indexOf(ret_val[1])]; + this.settings.playlist_group_data[this.cur_playlist_name] = ret_val[1]; + + delete this.settings.playlist_custom_group_data[this.cur_playlist_name]; + + this.settings.save(); + this.settings.send_sync(); + + grCfg.config.addConfigurationObject(grDef.themePlaylistGroupingPresetsSchema, this.settings.group_presets); + grCfg.config.writeConfiguration(); + + on_execute_callback_fn(); + }; + + const htmlCode = grm.utils.prepareHTML(`${fb.ProfilePath}georgia-reborn\\scripts\\playlist\\assets\\html\\GroupPresetsMngr.html`); + utils.ShowHtmlDialog(window.ID, htmlCode, { width: 650, height: 425, data: [JSON.stringify([this.settings.group_presets, this.cur_group.name, this.settings.default_group_name]), on_ok_fn] }); + } + + /** + * Opens a dialog box that allows the user to enter a custom grouping query. + * The function also takes a callback function that is called when the user clicks the OK button. + * @param {Function} on_execute_callback_fn - The callback function to call when the user clicks the OK button. + * @private + */ + _request_user_query(on_execute_callback_fn) { + const on_ok_fn = (ret_val) => { + const groupHandlerSettings = new PlaylistGroupingSettings(); + const custom_group = groupHandlerSettings.group('user_defined', '', ret_val[0], ret_val[1]); + this.cur_group = custom_group; + + this.settings.playlist_group_data[this.cur_playlist_name] = 'user_defined'; + this.settings.playlist_custom_group_data[this.cur_playlist_name] = custom_group; + + this.settings.save(); + this.settings.send_sync(); + + on_execute_callback_fn(); + }; + + const parsed_query = this.cur_group.name === 'user_defined' ? [this.cur_group.group_query, this.cur_group.title_query] : ['', '[%album artist%]']; + const htmlCode = grm.utils.prepareHTML(`${fb.ProfilePath}georgia-reborn\\scripts\\playlist\\assets\\html\\MsgBox.html`); + utils.ShowHtmlDialog(window.ID, htmlCode, { width: 650, height: 425, data: ['Foobar2000: Group by', ['Grouping Query', 'Title Query'], parsed_query, on_ok_fn] }); + } + + /** + * Removes any playlists that are no longer present from the settings. + * @private + */ + _cleanup_settings() { + for (const i in this.settings.playlist_group_data) { + console.log(i); + if (!this.playlists.includes(i)) { + delete this.settings.playlist_group_data[i]; + } + } + + for (const i in this.settings.playlist_custom_group_data) { + if (!this.playlists.includes(i)) { + delete this.settings.playlist_custom_group_data[i]; + } + } + + this.settings.save(); + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Sets the active playlist. + * @param {string} cur_playlist_name_arg - The name of the playlist to make active. + */ + set_active_playlist(cur_playlist_name_arg) { + this.cur_playlist_name = cur_playlist_name_arg; + let group_name = this.settings.playlist_group_data[this.cur_playlist_name]; + + this.cur_group = null; + if (group_name) { + if (group_name === 'user_defined') { + this.cur_group = this.settings.playlist_custom_group_data[this.cur_playlist_name]; + } + else if (this.group_by_name.includes(group_name)) { + this.cur_group = this.settings.group_presets[this.group_by_name.indexOf(group_name)]; + } + + if (!this.cur_group) { + delete this.settings.playlist_group_data[this.cur_playlist_name]; + group_name = ''; + } + } + + if (!this.cur_group) { + group_name = this.settings.default_group_name; + this.cur_group = this.settings.group_presets[this.group_by_name.indexOf(group_name)]; + } + + Assert(this.cur_group != null, ArgumentError, 'group_name', group_name); + } + + /** + * Gets the query for the active playlist. + * @returns {string} The query for the active playlist. + */ + get_query() { + return this.cur_group.group_query; + } + + /** + * Gets the title query for the active playlist. + * @returns {string} The title query for the active playlist. + */ + get_title_query() { + return this.cur_group.title_query; + } + + /** + * Gets the sub-title query for the active playlist. + * @returns {string} The sub-title query for the active playlist. + */ + get_sub_title_query() { + return this.cur_group.sub_title_query; + } + + /** + * Gets the name of the current query. + * @returns {string} The name of the current query. + */ + get_query_name() { + return this.cur_group.name; + } + + /** + * Whether to show the disc for the current query. + * @returns {boolean} True or false. + */ + show_disc() { + return this.cur_group.show_disc; + } + + /** + * Whether to show the date for the current query. + * @returns {boolean} True or false. + */ + show_date() { + return this.cur_group.show_date; + } + + /** + * Appends the menu to the given parent menu. + * @param {ContextMenu} parent_menu - The parent menu to append to. + * @param {Function} on_execute_callback_fn - The callback function to call when an item is executed. + */ + append_menu_to(parent_menu, on_execute_callback_fn) { + const group = new ContextMenu('Grouping'); + parent_menu.append(group); + + group.appendItem('Manage presets', () => { + this._manage_groupings(on_execute_callback_fn); + }); + + group.separator(); + + group.appendItem('Reset to default', () => { + delete this.settings.playlist_custom_group_data[this.cur_playlist_name]; + delete this.settings.playlist_group_data[this.cur_playlist_name]; + + this.cur_group = this.settings.group_presets[this.group_by_name.indexOf(this.settings.default_group_name)]; + + this.settings.save(); + this.settings.send_sync(); + this.settings.load(); + + grCfg.config.addConfigurationObject(grDef.themePlaylistGroupingPresetsSchema, grDef.themePlaylistGroupingPresets); + grCfg.config.writeConfiguration(); + + on_execute_callback_fn(); + }); + + group.separator(); + + let group_by_text = 'by...'; + if (this.cur_group.name === 'user_defined') { + group_by_text += ` [${this.get_query()}]`; + } + group.appendItem(group_by_text, () => { + this._request_user_query(on_execute_callback_fn); + }, { is_radio_checked: this.cur_group.name === 'user_defined' }); + + for (const group_item of this.settings.group_presets) { + let group_by_text = group_item.description; + if (group_item.name === this.settings.default_group_name) { + group_by_text += ' [default]'; + } + + group.appendItem(group_by_text, () => { + this.cur_group = group_item; + + delete this.settings.playlist_custom_group_data[this.cur_playlist_name]; + + this.settings.playlist_group_data[this.cur_playlist_name] = group_item.name; + this.settings.save(); + this.settings.send_sync(); + + on_execute_callback_fn(); + }, { is_radio_checked: this.cur_group.name === group_item.name }); + } + } + + /** + * Called when the sync state is changed. + * Updates the settings object with the new state and reinitializes the name to preset map. + * It also sets the active playlist to the current playlist name. + * @param {?} value - The new sync state. + */ + sync_state(value) { + this.settings.receive_sync(value); + this._initialize_name_to_preset_map(); + this.set_active_playlist(this.cur_playlist_name); + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Callback for when the playlists have changed. + */ + on_playlists_changed() { + const playlist_count = plman.PlaylistCount; + const new_playlists = []; + for (let i = 0; i < playlist_count; ++i) { + new_playlists.push(plman.GetPlaylistName(i)); + } + + let save_needed = false; + + if (this.playlists.length > playlist_count) { + // Removed + + const playlists_to_remove = Difference(this.playlists, new_playlists); + for (const playlist_name of playlists_to_remove) { + delete this.settings.playlist_group_data[playlist_name]; + delete this.settings.playlist_custom_group_data[playlist_name]; + } + + save_needed = true; + } + else if (this.playlists.length === playlist_count) { + // May be renamed? + + const playlist_difference_new = Difference(new_playlists, this.playlists); + const playlist_difference_old = Difference(this.playlists, new_playlists); + if (playlist_difference_old.length === 1) { + // playlist_difference_new.length > 0 and playlist_difference_old.length === 0 means that + // playlists contained multiple items of the same name (one of which was changed) + const old_name = playlist_difference_old[0]; + const new_name = playlist_difference_new[0]; + + const group_name = this.settings.playlist_group_data[old_name]; + const custom_group = this.settings.playlist_custom_group_data[old_name]; + + this.settings.playlist_group_data[new_name] = group_name; + if (custom_group) { + this.settings.playlist_custom_group_data[new_name] = custom_group; + } + + delete this.settings.playlist_group_data[old_name]; + delete this.settings.playlist_custom_group_data[old_name]; + + save_needed = true; + } + } + + this.playlists = new_playlists; + if (save_needed) { + this.settings.save(); + } + } + // #endregion +} + + +/** + * A class that handles and manipulates settings related to grouping playlists. + */ +class PlaylistGroupingSettings { + /** + * Creates the `PlaylistGroupingSettings` instance. + */ + constructor() { + /** + * Reads the configuration from the `config` object and assigns it to the `prefs` variable. + * @public @type {ReturnType} + */ + this.prefs = grCfg.config.readConfiguration(); + /** + * An object that maps playlist names to their group names. + * @public @type {{ [playlistName: string]: string }} + */ + this.playlist_group_data = {}; + /** + * An object that maps playlist names to their custom grouping settings. + * @public @type {{ [playlistName: string]: PlaylistGroupingSettings }} + */ + this.playlist_custom_group_data = {}; + /** @public @type {string} */ + this.default_group_name = ''; + /** @public @type {Array} */ + this.group_presets = []; + + this._fixup_pl_set(); + this.load(); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Checks and fixes the values of certain properties in the `plSet` object. + * @private + */ + _fixup_pl_set() { + if (!plSet.playlist_group_data || !IsObject(JSON.parse(plSet.playlist_group_data))) { + plSet.playlist_group_data = JSON.stringify({}); + } + + if (!plSet.playlist_custom_group_data || !IsObject(JSON.parse(plSet.playlist_custom_group_data))) { + plSet.playlist_custom_group_data = JSON.stringify({}); + } + + if (!plSet.group_presets || !Array.isArray(JSON.parse(plSet.group_presets))) { + plSet.group_presets = JSON.stringify([ + new PlaylistGroupingPreset('artist', 'by artist', '%album artist%', undefined, '', {}), + new PlaylistGroupingPreset('artist_album', 'by artist / album', '%album artist%%album%', undefined, undefined, { show_date: true }), + new PlaylistGroupingPreset('artist_album_disc', 'by artist / album / disc number', '%album artist%%album%%discnumber%', undefined, undefined, { show_date: true, show_disc: true }), + new PlaylistGroupingPreset('artist_album_disc_edition', 'by artist / album / disc number / edition / codec', '%album artist%%album%%discnumber%%edition%%codec%', undefined, undefined, { show_date: true, show_disc: true }), + new PlaylistGroupingPreset('path', 'by path', '$directory_path(%path%)', undefined, undefined, { show_date: true }), + new PlaylistGroupingPreset('date', 'by date', '%date%', undefined, undefined, { show_date: true }) + ]); + } + + if (!plSet.default_group_name || !IsString(plSet.default_group_name)) { + plSet.default_group_name = this.prefs.themePlaylistGroupingPresets[3].name || this.prefs.themePlaylistGroupingPresets[0].name || 'artist_album_disc_edition'; + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Loads settings from the properties object. + */ + load() { + this.playlist_group_data = JSON.parse(plSet.playlist_group_data); + this.playlist_custom_group_data = JSON.parse(plSet.playlist_custom_group_data); + this.default_group_name = plSet.default_group_name; + this.group_presets = this.prefs.themePlaylistGroupingPresets || JSON.parse(plSet.group_presets); + } + + /** + * Saves settings to the properties object. + */ + save() { + plSet.playlist_group_data = JSON.stringify(this.playlist_group_data); + plSet.playlist_custom_group_data = JSON.stringify(this.playlist_custom_group_data); + plSet.default_group_name = this.default_group_name; + plSet.group_presets = JSON.stringify(this.group_presets); + } + + /** + * Sends the sync data to the other clients. + */ + send_sync() { + const syncData = { + g_playlist_group_data: plSet.playlist_group_data, + g_playlist_custom_group_data: plSet.playlist_custom_group_data, + g_default_group_name: plSet.default_group_name, + g_group_presets: plSet.group_presets + }; + + window.NotifyOthers('sync_group_query_state', syncData); + } + + /** + * Receives the sync data from the other clients. + * @param {{g_playlist_group_data, g_playlist_custom_group_data, g_default_group_name, g_group_presets}} settings_data - The sync data. + */ + receive_sync(settings_data) { + plSet.playlist_group_data = settings_data.g_playlist_group_data; + plSet.playlist_custom_group_data = settings_data.g_playlist_custom_group_data; + plSet.default_group_name = settings_data.g_default_group_name; + plSet.group_presets = settings_data.g_group_presets; + + this.load(); + } + // #endregion +} + + +/** + * A class that handles a grouping configuration for a playlist within the PlaylistGroupingSettings namespace. + */ +class PlaylistGroupingPreset { + /** + * Creates the `PlaylistGroupingPreset` instance. + * @param {string} name - The name of the group. + * @param {string} description - A brief description of the group. + * @param {?string} [group_query] - The query used to group items within the playlist. + * @param {?string} [title_query] - The query used to generate the title for the group. + * @param {?string} [sub_title_query] - The query used to generate the subtitle for the group. + * @param {object} [options] - Additional options for the group. + * @param {boolean} [options.show_date] - A flag indicating whether to show the date within the group. + * @param {boolean} [options.show_disc] - A flag indicating whether to show disc information within the group. + */ + constructor(name, description, group_query = '', title_query = '[%album artist%]', sub_title_query = "[%album%[ '('%albumsubtitle%')']][ - '['%edition%']']", options = {}) { + /** @public @type {string} */ + this.name = name; + /** @public @type {string} */ + this.description = description; + /** @public @type {string} */ + this.group_query = group_query; + /** @public @type {string} */ + this.title_query = title_query; + /** @public @type {string} */ + this.sub_title_query = sub_title_query; + /** @public @type {boolean} */ + this.show_date = options.show_date || false; + /** @public @type {boolean} */ + this.show_disc = options.show_disc || false; + } +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-helpers.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-helpers.js new file mode 100644 index 00000000..7785c02e --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-helpers.js @@ -0,0 +1,115 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Helpers * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +/////////////// +// * FONTS * // +/////////////// +/** + * Creates and assigns playlist fonts. + * If fonts already exist and `forceCreation` is not set to true, the function will not recreate the fonts. + * @global + * @param {boolean} [forceCreation] - Determines whether fonts should be forcibly recreated. + */ +function PlaylistCreateFonts(forceCreation = false) { + if (!forceCreation && pl.font && Object.keys(pl.font).length > 0) { + return; // Fonts already created and no forceful creation requested + } + + const fontDefault = grSet.customThemeFonts ? grCfg.customFont.fontDefault : 'Segoe UI'; + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const rowFontSize = grSet[`playlistFontSize_${grSet.layout}`]; + + const titleNormalFont = grSet.customThemeFonts ? grCfg.customFont.playlistTitleNormal : 'Segoe UI'; + const titleSelectedFont = grSet.customThemeFonts ? grCfg.customFont.playlistTitleSelected : 'Segoe UI'; + const titlePlayingFont = grSet.customThemeFonts ? grCfg.customFont.playlistTitlePlaying : 'Segoe UI'; + + const artistNormalFont = grSet.customThemeFonts ? grCfg.customFont.playlistArtistNormal : 'Segoe UI Semibold'; + const artistPlayingFont = grSet.customThemeFonts ? grCfg.customFont.playlistArtistPlaying : 'Segoe UI Semibold'; + const artistNormalCompactFont = grSet.customThemeFonts ? grCfg.customFont.playlistArtistNormalCompact : 'Segoe UI Semibold'; + const artistPlayingCompactFont = grSet.customThemeFonts ? grCfg.customFont.playlistArtistPlayingCompact : 'Segoe UI Semibold'; + + const albumFont = grSet.customThemeFonts ? grCfg.customFont.playlistAlbum : 'Segoe UI Semibold'; + const dateFont = grSet.customThemeFonts ? grCfg.customFont.playlistDate : 'Segoe UI Semibold'; + const dateCompactFont = grSet.customThemeFonts ? grCfg.customFont.playlistDateCompact : 'Segoe UI Semibold'; + const infoFont = grSet.customThemeFonts ? grCfg.customFont.playlistInfo : 'Segoe UI'; + const coverFont = grSet.customThemeFonts ? grCfg.customFont.playlistCover : 'Segoe UI Semibold'; + + const playcountFont = grSet.customThemeFonts ? grCfg.customFont.playlistPlaycount : 'Segoe UI'; + + pl.font.title_normal = Font(titleNormalFont, rowFontSize); + pl.font.title_selected = Font(titleSelectedFont, rowFontSize); + pl.font.title_playing = Font(titlePlayingFont, rowFontSize); + + pl.font.artist_normal = Font(artistNormalFont, headerFontSize + 3, grSet.customThemeFonts ? FontStyle.bold : 0); + pl.font.artist_playing = Font(artistPlayingFont, headerFontSize + 3, grSet.customThemeFonts ? FontStyle.bold : 0); + pl.font.artist_normal_compact = Font(artistNormalCompactFont, headerFontSize); + pl.font.artist_playing_compact = Font(artistPlayingCompactFont, headerFontSize, FontStyle.underline); + + pl.font.album = Font(albumFont, headerFontSize); + pl.font.date = Font(dateFont, headerFontSize + 3); + pl.font.date_compact = Font(dateCompactFont, headerFontSize); + pl.font.info = Font(infoFont, rowFontSize - 1); + pl.font.cover = Font(coverFont, rowFontSize - 1); + + pl.font.playcount = Font(playcountFont, rowFontSize - 3); + pl.font.plr_track = Font(playcountFont, rowFontSize - 3); + pl.font.rating_not_set = Font('Segoe UI Symbol', rowFontSize + 2); + pl.font.rating_set = Font('Segoe UI Symbol', rowFontSize + 4); + pl.font.scrollbar = Font('Segoe UI Symbol', headerFontSize); + + pl.font.font_awesome = Font('FontAwesome', rowFontSize + 2); + pl.font.dummy_text = Font(fontDefault, rowFontSize + 1); +} + + +////////////////// +// * GEOMETRY * // +////////////////// +/** + * Rescales the playlist based on the font size settings and creates playlist fonts if they haven't been created already. + * @global + * @param {boolean} [forceRescale] - Whether the playlist should be rescaled even if the playlist fonts have already been created. + */ +function PlaylistRescale(forceRescale = false) { + PlaylistCreateFonts(forceRescale); + plSet.row_h = Math.round(grSet[`playlistFontSize_${grSet.layout}`] * 1.667); + pl.geo.row_h = SCALE(plSet.row_h); + pl.geo.scrollbar_w = plSet.scrollbar_w; // Don't use SCALE() + pl.geo.scrollbar_right_pad = SCALE(plSet.scrollbar_right_pad); + pl.geo.scrollbar_top_pad = SCALE(plSet.scrollbar_top_pad); + pl.geo.scrollbar_bottom_pad = SCALE(plSet.scrollbar_bottom_pad); + pl.geo.list_bottom_pad = SCALE(plSet.list_bottom_pad); +} + + +////////////////// +// * POSITION * // +////////////////// +/** + * Sets and updates the playlist x-coordinate when resizing or changing the playlist layout. + * @global + * @returns {number} The playlist x-coordinate. + */ +function PlaylistSetX() { + const noAlbumArtSize = grm.ui.wh - grm.ui.topMenuHeight - grm.ui.lowerBarHeight; + + if (grSet.panelWidthAuto && grm.ui.noAlbumArtStub) grm.ui.initNoAlbumArtSize(); + + return grSet.layout === 'default' && (grSet.playlistLayout === 'normal' || + grSet.playlistLayoutNormal && (grm.ui.displayBiography || grm.ui.displayLyrics)) ? + grSet.panelWidthAuto ? grm.ui.displayLibrarySplit() ? noAlbumArtSize : !fb.IsPlaying ? 0 : grm.ui.albumArtSize.x + grm.ui.albumArtSize.w : + grm.ui.ww * 0.5 : + 0; +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-content.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-content.js new file mode 100644 index 00000000..2a5746fd --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-content.js @@ -0,0 +1,427 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Content * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +///////////////// +// * CONTENT * // +///////////////// +/** + * A class that generates and manages items to draw in a playlist. + * @augments {BaseListRowContent} + */ +class PlaylistContent extends BaseListRowContent { + /** + * Creates the `PlaylistContent` instance. + */ + constructor() { + super(); + + /** + * It is assumed that every sub_items array contains only items of the same single type. + * @type {Array} + * @protected + */ + this.sub_items = []; + + /** @public @type {PlaylistContentNavigation} */ + this.helper = new PlaylistContentNavigation(this); + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Generates items to draw based on the given parameters and returns an array of items. + * @override + * @param {number} wy - The y-coordinate of the top edge of the visible area. + * @param {number} wh - The height of the viewport. + * @param {number} row_shift - The number of rows to shift the drawing position vertically. + * @param {number} pixel_shift - The number of pixels to shift the items vertically. + * @param {number} row_h - The height of each row in the drawing. + * @returns {Array} An array of items to draw. + */ + generate_items_to_draw(wy, wh, row_shift, pixel_shift, row_h) { + if (!this.rows.length) { + return []; + } + + if (!plSet.show_header) { + return BaseListRowContent.prototype.generate_items_to_draw.apply(this, [wy, wh, row_shift, pixel_shift, row_h]); + } + + const first_item = this.generate_first_item_to_draw(wy, wh, row_shift, pixel_shift, row_h); + return this.generate_all_items_to_draw(wy, wh, first_item); + } + + /** + * Calculates the position of the first visible item to draw in the playlist. + * @param {number} wy - The y-coordinate of the top edge of the visible area. + * @param {number} wh - The height of the viewport. + * @param {number} row_shift - The number of rows to shift the drawing position vertically. + * @param {number} pixel_shift - The number of pixels to shift the items vertically. + * @param {number} row_h - The height of each row in the drawing. + * @returns {PlaylistRow|PlaylistBaseHeader} The first visible item in the playlist based on the provided parameters. + */ + generate_first_item_to_draw(wy, wh, row_shift, pixel_shift, row_h) { // Rewritten + const set_x = PlaylistSetX(); + const start_y = wy + pixel_shift; + let cur_row = 0; + + /** + * Searches sub_items for the first visible item, considering if items are headers with sub-items. + * @param {PlaylistBaseHeader[]|PlaylistRow[]} sub_items - The array of items to search through. + * @param {boolean} isHeader - A flag indicating if the items in the array are header items. + * @returns {?PlaylistBaseHeader|?PlaylistRow} The first visible item or null if none are visible. + */ + const _iterate_level = (sub_items, isHeader) => { + if (!Array.isArray(sub_items) || sub_items.length === 0) { + return null; + } + for (let i = 0, len = sub_items.length; i < len; ++i) { + const item = sub_items[i]; + const itemHeightInRows = isHeader ? Math.round(item.h / row_h) : 1; + + if (cur_row + itemHeightInRows > row_shift) { + item.set_x(set_x); + item.set_y(start_y + (cur_row - row_shift) * row_h); + return item; + } + + cur_row += itemHeightInRows; + + if (isHeader && !item.is_collapsed) { + const result = _iterate_level(item.sub_items, true); + if (result) return result; + } + } + + return null; + }; + + const isHeader = this.sub_items.length > 0 && this.sub_items[0] instanceof PlaylistBaseHeader; + const first_item = _iterate_level(this.sub_items, isHeader); + + if (first_item == null) { + plSet.scrollbar_pos = 0; + } + + // No idea if this debug warning is still needed when we are using the safeguard + // Maybe we need to init the playlist or scrollbar if first_item is null due to a race condition + // Assert(first_item != null, LogicError, 'first_item_to_draw can\'t be null!'); + + return first_item; + } + + /** + * Generates an array of items to draw based on the given parameters and starting item. + * It iterates through the items starting from `first_item` and collects those that fall within the visible area specified by `wy` and `wh`. + * @param {number} wy - The y-coordinate of the top edge of the visible area. + * @param {number} wh - The height of the viewport. + * @param {PlaylistRow|PlaylistBaseHeader} first_item - The starting item from which the drawing of items will begin. + * @returns {Array} The array of items that should be drawn on the visible area. + */ + generate_all_items_to_draw(wy, wh, first_item) { // Rewritten + if (!first_item) return []; + + const items_to_draw = [first_item]; + const cur_x = PlaylistSetX(); + let cur_y = first_item.y + first_item.h; + + /** + * Iterates over a list of sub items starting from `start_item` to collect items to draw, + * considering the current y-coordinate (`cur_y`) and visibility within the viewport. + * If `start_item` is not provided, iteration starts from the beginning of `sub_items`. + * @param {Array|Array} sub_items - The sub-items to iterate over. + * @param {PlaylistRow|PlaylistBaseHeader} start_item - An optional item from which to start the iteration. + * @param {boolean} is_cur_level_header - Indicates if the current level items are headers. + * @returns {boolean} True if `start_item` was used, indicating that items have been added to the drawing queue. + */ + const _iterate_level = (sub_items, start_item, is_cur_level_header) => { + let start_item_used = !start_item; + let leveled_start_item = start_item; + + while (leveled_start_item && leveled_start_item.parent !== sub_items[0].parent) { + leveled_start_item = leveled_start_item.parent; + } + + const start_idx = leveled_start_item ? (leveled_start_item instanceof PlaylistRow ? leveled_start_item.idx_in_header : leveled_start_item.idx) : 0; + + for (let i = start_idx, item; i < sub_items.length; ++i) { + item = sub_items[i]; + if (start_item_used) { + item.set_x(cur_x); + item.set_y(cur_y); + items_to_draw.push(item); + cur_y += item.h; + } else if (item === start_item) { + start_item_used = true; + } + + if (cur_y >= wy + wh) break; + + if (is_cur_level_header && !(item instanceof PlaylistBaseHeader && item.is_collapsed) && + _iterate_level(item.sub_items, start_item_used ? null : start_item, item.sub_items[0] instanceof PlaylistBaseHeader)) { + start_item_used = true; + } + } + + return start_item_used; + }; + + const is_first_level_header = this.sub_items[0] instanceof PlaylistBaseHeader; + _iterate_level(this.sub_items, first_item, is_first_level_header); + + return items_to_draw; + } + + /** + * Updates the size of items based on the given width, taking into account whether the header is shown or not. + * @override + * @param {number} w - The width. + */ + update_items_w_size(w) { + if (!plSet.show_header) { + BaseListRowContent.prototype.update_items_w_size.apply(this, [w]); + return; + } + + for (const item of this.sub_items) { + item.set_w(w); + } + } + + /** + * Calculates the total height of rows in a playlist, taking into account sub-items and whether the header is shown. + * @override + */ + calculate_total_h_in_rows() { // Rewritten + if (!plSet.show_header) { + return BaseListRowContent.prototype.calculate_total_h_in_rows.apply(this); + } + + if (!this.sub_items.length) { + return 0; + } + + // Calculate the height in rows for a single item outside the loop to avoid repetitive work + const single_item_height_in_rows = Math.round(this.sub_items[0].h / pl.geo.row_h); + let total_height_in_rows = single_item_height_in_rows * this.sub_items.length; + + for (const item of this.sub_items) { + if (!item.is_collapsed) { + total_height_in_rows += item.get_sub_items_total_h_in_rows(); + } + } + + return total_height_in_rows; + } + // #endregion +} + + +/** + * A class that provides methods for navigating and determining the visibility of items in a playlist content. + */ +class PlaylistContentNavigation { + /** + * Creates the `PlaylistContentNavigation` instance. + * @param {PlaylistContent} cnt_arg - The content of a playlist. + */ + constructor(cnt_arg) { + /** @private @constant @type {PlaylistContent} */ + this.cnt = cnt_arg; + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Gets the previous item before the header. + * @param {PlaylistBaseHeader} header - The header to search for the previous item before. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The previous item before the given header. + * @private + */ + _get_prev_item_before_header(header) { + if (header === header.parent.sub_items[0]) { + return header.parent instanceof PlaylistBaseHeader ? header.parent : null; + } + + // @ts-ignore + return this._get_last_visible_item(header.parent.sub_items[header.idx - 1]); + } + + /** + * Gets the previous item before the given row. + * @param {PlaylistRow} row - The row to search for the previous item before. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The previous item before the given row. + * @private + */ + _get_prev_item_before_row(row) { + if (row === row.parent.sub_items[0]) { + return row.parent; + } + + return row.parent.sub_items[row.idx_in_header - 1]; + } + + /** + * Gets the next item in the list, or null if there is no next item. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to start searching from. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The next item, or null if there is no next item. + * @private + */ + _get_next_item(item) { + let next_item = item; + while (next_item) { + if (!next_item.parent) { + return null; + } + + if (next_item !== Last(next_item.parent.sub_items)) { + const next_item_idx = next_item instanceof PlaylistRow ? next_item.idx_in_header : next_item.idx; + return next_item.parent.sub_items[next_item_idx + 1]; + } + + next_item = next_item.parent; + } + + return next_item; + } + + /** + * Get the last visible item in the list. + * @param {PlaylistBaseHeader} item - The item to search for the last visible item in. + * @returns {PlaylistBaseHeader|PlaylistRow} The last visible item in the given item. + * @private + */ + _get_last_visible_item(item) { + let last_item = item; + while (!(last_item instanceof PlaylistRow) && !last_item.is_collapsed) { + last_item = Last(last_item.sub_items); + } + + return last_item; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Gets the visible parent of the given item. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to get the parent of. + * @returns {?PlaylistBaseHeader} The visible parent item, or null if the item is not visible. + */ + get_visible_parent(item) { + if (!item.parent || !(item.parent instanceof PlaylistBaseHeader)) { + return null; + } + + let cur_item = item; + do { + cur_item = cur_item.parent; + } while (cur_item.parent && cur_item.parent.is_collapsed); + + return cur_item; + } + + /** + * Gets the navigable neighbor of the given item in the given direction. + * See {@link PlaylistContentNavigation.is_item_navigable}. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to start from. + * @param {number} direction - The direction to search in. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The first navigable neighbor, or null if no neighbor is found. + */ + get_navigable_neighbor(item, direction) { + let neighbor_item = item; + do { + neighbor_item = this.get_visible_neighbor(neighbor_item, direction); + } while (neighbor_item && !this.is_item_navigable(neighbor_item)); + + return neighbor_item; + } + + /** + * Gets the visible neighbor of the given item in the given direction. + * See {@link PlaylistContentNavigation.is_item_visible}. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to get the neighbor of. + * @param {number} direction - The direction to get the neighbor in. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The visible neighbor of the given item in the given direction. + */ + get_visible_neighbor(item, direction) { + return direction > 0 ? this.get_next_visible_item(item) : this.get_prev_visible_item(item); + } + + /** + * Gets the previous visible item of the given item. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to get the previous visible item of. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The previous visible item in the list. + */ + get_prev_visible_item(item) { + if (!plSet.show_header) { + if (item === this.cnt.rows[0]) { + return null; + } + + return this.cnt.rows[item.idx - 1]; + } + + if (item instanceof PlaylistBaseHeader) { + return this._get_prev_item_before_header(item); + } + + return this._get_prev_item_before_row(item); + } + + /** + * Gets the next visible item in the list. + * @param {PlaylistRow|PlaylistBaseHeader} item - The current item. + * @returns {PlaylistRow|PlaylistBaseHeader|null} The next visible item, or null if there is none. + */ + get_next_visible_item(item) { + if (!plSet.show_header) { + if (item === Last(this.cnt.rows)) { + return null; + } + + return this.cnt.rows[item.idx + 1]; + } + + if (item instanceof PlaylistBaseHeader && !item.is_collapsed) { + return item.sub_items[0]; + } + + return this._get_next_item(item); + } + + /** + * Checks if the given item is visible. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to check. + * @returns {boolean} True if item is not hidden by collapsed parent. + */ + is_item_visible(item) { + if (item.parent && item.parent instanceof PlaylistBaseHeader) { + return !item.parent.is_collapsed; + } + + return true; + } + + /** + * Checks if the given item is navigable. + * @param {PlaylistRow|PlaylistBaseHeader} item - The item to check. + * @returns {boolean} True if item can be selected by arrow keys. + */ + is_item_navigable(item) { + return item instanceof PlaylistRow ? true : item.is_collapsed; + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-header.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-header.js new file mode 100644 index 00000000..ffcbfc98 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-header.js @@ -0,0 +1,1589 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Header * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +///////////////////// +// * BASE HEADER * // +///////////////////// +/** + * A class that represents the base header in the playlist and provides methods + * for initializing sub-items and drawing the headers. + * @augments {BaseListItem} + */ +class PlaylistBaseHeader extends BaseListItem { + /** + * Creates the `PlaylistBaseHeader` instance. + * Represents a header element in a parent container. + * @abstract + * @param {BaseListContent|PlaylistBaseHeader} parent - The parent header. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} idx - The index. + */ + constructor(parent, x, y, w, h, idx) { + super(x, y, w, h); + + /** @public @constant @type {PlaylistBaseHeader} */ + this.parent = parent; + /** @public @constant @type {number} */ + this.idx = idx; + /** @public @type {boolean} */ + this.is_collapsed = false; + /** @public @type {boolean} Currently only updated at draw time */ + this.hasSelection = false; + /** @private @type {number} */ + this.row_count = 0; + /** @public @type {Array|Array} */ + this.sub_items = []; + + /** @public @type {PlaylistMetaHandler} */ + this.meta_handler = new PlaylistMetaHandler(); + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the header. + * @abstract + * @override + */ + draw(gr) { + // Need this useless method to suppress warning =( + throw new LogicError('draw not implemented'); + } + + /** + * Initializes the items in the header. + * @abstract + * @param {Array|Array} items - The items to initialize the contents with. + * @returns {number} The number of processed items. + */ + initialize_items(items) { + throw new LogicError('initialize_contents not implemented'); + } + + /** + * Sets the width of a ListItem and recursively sets the width of its sub-items. + * @override + * @param {number} w - The width. + */ + set_w(w) { + BaseListItem.prototype.set_w.apply(this, [w]); + + for (const item of this.sub_items) { + item.set_w(w); + } + } + + /** + * Gets the first row object from a list of sub-items. + * @returns {PlaylistRow|null} The first row, or null if there are no rows. + */ + get_first_row() { + let item = this.sub_items[0]; + + while (item) { + if (item instanceof PlaylistRow) { + return item; + } + item = (item.sub_items && item.sub_items.length > 0) ? item.sub_items[0] : null; + } + + return null; + } + + /** + * Gets an array of row indexes by recursively iterating through sub_items. + * @returns {number[]} An array of row indexes. + */ + get_row_indexes() { // Rewritten + const row_indexes = []; + const stack = this.sub_items.slice(); + + while (stack.length > 0) { + const item = stack.pop(); + if (item instanceof PlaylistRow) { + row_indexes.push(item.idx); + } else if (item.sub_items) { + for (let i = item.sub_items.length - 1; i >= 0; i--) { + stack.push(item.sub_items[i]); + } + } + } + + return row_indexes; + } + + /** + * Calculates the total height of sub-items in rows, taking into account any nested sub-items. + * @returns {number} The total height in rows. + */ + get_sub_items_total_h_in_rows() { + if (!this.sub_items.length) { + return 0; + } + + if (this.sub_items[0] instanceof PlaylistRow) { + return this.sub_items.length; + } + + let h_in_rows = Math.round(this.sub_items[0].h / pl.geo.row_h) * this.sub_items.length; + for (const item of this.sub_items) { + if (!item.is_collapsed) { + h_in_rows += item.get_sub_items_total_h_in_rows(); + } + } + + return h_in_rows; + } + + /** + * Calculates the total duration in seconds of a list of sub-items, recursively if the sub-items are not of type PlaylistRow. + * @returns {number} A float number. + */ + get_duration() { + let duration_in_seconds = 0; + + if (this.sub_items[0] instanceof PlaylistRow) { + for (const item of this.sub_items) { + duration_in_seconds += item.metadb.Length; + } + } + else { + for (const item of this.sub_items) { + duration_in_seconds += item.get_duration(); + } + } + + return duration_in_seconds; + } + + /** + * Checks if any of the sub_items have been selected, taking into account whether the first sub_item is a header or not. + * @returns {boolean} True or false. + */ + has_selected_items() { + // const is_function = typeof this.sub_items[0].has_selected_items === 'function'; + const isHeader = this.sub_items[0] instanceof PlaylistBaseHeader; + return this.sub_items.some(item => isHeader ? item.has_selected_items() : item.is_selected()); + } + + /** + * Checks if all sub-items are completely selected, taking into account whether the first sub-item is a header or not. + * @returns {boolean} True or false. + */ + is_completely_selected() { + const isHeader = this.sub_items[0] instanceof PlaylistBaseHeader; + return this.sub_items.every(item => isHeader ? item.is_completely_selected() : item.is_selected()); + } + + /** + * Checks if any of the sub_items are currently playing. + * @returns {boolean} True or false. + */ + is_playing() { + const is_function = typeof this.sub_items[0].is_playing === 'function'; + return this.sub_items.some(item => is_function ? item.is_playing() : item.is_playing); + } + + /** + * Checks if any of the sub_items are focused, taking into account whether the is_focused property is a function or a boolean value. + * @returns {boolean} True or false. + */ + is_focused() { + const is_function = typeof this.sub_items[0].is_focused === 'function'; + return this.sub_items.some(item => is_function ? item.is_focused() : item.is_focused); + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Handles mouse double click events on the playlist header. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_lbtn_dblclk(x, y, m) { + plman.ExecutePlaylistDefaultAction(plman.ActivePlaylist, this.get_first_row().idx); + } + // #endregion +} + + +//////////////// +// * HEADER * // +//////////////// +/** + * A class that creates and draws the playlist header. + * @augments {PlaylistBaseHeader} + */ +class PlaylistHeader extends PlaylistBaseHeader { + /** + * Creates the `PlaylistHeader` instance. + * @param {BaseListContent|PlaylistBaseHeader} parent - The parent object or container that this object belongs to. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} idx - The index. + */ + constructor(parent, x, y, w, h, idx) { + super(parent, x, y, w, h, idx); + + /** @private @type {FbMetadbHandle} */ + this.metadb = undefined; + /** @private @type {boolean} Last value of this.is_playing() updated each draw cycle. */ + this.was_playing = undefined; + + /** @private @constant @type {number} */ + this.art_max_size = this.h - SCALE(16); + /** @private @type {?GdiBitmap} undefined > Not Loaded; null > Loaded & Not Found; !isNil > Loaded & Found. */ + this.art = undefined; + /** @public @type {GdiBitmap} */ + this.header_image = undefined; + + /** @private @type {array} */ + this.hyperlinks = {}; + /** @public @type {boolean} */ + this.hyperlinks_initialized = false; + + /** @private @type {number} */ + this.rating_left_pad = 0; + /** @private @type {number} */ + this.rating_right_pad = 10; + /** @private @type {PlaylistRating} */ + this.rating = undefined; + + /** @public @type {PlaylistGroupingHandler} */ + this.grouping_handler = PlaylistHeader.grouping_handler; + + this.initialize_rating(); + } + + // * STATIC METHODS * // + // #region STATIC METHODS + /** + * Gets the image cache, creating it if it doesn't exist. + * @returns {PlaylistImageCache} The singleton instance of the PlaylistImageCache. + */ + static get img_cache() { + if (!this._imgCache) { + this._imgCache = new PlaylistImageCache(200); + } + return this._imgCache; + } + + /** + * Gets the grouping handler, creating it if it doesn't exist. + * @returns {PlaylistGroupingHandler} The singleton instance of the PlaylistGroupingHandler. + */ + static get grouping_handler() { + if (!this._groupingHandler) { + this._groupingHandler = new PlaylistGroupingHandler(); + } + return this._groupingHandler; + } + + /** + * Prepares the initialization data for the playlist header. + * @param {Array} rows_to_process - The rows to process. + * @param {FbMetadbHandleList} rows_metadb - The metadb of the rows to process. + * @returns {Array} An array of two elements, has the following format [Array<[row,row_data]>, disc_header_prepared_data]. + */ + static prepare_initialization_data(rows_to_process, rows_metadb) { + let query = PlaylistHeader.grouping_handler.get_query(); + if (plSet.show_disc_header && query && PlaylistHeader.grouping_handler.show_disc()) { + query = query.replace(/%discnumber%/, '').replace(/%totaldiscs%/, '').replace(/%subtitle%/, ''); + } + + const tfo = fb.TitleFormat(query || ''); // Workaround a bug, because of which '' is sometimes treated as null :\ + const rows_data = tfo.EvalWithMetadbs(rows_metadb); + + const prepared_disc_data = plSet.show_disc_header ? PlaylistDiscHeader.prepare_initialization_data(rows_to_process, rows_metadb) : []; + + return [Zip(rows_to_process, rows_data), prepared_disc_data]; + } + + /** + * Creates the playlist header for each item. + * @param {PlaylistContent} parent - The parent element. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {Array} prepared_rows - The prepared rows, has the following format [Array<[row,row_data]>, disc_header_prepared_data]. + * @returns {Array} An array of headers. + */ + static create_headers(parent, x, y, w, h, prepared_rows) { + const prepared_header_rows = prepared_rows[0]; + + let header_idx = 0; + const headers = []; + while (prepared_header_rows.length) { + const header = new PlaylistHeader(parent, x, y, w, h, header_idx); + const processed_rows_count = header.initialize_items(prepared_rows); + + TrimArray(prepared_header_rows, processed_rows_count, true); + + headers.push(header); + ++header_idx; + } + + return headers; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the playlist header either in normal or compact layout. + * @override + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} top - The y-coordinate of the top edge of the header area. + * @param {number} bottom - The y-coordinate of the bottom edge of the header. + */ + draw(gr, top, bottom) { + if (this.w <= 0 || this.h <= 0) return; + // drawProfiler = fb.CreateProfiler('PlaylistHeader.draw items:' + this.sub_items.length); + if (plSet.use_compact_header) { + this.draw_compact_header(gr); + } + else { + this.draw_normal_header(gr, top, bottom); + } + // drawProfiler.Print(); + } + + /** + * Draws the playlist header in normal layout. + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} top - The y-coordinate of the top edge of the header area. + * @param {number} bottom - The y-coordinate of the bottom edge of the header. + */ + draw_normal_header(gr, top, bottom) { + let clipImg = gdi.CreateImage(this.w, this.h); + const grClip = clipImg.GetGraphics(); + const scrollbar = plSet.show_scrollbar && pl.playlist.is_scrollbar_available; + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + + if (!this.hyperlinks_initialized) { + this.initialize_hyperlinks(gr); + } + let cache_header = true; // Caching is a lot faster, but need to handle artwork loading + if (this.was_playing !== this.is_playing()) { + this.was_playing = this.is_playing(); + cache_header = false; + this.clearCachedHeaderImg(); + } + const hasSelection = this.has_selected_items(); + if (this.hasSelection !== hasSelection) { + this.hasSelection = hasSelection; + cache_header = false; + this.clearCachedHeaderImg(); + } + if (!cache_header || !this.header_image || this.clearCachedHeaderImg) { + let artist_color = pl.col.header_artist_normal; + let album_color = pl.col.header_album_normal; + let info_color = pl.col.header_info_normal; + let date_color = pl.col.header_date_normal; + let line_color = pl.col.header_line_normal; + let artist_font = pl.font.artist_normal; + const date_font = pl.font.date; + const updatedNowpBg = pl.col.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing + + if (this.is_playing() && updatedNowpBg) { + artist_color = pl.col.header_artist_playing; + album_color = pl.col.header_album_playing; + info_color = pl.col.header_info_playing; + date_color = pl.col.header_date_playing; + line_color = pl.col.header_line_playing; + artist_font = pl.font.artist_playing; + + if (grCol.lightBg && grSet.theme === 'black') { + artist_color = RGB(0, 0, 0); + album_color = RGB(20, 20, 20); + info_color = RGB(20, 20, 20); + date_color = RGB(20, 20, 20); + } else if (grCol.lightBg && grSet.theme === 'white' && grSet.layout !== 'default') { + artist_color = RGB(60, 60, 60); + album_color = RGB(60, 60, 60); + info_color = RGB(60, 60, 60); + date_color = RGB(60, 60, 60); + line_color = RGB(100, 100, 100); + } + } + + if (!grSet.styleBlend) grClip.FillSolidRect(0, 0, this.w, this.h, pl.col.bg); // Solid background for ClearTypeGridFit text rendering + + // if (this.hasSelection && pref.theme.startsWith('custom')) { + // grClip.FillSolidRect(0, 0, this.w, this.h, pl.col.row_selection_bg); + // } + + if (this.is_playing() && updatedNowpBg) { + const p = SCALE(6); // From art below + + if (grSet.theme === 'white' && !grSet.styleBlackAndWhite && !grSet.styleBlackAndWhite2 && grSet.layout === 'default' || (grSet.theme === 'reborn' || grSet.theme === 'random') && grm.ui.noAlbumArtStub) { + grClip.FillSolidRect(0, p, SCALE(8), this.h - p * 2, pl.col.header_nowplaying_bg); + } + else { + grClip.FillSolidRect(0, 0, scrollbar ? this.w - SCALE(12) : this.w, this.h * 2, pl.col.header_nowplaying_bg); + } + if (grSet.theme === 'white' && (grSet.styleBlackAndWhite || grSet.styleBlackAndWhite2) || !['white', 'black', 'cream'].includes(grSet.theme)) { + grClip.FillSolidRect(0, 0, SCALE(8), this.h, pl.col.header_sideMarker); + } + } + + // * Need to apply text rendering AntiAliasGridFit when using style Blend or when using custom theme fonts with larger font sizes + grClip.SetTextRenderingHint(grSet.styleBlend || grSet.customThemeFonts && headerFontSize > 18 ? TextRenderingHint.AntiAliasGridFit : TextRenderingHint.ClearTypeGridFit); + + if (this.is_collapsed && this.is_focused() || this.is_completely_selected() && plSet.show_header && plSet.auto_collapse) { + grClip.DrawRect(-1, 0, this.w + 1, this.h - 1, 1, line_color); + grClip.FillSolidRect(0, 0, SCALE(8), this.h, pl.col.header_sideMarker); + } + + //************************************************************// + + let left_pad = SCALE(10) + (this.art === null && plSet.auto_album_art || !plSet.show_album_art ? SCALE(10) : 0); + + // * ART BOX * // + if (plSet.show_album_art) { + if (!this.is_art_loaded()) { + const cached_art = PlaylistHeader.img_cache.get_image_for_meta(this.metadb); + if (cached_art) { + this.assign_art(cached_art); + } + } + + if (this.art !== null || !plSet.auto_album_art) { + const p = SCALE(6); + const spacing = SCALE(2); + + const art_box_size = this.art_max_size + spacing * 2; + const art_box_x = p * 3; + let art_box_y = p; + let art_box_w = art_box_size; + let art_box_h = art_box_size; + + if (this.art) { + const art_x = art_box_x + spacing; + let art_y = art_box_y + spacing; + const art_h = this.art.Height; + const art_w = this.art.Width; + if (art_h > art_w) { + art_box_w = art_w + spacing * 2; + } + else { + art_box_h = art_h + spacing * 2; + art_y += Math.round((this.art_max_size - art_h) / 2); + art_box_y = art_y - spacing; + } + grClip.DrawImage(this.art, art_x, art_y, art_w, art_h, 0, 0, art_w, art_h, 0, 220); + } + else if (!this.is_art_loaded()) { + grClip.DrawString('LOADING', pl.font.cover, this.is_playing() ? artist_color : pl.col.row_title_normal, art_box_x, art_box_y, art_box_size, art_box_size, Stringformat.align_center); + cache_header = false; // Don't cache until artwork is loaded + } + else { // null + const is_radio = this.metadb.RawPath.startsWith('http') || this.metadb.Path.startsWith('spotify'); + grClip.DrawString(grm.ui.isStreaming && is_radio ? 'LIVE\n ON AIR' : 'NO COVER', pl.font.cover, this.is_playing() ? artist_color : pl.col.row_title_normal, art_box_x, art_box_y, art_box_size, art_box_size, Stringformat.align_center); + } + + // * Used for Library thumbnail size + pl.thumbnail_size = art_box_w - 4; + grClip.DrawRect(art_box_x, art_box_y, art_box_w - 1, art_box_h - 1, 1, line_color); + left_pad += art_box_x + art_box_w; + + let i_ = 0; + let offset_ = 0; + while (this.hyperlinks[`artist${i_}`]) { + if (i_ === 0) { + offset_ = this.hyperlinks.artist0.x - left_pad; + if (!offset_) break; + } + this.hyperlinks[`artist${i_}`].x -= offset_; + i_++; + } + + this.hyperlinks.album && this.hyperlinks.album.setXOffset(left_pad); + + let i = 0; + let offset = 0; + while (this.hyperlinks[`genre${i}`]) { + if (i === 0) { + offset = this.hyperlinks.genre0.x - left_pad; + if (!offset) break; + } + this.hyperlinks[`genre${i}`].x -= offset; + i++; + } + } + else if (this.art === null && plSet.auto_album_art) { + this.hyperlinks.artist0 && this.hyperlinks.artist0.setXOffset(left_pad); + this.hyperlinks.album && this.hyperlinks.album.setXOffset(left_pad); + this.hyperlinks.genre0 && this.hyperlinks.genre0.setXOffset(left_pad); + } + } + + //************************************************************// + + const is_radio = this.metadb.RawPath.startsWith('http') || this.metadb.Path.startsWith('spotify'); + + // * Part1: Artist + // * Part2: Album + line + Date OR line + // * Part3: Info OR album + const part1_cur_x = left_pad; + let part2_cur_x = left_pad; + let part3_cur_x = left_pad; + + const part_h = !plSet.show_group_info ? this.h / 2 : this.h / 3; + let part2_right_pad = 0; + + // * DATE * // + if (this.grouping_handler.show_date()) { + const date_query = grSet.showPlaylistFullDate ? grTF.date : grTF.year; + const date_text = $(date_query, this.metadb); + if (date_text && date_text !== '0000') { + const date_w = Math.ceil(gr.MeasureString(date_text, date_font, 0, 0, 0, 0).Width + 5); + const date_x = this.w - date_w; + + if (!this.hyperlinks.date && date_x > left_pad) { + const date_y = 0; + const date_h = this.h - 4; + grClip.DrawString(date_text, date_font, date_color, date_x, date_y, date_w, date_h, Stringformat.v_align_center); + } else { + this.hyperlinks.date.draw(grClip, date_color); + } + + part2_right_pad += this.w - date_x; + } + } + + // * ARTIST * // + if (this.grouping_handler.get_title_query()) { + const artist_text_format = Stringformat.v_align_far | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + let artist_text = $(this.grouping_handler.get_title_query(), this.metadb); + + if (!artist_text && is_radio) { + artist_text = 'Radio Stream'; + } + if (artist_text) { + let artist_x = part1_cur_x; + let artist_w = this.w - artist_x * 2; + let artist_h = part_h; + if (!plSet.show_group_info) { + artist_w -= part2_right_pad + 5; + artist_h -= 5; + } + + if (is_radio || !this.hyperlinks.artist0) { + grClip.DrawString(artist_text, artist_font, artist_color, artist_x, 0, artist_w, artist_h, artist_text_format); + } + else { + let i = 0; + let artist_hyperlink; + while (this.hyperlinks[`artist${i}`]) { + if (i > 0) { + grClip.DrawString(' \u2022 ', artist_font, artist_color, artist_hyperlink.x + artist_hyperlink.getWidth(), artist_h * 0.25, SCALE(20), artist_h); + } + artist_hyperlink = this.hyperlinks[`artist${i}`]; + artist_hyperlink.draw(grClip, artist_color); + artist_x = artist_hyperlink.x; + artist_w = artist_hyperlink.getWidth(); + i++; + } + } + // part1_cur_x += artist_w; + } + } + + // * ALBUM * // + if (this.grouping_handler.get_sub_title_query()) { + const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); + + if (album_text) { + const album_h = part_h; + let album_y = part_h; + let album_x; + if (plSet.show_group_info) { + album_x = part2_cur_x; + } + else { + album_y += 5; + album_x = part3_cur_x; + } + const album_w = this.w - album_x - (part2_right_pad + 5); + + let album_text_format = Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + if (plSet.show_group_info) { + album_text_format |= Stringformat.v_align_center; + } + + if (!this.hyperlinks.album) { + grClip.DrawString(album_text, pl.font.album, album_color, album_x, album_y, album_w, album_h, album_text_format); + } else { + this.hyperlinks.album.draw(grClip, album_color); + } + + const album_text_w = Math.ceil( + /** @type {!number} */ + gr.MeasureString(album_text, pl.font.album, 0, 0, 0, 0).Width + ); + if (plSet.show_group_info) { + part2_cur_x += album_text_w; + } + else { + part3_cur_x += album_text_w; + } + } + } + + // * INFO * // + if (plSet.show_group_info) { + const info_x = part3_cur_x; + const info_y = 2 * part_h - (RES._4K ? 5 : 0); + const info_h = part_h; //row_h; + const info_w = this.w - info_x; + const info_text_format = Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + + // * Genres + let genre_text_w = 0; + const extraGenreSpacing = 0; // Don't use SCALE() due to font differences + let genreX = info_x; + if (!is_radio && this.grouping_handler.get_query_name() !== 'artist') { + if (!this.hyperlinks.genre0) { + const genre_text = $('[%genre%]', this.metadb).replace(/, /g, ' \u2022 '); + genre_text_w = Math.ceil(gr.MeasureString(genre_text, pl.font.info, 0, 0, 0, 0).Width + extraGenreSpacing); + grClip.DrawString(genre_text, pl.font.info, info_color, genreX, info_y, info_w, info_h, info_text_format); + } else { + let i = 0; + let genre_hyperlink; + while (this.hyperlinks[`genre${i}`]) { + if (i > 0) { + grClip.DrawString(' \u2022 ', pl.font.info, info_color, genre_hyperlink.x + genre_hyperlink.getWidth() + SCALE(2), info_y, SCALE(20), info_h); + } + genre_hyperlink = this.hyperlinks[`genre${i}`]; + genre_hyperlink.draw(grClip, info_color); + genreX = genre_hyperlink.x; + genre_text_w = genre_hyperlink.getWidth(); + i++; + } + } + } + + const info_text = this.getGroupInfoString(is_radio, genre_text_w > 0); + const info_text_w = Math.ceil(gr.MeasureString(info_text, pl.font.info, 0, 0, 0, 0).Width); // TODO: Mordred - should only call MeasureString once + grClip.DrawString(info_text, pl.font.info, info_color, genreX + (genre_text_w || 0), info_y + 1, info_w - (genreX - info_x) - (genre_text_w || 0) - SCALE(20), info_h, info_text_format); + + // * Record labels + if (!this.hyperlinks.label0) { + const label_string = $('$if2(%label%,[%publisher%])', this.metadb).replace(/, /g, ' \u2022 '); + const label_w = Math.ceil(gr.MeasureString(label_string, pl.font.info, 0, 0, 0, 0).Width + 10); + if (info_w > label_w + info_text_w) { + grClip.DrawString(label_string, pl.font.info, info_color, this.w - label_w - 10, info_y, label_w, info_h, Stringformat.h_align_far); + } + } else { + let i = 0; + let drawCount = 0; + let lastLabel; + while (this.hyperlinks[`label${i}`]) { + const label_hyperlink = this.hyperlinks[`label${i}`]; + if (label_hyperlink.x > genreX + genre_text_w + info_text_w) { + if (drawCount > 0) { + grClip.DrawString(' \u2022', pl.font.info, info_color, lastLabel.x + lastLabel.getWidth(), info_y, SCALE(20), info_h); + } + label_hyperlink.draw(grClip, info_color); + drawCount++; + } + lastLabel = label_hyperlink; // We want to draw bullet AFTER the previous label + i++; + } + } + + // * Info line + const info_text_h = Math.ceil(gr.MeasureString(info_text, pl.font.info, 0, 0, 0, 0).Height + 5); + const line_x1 = left_pad; + const line_x2 = this.w - SCALE(20); + const line_y = info_y + info_text_h; + if (line_x2 - line_x1 > 0) { + grClip.DrawLine(line_x1, line_y, line_x2, line_y, 1, line_color); + } + } + + // * PART 2 ALBUM LINE + { + let line_x1 = part2_cur_x; + if (line_x1 !== left_pad) { + line_x1 += RES._4K ? 20 : 9; + } + + const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); + const album_height = gr.MeasureString(album_text, pl.font.album, 0, 0, 0, 0).Height; + const date_query = grSet.showPlaylistFullDate ? grTF.date : grTF.year; + const date_text = $(date_query, this.metadb); + const date_height = gr.MeasureString(date_text, pl.font.date, 0, 0, 0, 0).Height; + const line_x2 = this.w - part2_right_pad - (date_text ? (RES._4K ? 58 : 25) : SCALE(20)); + + const yCorrSize = { + 22: { '4K': -4, 'HD': -1 }, + 20: { '4K': -5, 'HD': 0 }, + 18: { '4K': -5, 'HD': -1 }, + 17: { '4K': -4, 'HD': 1 }, + 16: { '4K': -3, 'HD': -1 }, + 15: { '4K': -4, 'HD': 0 }, + 14: { '4K': -4, 'HD': 0 }, + 13: { '4K': -3, 'HD': 0 }, + 12: { '4K': -5, 'HD': 0 }, + 10: { '4K': -2, 'HD': 0 } + }; + const yCorr = yCorrSize[headerFontSize] ? yCorrSize[headerFontSize][RES._4K ? '4K' : 'HD'] : 0; + const line_y = + !plSet.show_group_info ? Math.round(this.h / 2) : + grSet.customThemeFonts ? Math.floor(this.h / 2 + (date_height - album_height)) : + Math.round(this.h / 2) + SCALE(RES._4K ? 7 : 6) + yCorr; + + if (line_x2 - line_x1 > 0) { + grClip.DrawLine(line_x1, line_y, line_x2, line_y, 1, line_color); + } + } + + clipImg.ReleaseGraphics(grClip); + if (cache_header) { + this.header_image = clipImg; + } + } + + let y = this.y; + let h = this.h; + let srcY = 0; + if (this.y < top) { + y = top; + h = this.h - (top - this.y); + srcY = this.h - h; + } else if (this.y + this.h > bottom) { + h = bottom - this.y; + } + gr.DrawImage(cache_header ? this.header_image : clipImg, this.x, y, this.w, h, 0, srcY, this.w, h); + if (this.is_completely_selected() && (grSet.theme === 'white' || grSet.theme === 'black')) { + gr.SetSmoothingMode(SmoothingMode.None); + gr.FillSolidRect(this.x, y + SCALE(6), 0, h, grCol.primary); + gr.SetSmoothingMode(SmoothingMode.HighQuality); + } + clipImg = null; + } + + /** + * Draws the playlist header in compact layout. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + draw_compact_header(gr) { + let artist_color = pl.col.header_artist_normal; + let album_color = pl.col.header_album_normal; + let date_color = pl.col.header_date_normal; + let line_color = pl.col.header_line_normal; + let artist_font = pl.font.artist_normal_compact; + const date_font = pl.font.date_compact; + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const updatedNowpBg = pl.col.header_nowplaying_bg !== ''; // * Wait until nowplaying bg has a new color to prevent flashing + const scrollbar = plSet.show_scrollbar && pl.playlist.is_scrollbar_available; + + if (this.is_playing() && updatedNowpBg) { + artist_color = pl.col.header_artist_playing; + album_color = pl.col.header_album_playing; + date_color = pl.col.header_date_playing; + line_color = pl.col.header_line_playing; + artist_font = pl.font.artist_playing_compact; + + if (grCol.lightBg && (grSet.theme === 'white' || grSet.theme === 'black')) { + artist_color = RGB(0, 0, 0); + album_color = RGB(20, 20, 20); + date_color = RGB(20, 20, 20); + } + else if (!grCol.lightBg && (grSet.theme === 'white' || grSet.theme === 'black')) { + artist_color = RGB(240, 240, 240); + album_color = RGB(220, 220, 220); + date_color = RGB(220, 220, 220); + } + } + + const clipImg = gdi.CreateImage(this.w, this.h); + const grClip = clipImg.GetGraphics(); + + //---> + if (!grSet.styleBlend) grClip.FillSolidRect(0, 0, this.w, this.h, pl.col.bg); // Solid background for ClearTypeGridFit text rendering + // if (this.has_selected_items() && pref.theme.startsWith('custom')) { + // grClip.FillSolidRect(0, 0, this.w, this.h, pl.col.row_selection_bg); + // } + + if (this.is_playing() && updatedNowpBg) { + grClip.FillSolidRect(0, 0, scrollbar ? this.w - SCALE(12) : this.w, this.h, pl.col.header_nowplaying_bg); + grClip.FillSolidRect(0, 0, ['white', 'black', 'cream'].includes(grSet.theme) ? 0 : SCALE(8), this.h, pl.col.header_sideMarker); + } + + // * Need to apply text rendering AntiAliasGridFit when using style Blend or when using custom theme fonts with larger font sizes + grClip.SetTextRenderingHint(grSet.styleBlend || grSet.customThemeFonts && headerFontSize > 18 ? TextRenderingHint.AntiAliasGridFit : TextRenderingHint.ClearTypeGridFit); + + if (this.is_collapsed && this.is_focused()) { + grClip.DrawRect(-1, 0, this.w + 1, this.h - 1, 1, line_color); + } + + //************************************************************// + + const is_radio = this.metadb.RawPath.startsWith('http'); + + const left_pad = SCALE(20); + let right_pad = 0; + let cur_x = left_pad; + + // * Date + if (this.grouping_handler.show_date()) { + const date_query = grSet.showPlaylistFullDate ? grTF.date : grTF.year; + const date_text = $(date_query, this.metadb); + if (date_text) { + const date_w = Math.ceil(gr.MeasureString(date_text, date_font, 0, 0, 0, 0).Width + 5); + const date_x = this.w - date_w - SCALE(12); + const date_y = 0; + const date_h = this.h; + + if (date_x > left_pad) { + grClip.DrawString(date_text, date_font, date_color, date_x, date_y, date_w, date_h, Stringformat.v_align_center); + } + + right_pad += this.w - date_x; + } + } + + // * Artist + if (this.grouping_handler.get_title_query()) { + let artist_text = $(this.grouping_handler.get_title_query(), this.metadb); + if (!artist_text) { + artist_text = is_radio ? 'Radio Stream' : '?'; + } + + if (artist_text) { + const artist_x = cur_x; + const artist_w = this.w - artist_x - (right_pad + 5); + const artist_h = this.h; + + const artist_text_format = Stringformat.v_align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + grClip.DrawString(artist_text, artist_font, artist_color, artist_x, 0, artist_w, artist_h, artist_text_format); + + cur_x += Math.ceil( + /** @type {!number} */ + gr.MeasureString(artist_text, artist_font, 0, 0, 0, 0).Width + ); + } + } + + // * Album + if (this.grouping_handler.get_sub_title_query()) { + // let album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); + let album_text = plSet.show_disc_header ? $('[%album%]', this.metadb) : $(this.grouping_handler.get_sub_title_query(), this.metadb); + + if (album_text) { + album_text = ` - ${album_text}`; + + const album_h = this.h; + const album_x = cur_x; + const album_w = this.w - album_x - (right_pad + SCALE(40)); + + const album_text_format = Stringformat.v_align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + grClip.DrawString(album_text, pl.font.album, album_color, album_x, 0, album_w, album_h, album_text_format); + + // cur_x += gr.MeasureString(album_text, pl.font.album, 0, 0, 0, 0).Width; + } + } + + clipImg.ReleaseGraphics(grClip); + gr.DrawImage(clipImg, this.x, this.y, this.w, this.h, 0, 0, this.w, this.h, 0, 255); + + // * Callbacks for headerTooltip + this.artist_w_compact = this.w - cur_x - (right_pad + 5); + this.album_w_compact = this.w - cur_x - (right_pad + SCALE(40)); + this.artist_text_w_compact = gr.MeasureString($(this.grouping_handler.get_title_query(), this.metadb), artist_font, 0, 0, 0, 0).Width; + this.album_text_w_compact = gr.MeasureString($(this.grouping_handler.get_sub_title_query(), this.metadb), pl.font.album, 0, 0, 0, 0).Width; + } + + /** + * Sets the x-coordinate for the ListItem object. + * @param {number} x - The x-coordinate. + */ + set_x(x) { + BaseListItem.prototype.set_x.apply(this, [x]); + } + + /** + * Sets the y-coordinate for the ListItem object. + * @param {number} y - The y-coordinate. + */ + set_y(y) { + BaseListItem.prototype.set_y.apply(this, [y]); + + for (const h in this.hyperlinks) { + this.hyperlinks[h].setY(y); + } + } + + /** + * Sets the width of a list item and its sub-items, as well as the container width of any hyperlinks within the item. + * @param {number} w - The width. + */ + set_w(w) { + this.reset_hyperlinks(); // Update hyperlinks container width when this.list_w changes, i.e when auto-hide scrollbar visibility state changes + BaseListItem.prototype.set_w.apply(this, [w]); + + for (const item of this.sub_items) { + item.set_w(w); + } + + for (const h in this.hyperlinks) { + this.hyperlinks[h].setContainerWidth(w); + } + + this.initialize_rating(); + } + + /** + * Initializes items by creating sub headers and assigning properties to each item. + * @override + * @param {Array} rows_with_data - The rows with data. + * @returns {number} The length of the `owned_rows` array. + */ + initialize_items(rows_with_data) { // Rewritten + if (!rows_with_data[0].length) { + this.sub_items = []; + return 0; + } + + const rows_with_header_data = rows_with_data[0]; + const first_data = rows_with_header_data[0][1]; + const owned_rows = []; + let idx = 0; + + for (; idx < rows_with_header_data.length; idx++) { + if (first_data !== rows_with_header_data[idx][1]) { + break; + } + const item = rows_with_header_data[idx][0]; + item.idx_in_header = idx; + item.parent = this; + if (plSet.show_header) { + item.is_odd = !(idx & 1); + } + owned_rows.push(item); + } + + this.metadb = owned_rows[0].metadb; + + const show_disc_headers = plSet.show_disc_header && this.grouping_handler.show_disc(); + const rows_with_disc_header_data = rows_with_data[1] || []; + const sub_headers = show_disc_headers ? this.create_disc_headers(rows_with_disc_header_data, idx) : []; + this.sub_items = sub_headers.length ? sub_headers : owned_rows; + + return idx; + } + + /** + * Initializes hyperlinks for various metadata fields such as date, artist, album, record labels, and genres. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + initialize_hyperlinks(gr) { + const date_font = pl.font.date; + const artist_font = pl.font.artist_normal; + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const art_box_x = 3 * SCALE(6); + const spacing = SCALE(2); + const art_box_size = this.art_max_size + spacing * 2; + const left_pad = SCALE(10) + (this.art !== null && plSet.show_album_art && !plSet.auto_album_art ? art_box_x + art_box_size : 0); + const right_edge = SCALE(20); + const part_h = this.h / 3; + const separatorWidth = gr.MeasureString(' \u2020', pl.font.info, 0, 0, 0, 0).Width; + const bulletWidth = Math.ceil(gr.MeasureString('\u2020', pl.font.info, 0, 0, 0, 0).Width); + const spaceWidth = Math.ceil(separatorWidth - bulletWidth) + SCALE(1); + + // * Date + const date_query = grSet.showPlaylistFullDate ? grTF.date : grTF.year; + const date_text = $(date_query, this.metadb); + if (date_text) { + const date_w = Math.ceil(gr.MeasureString(date_text, date_font, 0, 0, 0, 0).Width + 5); + const date_x = -date_w - right_edge + (RES._4K ? 5 : 7); + const yCorrSize = { + 20: { '4K': -1, 'HD': 2 }, + 18: { '4K': -1, 'HD': 0 }, + 16: { '4K': 1, 'HD': 0 }, + 13: { '4K': -1, 'HD': 0 }, + 12: { '4K': -1, 'HD': 0 } + }; + const HDCorr = !RES._4K && headerFontSize < 15 ? 1 : 0; + const cThemeFontsCorr = RES._4K ? (grSet.customThemeFonts ? 1 : 3) : (grSet.customThemeFonts ? -1 : 0); + const yCorr = yCorrSize[headerFontSize] && yCorrSize[headerFontSize][RES._4K ? '4K' : 'HD'] || 0; + const date_y_base = !plSet.show_group_info ? part_h : part_h - cThemeFontsCorr; + const date_y = date_y_base + yCorr + HDCorr; + + this.hyperlinks.date = new Hyperlink(date_text, date_font, 'date', date_x, date_y, this.w, true); + } + + // * Artist + const albumArtist = '%album artist%'; + const is_radio = this.metadb.RawPath.startsWith('http'); + let artist_text = []; + let artist_x = left_pad; + let artist_w; + let multi_artist_spacing_w = 0; + let multi_artist = false; + if (!is_radio) { + for (let i = 0; i < albumArtist.length; i++) { + artist_text.push(...GetMetaValues(albumArtist, this.metadb)); + } + artist_text = [...new Set(artist_text)]; // Remove duplicates + for (let i = 0; i < artist_text.length; i++) { + if (i > 0) { + artist_x += bulletWidth + spaceWidth * 3; // Spacing between multi artists + multi_artist_spacing_w = bulletWidth + spaceWidth * 3 * i; // Total spacing width + } + const single_artist_w = this.w - left_pad * 2; + const multi_artist_w = this.w - left_pad - artist_x; + const ellipsis_w = gr.MeasureString('...', artist_font, 0, 0, 0, 0).Width; + artist_w = gr.MeasureString(artist_text[i], artist_font, 0, 0, 0, 0).Width; + if (artist_text.length > 1) { + multi_artist = true; + if (artist_w > multi_artist_w) { + while (artist_w + ellipsis_w > multi_artist_w && artist_text[i].length > 0) { + artist_text[i] = artist_text[i].substring(0, artist_text[i].length - 1); + artist_w = gr.MeasureString(artist_text[i], artist_font, 0, 0, 0, 0).Width; + } + if (artist_text[i].length > 0) { + artist_text[i] += '...'; + artist_w += ellipsis_w; + } + } + } else { + artist_w = single_artist_w; + } + this.hyperlinks[`artist${i}`] = new Hyperlink(artist_text[i], artist_font, 'artist', artist_x, SCALE(5 * (!plSet.show_group_info ? 2 : 1)), artist_w, true); + artist_x += artist_w; + } + } + + // * Album + const album_y = part_h * (!plSet.show_group_info ? 1.5 : 1) + ((RES._4K || RES._QHD && headerFontSize === 17 ? 5 : 4) * (!plSet.show_group_info ? 2 : 1)); + const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); + + if (album_text) { + this.hyperlinks.album = new Hyperlink(album_text, pl.font.album, 'album', left_pad, album_y, this.w - left_pad * 2, true); + } + + // * Record labels + const labelTags = ['label', 'publisher', 'discogs_label']; + let labels = []; + for (const label of labelTags) { + labels.push(...GetMetaValues(label, this.metadb)); + } + labels = [...new Set(labels)]; // Remove duplicates + let label_left = -right_edge * 2 + (RES._4K ? 42 : 20); + const label_y = Math.round(2 * this.h / 3) - (RES._4K ? 4 : -1); + for (let i = labels.length - 1; i >= 0; --i) { + if (i !== labels.length - 1) { + label_left -= (bulletWidth + spaceWidth * 2); // Spacing between labels + } + const label_w = gr.MeasureString(labels[i], pl.font.info, 0, 0, 0, 0).Width; + label_left -= label_w; + this.hyperlinks[`label${i}`] = new Hyperlink(labels[i], pl.font.info, 'label', label_left, label_y, this.w, true); + } + + // * Genres + const genres = GetMetaValues('%genre%', this.metadb); + let genre_left = left_pad; + const genre_y = label_y; + for (let i = 0; i < genres.length; i++) { + if (i > 0) { + genre_left += bulletWidth + spaceWidth * 2; // Spacing between genres + } + const genre_w = gr.MeasureString(genres[i], pl.font.info, 0, 0, 0, 0).Width; + this.hyperlinks[`genre${i}`] = new Hyperlink(genres[i], pl.font.info, 'genre', genre_left, genre_y, this.w, true); + genre_left += genre_w; + } + + for (const h in this.hyperlinks) { + this.hyperlinks[h].setY(this.y); + } + + // * Callbacks for headerTooltip + this.artist_text_w = gr.MeasureString(artist_text, artist_font, 0, 0, 0, 0).Width + (multi_artist ? multi_artist_spacing_w : 0); + this.album_text_w = gr.MeasureString(album_text, pl.font.album, 0, 0, 0, 0).Width; + this.max_w = this.w - left_pad * 2; + + // * Hyperlinks init done + this.hyperlinks_initialized = true; + } + + /** + * Initializes the rating and sets its position within a given area. + */ + initialize_rating() { + this.rating = new PlaylistRating(0, this.y, this.w - this.rating_right_pad, this.h, this.metadb); + this.rating.x = this.x + this.w - (this.rating.w + this.rating_right_pad); + } + + /** + * Creates disc headers for the playlist based on the provided parameters. + * @param {Array} prepared_rows - The rows with data. + * @param {number} rows_to_process_count - The number of rows to process. + * @returns {Array} The disc headers. + */ + create_disc_headers(prepared_rows, rows_to_process_count) { + return PlaylistDiscHeader.create_headers(this, this.x, 0, this.w, pl.geo.row_h, prepared_rows, rows_to_process_count); + } + + /** + * Returns a string containing information about a group of audio files, + * including codec, sample rate, bit depth, track count, disc number, replay gain, genre tags and duration. + * @param {boolean} is_radio - If the playback source is from radio streaming. + * @param {boolean} hasGenreTags - If the group has genre tags or not. + * @returns {string} The information about the group. + */ + getGroupInfoString(is_radio, hasGenreTags) { + const bitspersample = Number($('$info(bitspersample)', this.metadb)); + const samplerate = Number($('$info(samplerate)', this.metadb)); + const sample = ((bitspersample > 16 || samplerate > 44100 || grCfg.settings.playlistShowBitSampleAlways) ? $(' [$info(bitspersample)bit/]', this.metadb) + samplerate / 1000 + 'khz' : ''); + const formatAiffWav = 'aiff' || 'wav'; + let codec = formatAiffWav ? $('$upper($ext(%path%))', this.metadb) : $('$if2(%codec%,$ext(%path%))', this.metadb); + + if (codec === 'dca (dts coherent acoustics)') { + codec = 'dts'; + } + if (codec === 'cue') { + codec = $('$ext($info(referenced_file))', this.metadb); + } + else if (codec === 'mpc') { + codec += $('[-$info(codec_profile)]', this.metadb).replace('quality ', 'q'); + } + else if (['dts', 'ac3', 'atsc a/52'].includes(codec)) { + codec += $("[ $replace($replace($replace($info(channel_mode), + LFE,),' front, ','/'),' rear surround channels',$if($strstr($info(channel_mode),' + LFE'),.1,.0))] %bitrate%", this.metadb) + ' kbps'; + codec = codec.replace('atsc a/52', 'Dolby Digital'); + } + else if ($('$info(encoding)', this.metadb) === 'lossy') { + codec += $('$info(codec_profile)', this.metadb) === 'CBR' ? $('[-%bitrate% kbps]', this.metadb) : $('[-$info(codec_profile)]', this.metadb); + } + if (codec) { + codec += sample; + } + else { + codec = (this.metadb.RawPath.startsWith('3dydfy:') || this.metadb.RawPath.startsWith('fy+')) ? 'yt' : this.metadb.Path; + } + + let track_count = this.sub_items.length; + let has_discs = false; + if (this.sub_items[0] instanceof PlaylistDiscHeader) { + track_count = 0; + has_discs = true; + for (const discHeader of this.sub_items) { + track_count += discHeader.sub_items.length; + } + } + + const disc_number = (!plSet.show_disc_header && $('[%totaldiscs%]', this.metadb) !== '1') ? $('[ | Disc: %discnumber%[/%totaldiscs%]]', this.metadb) : ''; + const track_text = is_radio ? '' : ' | ' + + // (this.grouping_handler.show_disc() && has_discs ? this.sub_items.length + ' Discs - ' : '') + + (this.grouping_handler.show_disc() && has_discs && ($('[%totaldiscs%]', this.metadb) > 1) ? `${this.sub_items.length} Discs - ` : '') + + track_count + (track_count === 1 ? ' Track' : ' Tracks'); + const replaygain = (this.grouping_handler.show_disc() && (!has_discs || $('[%totaldiscs%]', this.metadb) === '')) ? $('[ | %replaygain_album_gain%]', this.metadb) : ''; + const plr_album = (plSet.show_PLR_header && this.grouping_handler.show_disc() && (!has_discs || $('[%totaldiscs%]', this.metadb) === '')) ? ` | ${this.meta_handler.get_PLR($('%replaygain_album_gain%', this.metadb), $('%replaygain_album_peak_db%', this.metadb))} LU` : ''; + let info_text = codec + disc_number + replaygain + plr_album + track_text; + + if (hasGenreTags) { + info_text = ` | ${info_text}`; + } + if (this.get_duration()) { + info_text += ` | Time: ${utils.FormatDuration(this.get_duration())}`; + } + + if (plSet.show_rating_header && $('%rating%', this.metadb) !== '') { + const albumName = $('%album%', this.metadb); + const albumRating = this.rating.get_album_rating().get(albumName); + + if (albumRating !== undefined) { + info_text += ` | Rating: ${albumRating}`; + } + } + + // * Use custom playlist header info if pattern was defined in the config file + const customInfoText = $(grCfg.settings.playlistCustomHeaderInfo, this.metadb); + if (customInfoText !== '') { + return ` | ${customInfoText}`; + } + + return info_text; + } + + /** + * Assigns and resizes an image to the "art" property, based on certain conditions and constraints. + * @param {GdiBitmap} image - The album art image that is being assigned to the `art` property. + */ + assign_art(image) { + if (!image || !plSet.show_album_art) { + this.art = null; + return; + } + + if ((image.Height === this.art_max_size && image.Width <= this.art_max_size) || + (image.Height <= this.art_max_size && image.Width === this.art_max_size)) { + this.art = image; + } + else { + const ratio = image.Height / image.Width; + let art_h = this.art_max_size; + let art_w = this.art_max_size; + if (image.Height > image.Width) { + art_w = Math.round(art_h / ratio); + } + else { + art_h = Math.round(art_w * ratio); + } + + try { // Prevent crash if album art is corrupt, file format is not supported or has an unusual ICC profile embedded + this.art = image.Resize(art_w, art_h); + } catch (e) { + console.log('\n\n'); + } + } + + PlaylistHeader.img_cache.add_image_for_meta(this.art, this.metadb); + } + + /** + * Checks if the album art was successfully loaded. + * @returns {boolean} True or false. + */ + is_art_loaded() { + return this.art !== undefined; + } + + /** + * Clears the playlist header background image. + */ + clearCachedHeaderImg() { + this.header_image = null; + } + + /** + * Resets the current hyperlinks. + */ + reset_hyperlinks() { + this.hyperlinks_initialized = false; + this.hyperlinks = {}; + } + + /** + * Displays the playlist header tooltip when artist or album text is truncated. + */ + headerTooltip() { + if (!grSet.showTooltipMain && !grSet.showTooltipTruncated || grm.ui.displayCustomThemeMenu && grm.ui.displayBiography) { + return; + } + + const artist_text = $(this.grouping_handler.get_title_query(), this.metadb); + const album_text = $(this.grouping_handler.get_sub_title_query(), this.metadb); + + if (this.artist_text_w > this.max_w || this.album_text_w > this.max_w || + plSet.use_compact_header && (this.artist_text_w_compact > this.artist_w_compact || this.album_text_w_compact > this.album_w_compact)) { + grm.ttip.showDelayed(`${artist_text}\n${album_text}`); + } else { + grm.ttip.stop(); + } + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Checks if the mouse click is within the boundaries of any hyperlinks and triggers their click event. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_down(x, y, m) { + for (const h in this.hyperlinks) { + if (this.hyperlinks[h].trace(x - this.x, y)) { + this.hyperlinks[h].click(); + return true; + } + } + return false; + } + + /** + * Handles mouse movement events and updates the state of hyperlinks based on the mouse position. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_move(x, y, m) { + let is_hovering = false; + let handled = false; + let needs_redraw = false; + + for (const h in this.hyperlinks) { + if (this.hyperlinks[h].trace(x - this.x, y)) { + is_hovering = true; + if (this.hyperlinks[h].state !== HyperlinkStates.Hovered) { + this.hyperlinks[h].state = HyperlinkStates.Hovered; + this.headerTooltip(); + needs_redraw = true; + } + handled = true; + } + else if (this.hyperlinks[h].state !== HyperlinkStates.Normal) { + this.hyperlinks[h].state = HyperlinkStates.Normal; + needs_redraw = true; + } + } + + if (!is_hovering) { + grm.ttip.stop(); + } + + if (needs_redraw) { + this.clearCachedHeaderImg(); + this.repaint(); + } + + return handled; + } + // #endregion +} + + +///////////////////// +// * DISC HEADER * // +///////////////////// +/** + * A class that creates collapsible headers in the playlist row for music albums that have multiple CD's. + * @augments {PlaylistBaseHeader} + */ +class PlaylistDiscHeader extends PlaylistBaseHeader { + /** + * Creates the `PlaylistDiscHeader` instance. + * @param {object} parent - The parent object or container that this object belongs to. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} idx - The index to identify the instance of the object. + */ + constructor(parent, x, y, w, h, idx) { + super(parent, x, y, w, h, idx); + /** @private @type {number} */ + this.idx = idx; + /** @private @type {string} */ + this.disc_title = ''; + } + + // * STATIC METHODS * // + // #region STATIC METHODS + /** + * Prepares the initialization data for the disc header. + * @param {Array} rows_to_process - The rows to process. + * @param {FbMetadbHandleList} rows_metadb - The metadb of the rows to process. + * @returns {Array} Has the following format Array<[row,row_data]>. + */ + static prepare_initialization_data(rows_to_process, rows_metadb) { + const tfo = fb.TitleFormat(`$ifgreater(%totaldiscs%,1,[Disc %discnumber% $if(${grTF.disc_subtitle}, \u2014 ,) ],)[${grTF.disc_subtitle}]`); + const disc_data = tfo.EvalWithMetadbs(rows_metadb); + + return Zip(rows_to_process, disc_data); + } + + /** + * Creates a header for each disc. + * @param {PlaylistBaseHeader} parent - The parent element. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {Array} prepared_rows - The rows of data. Has the following format Array<[row,row_data]>. + * @param {number} rows_to_process_count - The number of rows to process. + * @returns {Array} An array of PlaylistDiscHeader instances that have been created. + */ + static create_headers(parent, x, y, w, h, prepared_rows, rows_to_process_count) { + /** + * Checks if the data in the rows is valid. + * @param {Array} rows_with_data - The rows of data. + * @param {number} rows_to_check_count - The number of rows to check. + * @returns {boolean} True if the data is valid, false otherwise. + */ + const has_valid_data = (rows_with_data, rows_to_check_count) => { + for (let i = 0; i < rows_to_check_count; ++i) { + if (!IsEmpty(rows_with_data[i][1])) { + return true; + } + } + return false; + }; + + if (!has_valid_data(prepared_rows, rows_to_process_count)) { + TrimArray(prepared_rows, rows_to_process_count, true); + return []; + } + + let header_idx = 0; + const headers = []; + const prepared_rows_copy = Object.assign(Object.create(prepared_rows)); + prepared_rows_copy.length = rows_to_process_count; + + while (prepared_rows_copy.length) { + const header = new PlaylistDiscHeader(parent, x, y, w, h, header_idx); + const processed_items = header.initialize_items(prepared_rows_copy); + + TrimArray(prepared_rows_copy, processed_items, true); + + headers.push(header); + ++header_idx; + } + + TrimArray(prepared_rows, rows_to_process_count, true); + + return headers; + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the disc header in the rows when an album contains multiple CDs. + * @param {GdiGraphics} gr - The GDI graphics object. + * @param {number} top - The top of the header. + * @param {number} bottom - The bottom of the header. + */ + draw(gr, top, bottom) { + gr.SetSmoothingMode(SmoothingMode.None); + + const cur_x = this.x + SCALE(20); + const right_pad = SCALE(20); + + let title_font = pl.font.title_normal; + let title_color = pl.col.row_title_normal; + + if (this.is_selected()) { + title_color = pl.col.row_title_selected; + title_font = pl.font.title_selected; + gr.FillSolidRect(this.x, this.y, SCALE(8), this.h - 1, pl.col.row_sideMarker); + } + + const disc_header_text_format = Stringformat.v_align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + const disc_text = this.disc_title; // $('[Disc %discnumber% $if('+ tf.disc_subtitle+', \u2014 ,) ]['+ tf.disc_subtitle +']', that.sub_items[0].metadb); + gr.DrawString(disc_text, title_font, title_color, cur_x, this.y, this.w, this.h, disc_header_text_format); + const disc_w = Math.ceil(gr.MeasureString(disc_text, title_font, 0, 0, 0, 0).Width + 14); + + const subheader_PLR_album = (plSet.show_PLR_header && $('[%totaldiscs%]', this.sub_items[0].metadb) > 1) ? `${this.meta_handler.get_PLR($('%replaygain_album_gain%', this.sub_items[0].metadb), $('%replaygain_album_peak_db%', this.sub_items[0].metadb))} LU | ` : ''; + const replayGain = ($('[%totaldiscs%]', this.sub_items[0].metadb) > 1) ? $('[%replaygain_album_gain% | ]', this.sub_items[0].metadb) : ''; + const tracks_text = `${(replayGain)}${(subheader_PLR_album)}${this.sub_items.length} Track${this.sub_items.length > 1 ? 's' : ''} - ${utils.FormatDuration(this.get_duration())}`; + + const tracks_w = Math.ceil(gr.MeasureString(tracks_text, title_font, 0, 0, 0, 0).Width + 20); + const tracks_x = this.x + this.w - tracks_w - right_pad; + + gr.DrawString(tracks_text, title_font, title_color, tracks_x, this.y, tracks_w, this.h, Stringformat.v_align_center | Stringformat.h_align_far); + + if (this.is_collapsed || !this.is_collapsed) { + const line_y = Math.round(this.y + this.h / 2) + SCALE(4); + gr.DrawLine(RES._4K ? cur_x + disc_w + 5 : cur_x + disc_w - 5, line_y, RES._4K ? this.x + this.w - tracks_w - 40 : this.x + this.w - tracks_w - 10, line_y, 1, pl.col.row_disc_subheader_line); + } + } + + /** + * Initializes sub_items with rows from rows_with_data that have the same value in the second column, + * sets properties for each row, and returns the length of sub_items. + * @override + * @param {Array} rows_with_data - An array of arrays, where each inner array represents a row of data. + * Each inner array contains two elements: the row object and the data value for that row. + */ + initialize_items(rows_with_data) { // Rewritten + /** @type {PlaylistRow[]} */ + this.sub_items = []; + + if (!rows_with_data.length) { + return 0; + } + + const first_data = rows_with_data[0][1]; + for (let index = 0; index < rows_with_data.length; index++) { + if (first_data !== rows_with_data[index][1]) { + break; + } + + const row = rows_with_data[index][0]; + row.idx_in_header = index; + row.is_odd = !(index & 1); + row.parent = this; + this.sub_items.push(row); + } + + this.disc_title = first_data; + + return this.sub_items.length; + } + + /** + * Calculates the total duration in seconds of a list of sub items. + * @override + * @returns {number} A float number. + */ + get_duration() { + let duration_in_seconds = 0; + + for (const item of this.sub_items) { + duration_in_seconds += item.metadb.Length; + } + + if (!duration_in_seconds) { + return 0; + } + + return duration_in_seconds; + } + + /** + * Checks if all sub_items are selected. + * @returns {boolean} True or false. + */ + is_selected() { + return this.sub_items.every(row => row.is_selected()); + } + + /** + * Toggles the state of the disc header. + */ + toggle_collapse() { + this.is_collapsed = !this.is_collapsed; + + // this.header.collapse(); + // this.header.expand(); + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Toggles the collapse state of the disc header when the left mouse button is double-clicked. + * @param {PlaylistCollapseHandler} collapse_handler - The collapse handler to use. + */ + on_mouse_lbtn_dblclk(collapse_handler) { + collapse_handler.toggle_collapse(this); + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-row.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-row.js new file mode 100644 index 00000000..ca6ea2c2 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list-row.js @@ -0,0 +1,541 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Row * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +///////////// +// * ROW * // +///////////// +/** + * A class that creates and draws the playlist rows. + * @augments {BaseListItem} + */ +class PlaylistRow extends BaseListItem { + /** + * Creates the `PlaylistRow` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {FbMetadbHandle} metadb - The metadb of the track. + * @param {number} idx - The playlist row index. + * @param {number} cur_playlist_idx_arg - The current playlist index. + */ + constructor(x, y, w, h, metadb, idx, cur_playlist_idx_arg) { + super(x, y, w, h); + + /** @private @constant @type {FbMetadbHandle} */ + this.metadb = metadb; + /** @public @constant @type {number} */ + this.idx = idx; + /** @private @constant @type {number} */ + this.cur_playlist_idx = cur_playlist_idx_arg; + /** @public @type {?number[]} */ + this.queue_indexes = undefined; + /** @public @type {number} */ + this.queue_idx_count = 0; + /** @public @type {number} */ + this.idx_in_header = undefined; + /** @public @constant @type {PlaylistBaseHeader} */ + this.parent = undefined; + + /** @public @type {boolean} */ + this.is_odd = false; + /** @public @type {boolean} */ + this.is_playing = false; + /** @public @type {boolean} */ + this.is_focused = false; + /** @public @type {boolean} */ + this.is_drop_boundary_reached = false; + /** @public @type {boolean} */ + this.is_drop_bottom_selected = false; + /** @public @type {boolean} */ + this.is_drop_top_selected = false; + /** @public @type {boolean} */ + this.is_cropped = false; + + /** @private @type {number} */ + this.rating_left_pad = 0; + /** @private @type {number} */ + this.rating_right_pad = 10; + /** @private @type {?PlaylistRating} */ + this.rating = undefined; + + /** @private @type {number} Playlist title color for row hover. */ + this.title_color = pl.col.row_title_normal; + /** @private @type {?string} */ + this.title_text = undefined; + /** @private @type {?string} */ + this.title_artist_text = undefined; + /** @private @type {?string} */ + this.count_text = undefined; + /** @private @type {?string} */ + this.length_text = undefined; + + /** @public @type {PlaylistMetaHandler} */ + this.meta_handler = new PlaylistMetaHandler(); + + this.initialize_rating(); + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Draws the playlist rows, including the titles, artists, lengths, ratings, playcounts, and queue positions. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + draw(gr) { + if (grSet.panelWidthAuto && !plSet.show_header) { + this.x = pl.playlist.x; + } + + gr.SetSmoothingMode(SmoothingMode.None); + + if (this.is_odd && plSet.show_row_stripes) { + gr.FillSolidRect(this.x, this.y, this.w, this.h, pl.col.row_stripes_bg); + } + + let title_font = pl.font.title_normal; + const title_artist_font = pl.font.title_selected; + let title_artist_color = pl.col.row_title_selected; + const scrollbar = plSet.show_scrollbar && pl.playlist.is_scrollbar_available; + + if (!grSet.playlistRowHover) this.title_color = pl.col.row_title_normal; + + if (this.is_selected()) { + this.title_color = pl.col.row_title_selected; + title_font = pl.font.title_selected; + title_artist_color = pl.col.row_title_normal; + + // if (plSet.show_row_stripes) { // Don't need this!? + // // Last item is cropped + // const rect_h = this.is_cropped ? this.h - 1 : this.h; + // gr.DrawRect(this.x, this.y, this.w, rect_h, 1, pl.col.row_selection_frame_cropped); + // } + + // if (pref.theme.startsWith('custom')) { + // gr.FillSolidRect(this.x, this.y, this.w, this.h, pl.col.row_selection_bg); + // } + if (!this.is_playing) { // Do not draw selection on now playing to prevent 1px overlapping + gr.DrawRect(this.x, this.is_playing ? this.y - 1 : this.y, scrollbar ? this.w - (RES._4K ? 25 : 13) : this.w, this.h, 1, pl.col.row_selection_frame); + // Hide DrawRect 1px gaps when all songs are completely selected + gr.DrawRect(this.x, this.is_playing ? this.y - 1 : this.y, SCALE(7), this.h, 1, pl.col.row_sideMarker); + gr.FillSolidRect(this.x, this.y, SCALE(8), this.h, pl.col.row_sideMarker); + } + } + + if (this.is_playing && pl.col.row_nowplaying_bg !== '') { // * Wait until nowplaying bg has a new color to prevent flashing + this.title_color = pl.col.row_title_playing; + title_font = pl.font.title_playing; + + const bg_color = pl.col.row_nowplaying_bg; + gr.FillSolidRect(this.x, this.y, scrollbar ? this.w - SCALE(12) : this.w, this.h, bg_color); + if (ColorDistance(bg_color, title_artist_color) < 195) { + title_artist_color = this.title_color; + } + + if (grCol.lightBg && (grSet.theme === 'white' && !grSet.styleBlackAndWhite && !grSet.styleBlackAndWhite2 || grSet.theme === 'black')) { + this.title_color = RGB(20, 20, 20); + title_artist_color = RGB(0, 0, 0); + } + if (!grCol.lightBg && grSet.theme === 'white' && !grSet.styleBlackAndWhite && !grSet.styleBlackAndWhite2 && !grm.ui.isStreaming && grSet.layout === 'default') { + this.title_color = RGB(240, 240, 240); + title_artist_color = RGB(220, 220, 220); + } + if (grSet.theme === 'white' && (grSet.styleBlackAndWhite || grSet.styleBlackAndWhite2) || !['white', 'black', 'cream'].includes(grSet.theme)) { + gr.FillSolidRect(this.x, this.y, SCALE(8), this.h, pl.col.row_sideMarker); + } + } + + //---> + if (this.is_drop_top_selected) { + gr.DrawLine(this.x, this.y + 1, this.x + this.w, this.y + 1, 2, this.is_drop_boundary_reached ? pl.col.row_drag_line_reached : pl.col.row_drag_line); + } + if (this.is_drop_bottom_selected) { + gr.DrawLine(this.x, this.y + this.h - 1, this.x + this.w, this.y + this.h - 1, 2, this.is_drop_boundary_reached ? pl.col.row_drag_line_reached : pl.col.row_drag_line); + } + + //////////////////////////////////////////////////////////// + + const is_radio = this.metadb.RawPath.startsWith('http'); + + const right_spacing = SCALE(20); + let cur_x = this.x + right_spacing; + let right_pad = SCALE(20); + const testRect = false; + + if ($('$ifgreater(%totaldiscs%,1,true,false)', this.metadb) !== 'false') { + cur_x += SCALE(0); + } + + // * LENGTH + { + if (this.length_text == null) { + this.length_text = $('[%length%]', this.metadb); + } + this.length_text = this.is_playing && grSet.playlistTimeRemaining ? $('[-%playback_time_remaining%]') : $('[%length%]', this.metadb); + + const length_w = SCALE(60); + if (this.length_text) { + const length_x = this.x + this.w - length_w - right_pad; + + gr.DrawString(this.length_text, title_font, this.title_color, length_x, this.y, length_w, this.h, Stringformat.h_align_far | Stringformat.v_align_center); + testRect && gr.DrawRect(length_x, this.y - 1, length_w, this.h, 1, RGBA(155, 155, 255, 250)); + } + // We always want that padding + right_pad += Math.max(length_w, Math.ceil(gr.MeasureString(this.length_text, title_font, 0, 0, 0, 0).Width + 10)); + } + + // * RATING + if (plSet.show_rating) { + this.rating.x = this.x + this.w - this.rating.w - right_pad; + this.rating.y = this.y; + this.rating.draw(gr, pl.col.row_rating_color); + + right_pad += this.rating.w + this.rating_right_pad + this.rating_left_pad; + } + + // * PLR + if (plSet.show_PLR) { + if ($('[%replaygain_track_gain%]', this.metadb) && $('[%replaygain_track_peak_db%]', this.metadb)) { + this.plr_track = this.meta_handler.get_PLR($('%replaygain_track_gain%', this.metadb), $('%replaygain_track_peak_db%', this.metadb)) + } + + if (this.plr_track) { + if (this.plr_track < 10) { + this.plr_track = ` ${this.plr_track}` + } + this.plr_track += ' LU |'; + + const plr_track_w = Math.ceil(gr.MeasureString(this.plr_track, pl.font.plr_track, 0, 0, 0, 0).Width); + const plr_track_x = this.x + this.w - plr_track_w - right_pad; + + gr.DrawString(this.plr_track, pl.font.plr_track, this.title_color, plr_track_x, this.y, plr_track_w, this.h, Stringformat.align_center); + testRect && gr.DrawRect(plr_track_x, this.y - 1, plr_track_w, this.h, 1, RGBA(155, 155, 255, 250)); + + right_pad = this.w - (plr_track_x - this.x) + 5; + } + } + + // * COUNT + if (plSet.show_playcount) { + if (this.count_text == null) { + if (is_radio) { + this.count_text = ''; + } + else { + this.count_text = $('%play_count%', this.metadb); + if (this.count_text !== '0') { + this.count_text = $('[$max(%play_count%, %lastfm_play_count%)]', this.metadb); + this.count_text = !Number(this.count_text) ? '' : (`${this.count_text} |`); + } + else if (grSet.lastFmScrobblesFallback) { + this.count_text = $('[$max(%lastfm_play_count%, %play_count%)]', this.metadb); + this.count_text = !Number(this.count_text) ? '' : (`${this.count_text} |`); + } + else { + // Don't want to show lastfm play count if track hasn't been played locally + this.count_text = ''; + } + } + } + + if (this.count_text) { + const count_w = Math.ceil( + /** @type {!number} */ + gr.MeasureString(this.count_text, pl.font.playcount, 0, 0, 0, 0).Width + ); + const count_x = this.x + this.w - count_w - right_pad; + + gr.DrawString(this.count_text, pl.font.playcount, this.title_color, count_x, this.y, count_w, this.h, Stringformat.align_center); + testRect && gr.DrawRect(count_x, this.y - 1, count_w, this.h, 1, RGBA(155, 155, 255, 250)); + + right_pad += count_w; + } + } + + // * QUEUE + const queueText = plSet.show_queue_position && this.queue_indexes != null ? ` [${this.queue_indexes}]` : ''; + + // * TITLE init + if (this.title_text == null) { + const margin = !grSet.showPlaylistTrackNumbers && !grSet.showPlaylistIndexNumbers ? this.is_playing ? ' ' : '' : ' '; + const indexNumbers = this.idx < 9 ? `0${this.idx + 1}. ` : `${this.idx + 1}. `; + const trackNumbers = grSet.showPlaylistIndexNumbers ? indexNumbers : `$if2(%tracknumber%,$pad_right(${this.idx_in_header + 1},2,0)). `; + const trackNumbersVinyl = grSet.showPlaylistIndexNumbers ? indexNumbers : `$if2(${grTF.vinyl_track},00. )`; + const trackNumberQuery = this.is_playing ? plSet.show_header ? ' ' : grSet.showVinylNums ? trackNumbersVinyl : trackNumbers : trackNumbers; + const showTrackNumber = grSet.showPlaylistTrackNumbers || grSet.showPlaylistIndexNumbers ? trackNumberQuery : ''; + const customTitle = grCfg.settings.playlistCustomTitle; + const customTitleNoHeader = grCfg.settings.playlistCustomTitleNoHeader; + + const titleQuery = + plSet.show_header ? + (grSet.showArtistPlaylistRows && grSet.showAlbumPlaylistRows && customTitle === '' ? `${showTrackNumber}${margin}%artist% - %album% - %title%[ '('%original artist%' cover)']` : + grSet.showArtistPlaylistRows && customTitle === '' ? `${showTrackNumber}${margin}%artist% - %title%[ '('%original artist%' cover)']` : + grSet.showAlbumPlaylistRows && customTitle === '' ? `${showTrackNumber}${margin}%album% - %title%[ '('%original artist%' cover)']` : + customTitle !== '' ? `${showTrackNumber}${margin}${customTitle}` : + `${showTrackNumber}${margin}%title%[ '('%original artist%' cover)']`) : + customTitleNoHeader !== '' ? `${showTrackNumber}${margin} ${customTitleNoHeader}` : + `${margin} %artist% - %album% - ${showTrackNumber} %title%[ '('%original artist%' cover)']`; + + this.title_text = (fb.IsPlaying && this.is_playing && is_radio) ? $(titleQuery) : $(titleQuery, this.metadb); + } + + // * TITLE ARTIST init + if (this.title_artist_text == null) { + const pattern = `^${$('%album artist%', this.metadb).replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')} `; + const regex = new RegExp(pattern); + this.title_artist_text = $(`[$if($strcmp(${grTF.artist},%artist%),$if(%album artist%,$if(%track artist%,%track artist%,),),${grTF.artist})]`, this.metadb); + if (this.title_artist_text.length) { + // if tf.artist evaluates to something different than %album artist% strip %artist% from the start of the string + // i.e. tf.artist = "Metallica feat. Iron Maiden" then we want this.title_artist_text = "feat. Iron Maiden" + this.title_artist_text = this.title_artist_text.replace(regex, ''); + this.title_artist_text = ` \u25AA ${this.title_artist_text}`; + } + } + + // * TITLE draw + { + const title_w = this.w - right_pad - SCALE(44); + const title_text_format = Stringformat.v_align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + DrawString(gr, this.title_text, title_font, this.title_color, cur_x, this.y, title_w, this.h, title_text_format); + if (this.is_playing) { + DrawString(gr, fb.IsPaused ? Guifx.pause : Guifx.play, grFont.guifx, this.title_color, cur_x, this.y, title_w, this.h, title_text_format); + } + + testRect && gr.DrawRect(this.x, this.y - 1, title_w, this.h, 1, RGBA(155, 155, 255, 250)); + + cur_x += Math.ceil( + /** @type {!number} */ + gr.MeasureString(this.title_text, title_font, 0, 0, title_w, this.h, title_text_format | Stringformat.measure_trailing_spaces).Width + ); + } + + // * TITLE ARTIST draw + if (this.title_artist_text) { + const title_artist_x = cur_x; + const title_artist_w = this.w - (title_artist_x - this.x) - right_pad; + const title_artist_text_format = Stringformat.v_align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + + if (grSet.showDifferentArtist) { + DrawString(gr, this.title_artist_text, title_artist_font, this.title_color, title_artist_x, this.y, title_artist_w, this.h, title_artist_text_format); + } + cur_x += Math.ceil( + /** @type {!number} */ + gr.MeasureString(this.title_artist_text, title_artist_font, 0, 0, title_artist_w, this.h, title_artist_text_format).Width + ); + } + + if (queueText) { + const queueX = cur_x; + const queueW = this.w - (queueX - this.x) - right_pad; + const queueTextFormat = Stringformat.v_align_center | Stringformat.trim_ellipsis_char | Stringformat.no_wrap; + let queueColor = this.title_color; // col.primary; + + if (this.is_playing || ColorDistance(queueColor, pl.col.row_stripes_bg) < 165) { + queueColor = this.title_color; + } + gr.DrawString(queueText, title_font, queueColor, queueX, this.y, queueW, this.h, queueTextFormat); + } + gr.SetSmoothingMode(SmoothingMode.HighQuality); + + // * Refresh playlist time remaining all the time when activated + let timeRemainingTimer = null; + if (grSet.playlistTimeRemaining && this.is_playing && fb.IsPlaying) { + timeRemainingTimer = setTimeout(() => { + const length_w = SCALE(60); + const length_x = this.x + this.w - length_w; + window.RepaintRect(length_x + 5, this.y, length_w + 5, this.h); + }, 1000); + } else { + clearTimeout(timeRemainingTimer); + } + + // * Change and update title_color back to normal from previous mouse row hover state + this.title_color = pl.col.row_title_normal; + + // * Callbacks for rowTooltip + this.title_w = this.w - right_pad - SCALE(44); + this.title_text_w = gr.MeasureString(this.title_text, title_font, 0, 0, 0, 0).Width; + } + + /** + * Sets the x-coordinate for the ListItem object and updates the x-coordinate for the rating property. + * @override + * @param {number} x - The x-coordinate. + */ + set_x(x) { + BaseListItem.prototype.set_x.apply(this, [x]); + this.rating.x = x; + } + + /** + * Sets the y-coordinate for the ListItem object and updates the y-coordinate for the rating property. + * @override + * @param {number} y - The y-coordinate. + */ + set_y(y) { + BaseListItem.prototype.set_y.apply(this, [y]); + this.rating.y = y; + } + + /** + * Sets the width for the ListItem object and updates the width for the rating property. + * @override + * @param {number} w - The width. + */ + set_w(w) { + BaseListItem.prototype.set_w.apply(this, [w]); + this.initialize_rating(); + } + + /** + * Changes the playlist row text state when playlist item is hovered or pressed. + * @param {PlaylistRowState} item - The playlist row item. + */ + changeRowState(item) { + const titleColor = { + [PlaylistRowState.normal]: pl.col.row_title_normal, + [PlaylistRowState.hovered]: pl.col.row_title_hovered, + [PlaylistRowState.pressed]: pl.col.row_title_selected + }; + this.title_color = titleColor[item] || this.title_color; + } + + /** + * Checks if a playlist item is selected. + * @returns {boolean} True or false. + */ + is_selected() { + return plman.IsPlaylistItemSelected(this.cur_playlist_idx, this.idx); + } + + /** + * Initializes the rating and sets its position within a given area. + */ + initialize_rating() { + this.rating = new PlaylistRating(0, this.y, this.w - this.rating_right_pad, this.h, this.metadb); + this.rating.x = this.x + this.w - (this.rating.w + this.rating_right_pad); + } + + /** + * Handles mouse click events on the rating. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + rating_click(x, y) { + Assert(plSet.show_rating, LogicError, 'Rating_click was called, when there was no rating object.\nShould use trace before calling click'); + this.rating.click(x, y); + } + + /** + * Checks if the rating is being traced. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + rating_trace(x, y) { + if (!plSet.show_rating) { + return false; + } + return this.rating.trace(x, y); + } + + /** + * Checks if the mouse is on a playlist item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True or false. + */ + trace(x, y) { + return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; + } + + /** + * Resets the queried data for title, title artist, count, and length. + */ + reset_queried_data() { + this.title_text = undefined; + this.title_artist_text = undefined; + this.count_text = undefined; + this.length_text = undefined; + } + + /** + * Clears the title text from an item in the playlist row. + */ + clear_title_text() { + this.title_text = null; + } + + /** + * Displays the playlist row tooltip when title text is truncated. + */ + rowTooltip() { + if (!grSet.showTooltipMain && !grSet.showTooltipTruncated || grm.ui.displayCustomThemeMenu && grm.ui.displayBiography) { + return; + } + + const is_radio = this.metadb.RawPath.startsWith('http'); + const margin = ''; + const indexNumbers = this.idx < 9 ? `0${this.idx + 1}. ` : `${this.idx + 1}. `; + const trackNumbers = grSet.showPlaylistIndexNumbers ? indexNumbers : `$if2(%tracknumber%,$pad_right(${this.idx_in_header + 1},2,0)). `; + const trackNumberQuery = grSet.showVinylNums ? grSet.showPlaylistIndexNumbers ? indexNumbers : grTF.vinyl_track : trackNumbers; + const showTrackNumber = grSet.showPlaylistTrackNumbers || grSet.showPlaylistIndexNumbers ? trackNumberQuery : ''; + const customTitle = grCfg.settings.playlistCustomTitle; + const customTitleNoHeader = grCfg.settings.playlistCustomTitleNoHeader; + + const titleQuery = + plSet.show_header ? + (grSet.showArtistPlaylistRows && grSet.showAlbumPlaylistRows && customTitle === '' ? `${showTrackNumber}${margin}%artist% - %album% - %title%[ '('%original artist%' cover)']` : + grSet.showArtistPlaylistRows && customTitle === '' ? `${showTrackNumber}${margin}%artist% - %title%[ '('%original artist%' cover)']` : + grSet.showAlbumPlaylistRows && customTitle === '' ? `${showTrackNumber}${margin}%album% - %title%[ '('%original artist%' cover)']` : + customTitle !== '' ? `${showTrackNumber}${margin}${customTitle}` : + `${showTrackNumber}${margin}%title%[ '('%original artist%' cover)']`) : + customTitleNoHeader !== '' ? `${showTrackNumber}${margin} ${customTitleNoHeader}` : + `%artist%$crlf()%album%$crlf()${showTrackNumber} %title%[ '('%original artist%' cover)']`; + + const title_text = (fb.IsPlaying && this.is_playing && is_radio) ? $(titleQuery) : $(titleQuery, this.metadb); + + if (this.title_text_w > this.title_w) { + grm.ttip.showDelayed(title_text); + } else { + grm.ttip.stop(); + } + } + + /** + * Updates and determines the color of the playlist row title text. + */ + update_title_color() { + const panelWhite = grCol.colBrightness > 210 && grSet.styleRebornFusion || grCol.colBrightness2 > 210 && grSet.styleRebornFusion2 || grSet.styleBlackAndWhite2; + const panelBlack = grCol.colBrightness < 25 && grSet.styleRebornFusion || grCol.colBrightness2 < 25 && grSet.styleRebornFusion2 || grSet.styleBlackAndWhite; + this.title_color = panelWhite ? RGB(80, 80, 80) : panelBlack ? RGB(200, 200, 200) : pl.col.row_title_normal; + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Traces the mouse movement and changes the playlist text row state on an item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + */ + on_mouse_move(x, y, m) { + if (grSet.playlistRowHover && grm.ui.loadingThemeComplete) { + this.changeRowState(this.trace(x, y) ? PlaylistRowState.hovered : PlaylistRowState.normal); + } + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-list.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list.js new file mode 100644 index 00000000..bed37c11 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-list.js @@ -0,0 +1,733 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN PlayList Base List * // +// * Author: TT * // +// * Org. Author: TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +/////////////////// +// * BASE LIST * // +/////////////////// +/** + * A class that represents a basic list with items to display and a scrollbar. + * Also handles basic controls for mouse and keyboard events. + */ +class BaseList { + /** + * Creates the `BaseList` instance. + * By default each item is a row of a fixed size. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {BaseListContent} content - The content container. + */ + constructor(x, y, w, h, content) { + /** @public @type {number} */ + this.x = x; + /** @public @type {number} */ + this.y = y; + /** @public @type {number} */ + this.w = w; + /** @public @type {number} */ + this.h = h; + + /** @protected @type {ListContent} */ + this.cnt = content; + /** @protected @constant {number}*/ + this.row_h = SCALE(plSet.row_h); // Also see playlist.reinitialize + /** @protected @type {number} */ + this.list_x = this.x + plSet.list_left_pad; + /** @protected @type {number} */ + this.list_y = 0; + /** @protected @type {number} */ + this.list_w = 0; + /** @protected @type {number} */ + this.list_h = 0; + /** @protected @type {number} */ + this.rows_to_draw_precise = 0; + /** @protected @type {Array} */ + this.items_to_draw = []; + + /** @protected @type {boolean} */ + this.mouse_in = false; + /** @protected @type {boolean} */ + this.mouse_down = false; + /** @protected @type {boolean} */ + this.mouse_double_clicked = false; + + /** @protected @type {PlaylistScrollbar} */ + this.scrollbar = undefined; + /** @protected @type {number} Row shift is always non-negative. */ + this.row_shift = 0; + /** @protected @type {number} Pixel shift is always non-positive. */ + this.pixel_shift = 0; + /** @protected @type {boolean} */ + this.is_scrollbar_visible = plSet.show_scrollbar; + /** @protected @type {boolean} */ + this.is_scrollbar_available = false; + /** @protected @type {boolean} */ + this.needs_scrollbar_update = false; + + /** + * Throttles a function to limit how often it can be invoked. This specific throttled function + * is used to repaint a rectangular area of a window at most once every frame at a rate of 60fps. + * @type {Function} + * @private + */ + this.throttled_repaint = Throttle(() => { + window.RepaintRect(this.x - 1, pl.playlist.y, this.w + 1, pl.playlist.h); + }, 1000 / 60); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Updates the width of the list and its scrollbar. + * @private + */ + _update_list_w_size() { + this.list_w = this.w - plSet.list_left_pad - plSet.list_right_pad; + + if (this.is_scrollbar_available) { + if (this.is_scrollbar_visible) { + this.list_w -= this.scrollbar.w + 2; + } + this.scrollbar.set_x(this.w - pl.geo.scrollbar_w - pl.geo.scrollbar_right_pad); + } + + this.initialize_scrollbar(); + this.update_scrollbar(); + this.on_content_to_draw_change(); + + this.cnt.update_items_w_size(this.list_w); + } + + /** + * Updates the size and position of the list and also the scrollbar. + * @private + */ + _update_list_h_size() { + this.list_y = this.y + plSet.list_top_pad; + this.list_h = this.h - (plSet.list_bottom_pad + plSet.list_top_pad); + + this.rows_to_draw_precise = this.list_h / this.row_h; + + this.initialize_scrollbar(); + this.update_scrollbar(); + this.on_content_to_draw_change(); + } + + /** + * Calculates the shift parameters for scrolling. + * @private + */ + _calculate_shift_params() { + this.row_shift = Math.floor(plSet.scrollbar_pos); + this.pixel_shift = -Math.round((plSet.scrollbar_pos - this.row_shift) * this.row_h); + } + + /** + * Updates the size of the list when visibility of the scrollbar changes. + * @param {boolean} is_visible - The state if the scrollbar is currently visible or not. + * @private + */ + _on_scrollbar_visibility_change(is_visible) { + if (this.is_scrollbar_visible !== is_visible) { + this.is_scrollbar_visible = is_visible; + this._update_list_w_size(); + } + } + // #endregion + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Updates the width size of the list. + * @param {number} w - The width. + * @protected + */ + on_w_size(w) { + this.w = w; + this._update_list_w_size(); + } + + /** + * Updates the height size of the list. + * @param {number} h - The height. + * @protected + */ + on_h_size(h) { + this.h = h; + this._update_list_h_size(); + } + + /** + * Calculates shift parameters and generates a list of items to draw. + * @protected + */ + on_content_to_draw_change() { + this._calculate_shift_params(); + this.items_to_draw = this.cnt.generate_items_to_draw(this.list_y, this.list_h, this.row_shift, this.pixel_shift, this.row_h); + } + + /** + * Called when playlist data in content is changed and updates the scrollbar. + * @protected + */ + on_list_items_change() { + this.update_scrollbar(); + this.on_content_to_draw_change(); + } + + /** + * Gets the item from an array that intersects with the given mouse coordinates. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {BaseListItem} The item that is found under the mouse coordinates. + * @protected + */ + get_item_under_mouse(x, y) { + return this.items_to_draw.find(item => item.trace(x, y)); + } + + /** + * Checks if a given point (x, y) is within the boundaries of an list item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True if the given x and y coordinates are within the boundaries. + */ + trace(x, y) { + return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; + } + + /** + * Checks if a given point (x, y) is within the boundaries of the list. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True if the given x and y coordinates are within the boundaries. + */ + trace_list(x, y) { + return x >= this.list_x && x < this.list_x + this.list_w && y >= this.list_y && y < this.list_y + this.list_h; + } + + /** + * Invokes a throttled repaint. + */ + repaint() { + this.throttled_repaint(); + } + + /** + * Appends a context menu item to a parent menu that toggles the visibility of a scrollbar in the playlist. + * @param {string} parent_menu - The menu to which the scrollbar visibility context menu item will be appended. + */ + append_scrollbar_visibility_context_menu_to(parent_menu) { + parent_menu.appendItem('Scrollbar auto-hide', () => { + grSet.playlistAutoHideScrollbar = !grSet.playlistAutoHideScrollbar; + plSet.show_scrollbar = !grSet.playlistAutoHideScrollbar; + grm.ui.updatePlaylist(); + }, { is_checked: grSet.playlistAutoHideScrollbar }); + } + + /** + * The scrollbar callback to update the scroll position and content. + * @protected + */ + scrollbar_redraw_callback() { + const invalidPos = (plSet.scrollbar_pos || this.scrollbar.scroll) > this.scrollbar.scrollable_lines; // Prevent scroll crash + plSet.scrollbar_pos = invalidPos ? 0 : this.scrollbar.scroll; + this.on_content_to_draw_change(); + this.repaint(); + } + + /** + * Initializes the scrollbar with specific dimensions and a callback for redrawing. + * @protected + */ + initialize_scrollbar() { + this.is_scrollbar_available = false; + + const scrollbar_x = this.x - SCALE(32) + this.w - pl.geo.scrollbar_w - pl.geo.scrollbar_right_pad; + const scrollbar_y = this.y + pl.geo.scrollbar_top_pad - (RES._4K ? 12 : 5); + const scrollbar_w = SCALE(28); + const scrollbar_h = this.h - (pl.geo.scrollbar_bottom_pad + pl.geo.scrollbar_top_pad) + (RES._4K ? 14 : 5); + + if (this.scrollbar) { + this.scrollbar.reset(); + } + + this.scrollbar = new PlaylistScrollbar(scrollbar_x, scrollbar_y, scrollbar_w, scrollbar_h, this.row_h, this.scrollbar_redraw_callback.bind(this)); + } + + /** + * Checks if the scrollbar should be displayed based on the total height of the content and updates the scrollbar accordingly. + * @protected + */ + update_scrollbar() { + const total_height_in_rows = this.cnt.calculate_total_h_in_rows(); + + if (total_height_in_rows <= this.rows_to_draw_precise) { + this.is_scrollbar_available = false; + plSet.scrollbar_pos = 0; + this._on_scrollbar_visibility_change(false); + } + else if (this.scrollbar) { + this.scrollbar.set_window_param(this.rows_to_draw_precise, total_height_in_rows); + this.scrollbar.scroll_to(plSet.scrollbar_pos, true); + + const invalidPos = (plSet.scrollbar_pos || this.scrollbar.scroll) > this.scrollbar.scrollable_lines; // Prevent scroll crash + plSet.scrollbar_pos = invalidPos ? 0 : this.scrollbar.scroll; + + this.is_scrollbar_available = true; + this._on_scrollbar_visibility_change(plSet.show_scrollbar); + } + } + // #endregion + + // * CALLBACKS * // + // #region CALLBACKS + /** + * Draws the list items with a scrollbar if necessary. + * @param {GdiGraphics} gr - The GDI graphics object. + */ + on_paint(gr) { + gr.SetTextRenderingHint(TextRenderingHint.ClearTypeGridFit); + + if (this.items_to_draw.length) { + for (let i = this.items_to_draw.length - 1; i >= 0; --i) { + this.items_to_draw[i].draw(gr); + } + } + else { + const empty = plman.PlaylistCount <= 1; + const name = plman.GetPlaylistName(plman.ActivePlaylist); + const text = empty ? 'Drop some tracks here\nor play from the library' : `Playlist: ${name}\nEmpty`; + gr.DrawString(text, pl.font.title_normal, pl.col.row_title_normal, this.x, this.y, this.w, this.h, Stringformat.align_center); + } + + if (this.is_scrollbar_visible) { + this.scrollbar.paint(gr); + } + } + + /** + * Adjusts the size and position of an element based on the provided parameters. + * Also handles hiding the scrollbar if auto-hide is enabled. + * @param {number} w - The width. + * @param {number} h - The height. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + on_size(w, h, x, y) { + const w_changed = this.w !== w || this.x !== x; + const h_changed = this.h !== h || this.y !== y; + + if (h_changed) { + this.y = y; + this.on_h_size(h); + } + + if (w_changed) { + this.x = x; + this.list_x = this.x + plSet.list_left_pad; + this.on_w_size(w); + } + + // * Makes sure to always hide scrollbar when auto-hide is enabled + if (grSet.playlistAutoHideScrollbar && plSet.show_scrollbar) { + plSet.show_scrollbar = false; + } + } + + /** + * Handles mouse double click events. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_dblclk(x, y, m) { + this.mouse_down = true; + this.mouse_double_clicked = true; + + if (this.is_scrollbar_visible && this.scrollbar.trace(x, y)) { + this.scrollbar.lbtn_dn(x, y); + return true; + } + + return false; + } + + /** + * Handles left mouse button down events and performs different actions based on the mouse position and other conditions. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_down(x, y, m) { + this.mouse_down = true; + + if (this.mouse_double_clicked) { + return true; + } + + if (this.is_scrollbar_visible && this.scrollbar.trace(x, y)) { + this.scrollbar.lbtn_dn(x, y); + return true; + } + + return false; + } + + /** + * Handles left mouse button up events and checks if the scrollbar is visible and if it was being dragged. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_lbtn_up(x, y, m) { + if (!this.mouse_down) { + return true; + } + + this.mouse_double_clicked = false; + this.mouse_down = false; + + if (this.is_scrollbar_visible) { + const wasDragging = this.scrollbar.b_is_dragging; + this.scrollbar.lbtn_up(x, y); + if (wasDragging) { + return true; + } + } + + return false; + } + + /** + * Handles mouse leave events and performs actions related to scrollbar and mouse state. + */ + on_mouse_leave() { + if (this.is_scrollbar_available) { + this.scrollbar.leave(); + } + + this.mouse_in = false; + } + + /** + * Handles mouse movement events and updates the scrollbar position, + * hides the scrollbar automatically, and updates the playlist row hover state. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {string} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_move(x, y, m) { + if (this.is_scrollbar_visible) { + this.scrollbar.move(x, y); + + if (this.scrollbar.b_is_dragging || this.scrollbar.trace(x, y)) { + return true; + } + } + + // * Automatic scrollbar hide + if (grSet.playlistAutoHideScrollbar) { + if (this.scrollbar.trace(x, y)) { + plSet.show_scrollbar = true; + this.update_scrollbar(); + this.needs_scrollbar_update = true; + } else if (!this.mouse_in || !this.scrollbar.trace(x, y)) { + plSet.show_scrollbar = false; + if (this.needs_scrollbar_update) { + this.update_scrollbar(); + this.needs_scrollbar_update = false; + } + } + } + // * Update playlist row hover state or automatic scrollbar hide + if (grSet.playlistRowHover || grSet.playlistAutoHideScrollbar) { + this.repaint(); + } + + this.mouse_in = this.trace(x, y); + + return false; + } + + /** + * Handles right mouse button up events and shows the context menu on scrollbar if available. + * Also handles the case when mouse is out of the list. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} m - The mouse mask. + * @returns {boolean} True or false. + */ + on_mouse_rbtn_up(x, y, m) { + if (!this.trace(x, y)) { + return true; + } + + if (!this.is_scrollbar_available + || !this.is_scrollbar_visible + || !this.scrollbar.trace(x, y)) { + return false; + } + + const cmm = new ContextMainMenu(); + + this.append_scrollbar_visibility_context_menu_to(cmm); + + if (utils.IsKeyPressed(VK_SHIFT)) { + grm.ctxMenu.contextMenuDefault(cmm); + } + + grm.ui.activeMenu = true; + cmm.execute(x, y); + grm.ui.activeMenu = false; + + this.repaint(); + + return true; + } + + /** + * Handles mouse wheel events and checks if a scrollbar is available. + * @param {number} step - The amount of scrolling that occurred on the mouse wheel. + */ + on_mouse_wheel(step) { + if (this.is_scrollbar_available) { + this.scrollbar.wheel(step); + } + } + // #endregion +} + + +//////////////////////// +// * ITEM CONTAINER * // +//////////////////////// +/** + * A class that represents a rectangular item with position and size properties. + * Provides a base for subclasses to implement methods for drawing, repainting, and tracing. + */ +class BaseListItem { + /** + * Creates the `BaseListItem` instance. + * Initializes the size and position of the item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @param {number} w - The width. + * @param {number} h - The height. + */ + constructor(x, y, w, h) { + /** + * Throttles a function to limit how often it can be invoked. This specific throttled function + * is used to repaint a rectangular area of a window at most once every frame at a rate of 60fps. + * @type {Function} + * @private + */ + this.throttled_repaint = Throttle(() => { + window.RepaintRect(this.x, this.y, this.w, this.h); + }, 1000 / 60); + + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Placeholder method for drawing the item. Subclasses should override this method. + * @abstract + * @param {GdiGraphics} gr - The GDI graphics object. + * @throws Will throw an error if the method is not overridden in a subclass. + */ + draw(gr) { + throw new LogicError('draw not implemented'); + } + + /** + * Sets the x-coordinate for the item. + * @param {number} x - The new x-coordinate. + */ + set_x(x) { + this.x = x; + } + + /** + * Sets the y-coordinate for the item. + * @param {number} y - The new y-coordinate. + */ + set_y(y) { + this.y = y; + } + + /** + * Sets the width for the item. + * @param {number} w - The new width. + */ + set_w(w) { + this.w = w; + } + + /** + * Invokes a throttled repaint to update the item's display on the window. + */ + repaint() { + this.throttled_repaint(); + } + + /** + * Checks if a given point (x, y) is within the boundaries of the item. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {boolean} True if the point is within the item's boundaries, false otherwise. + */ + trace(x, y) { + return x >= this.x && x < this.x + this.w && y >= this.y && y < this.y + this.h; + } + // #endregion +} + + +/////////////////////////// +// * CONTENT CONTAINER * // +/////////////////////////// +/** + * A class that represents an abstract content container that defines the interface + * for generating and updating item lists, as well as calculating the total height of the list in rows. + * Concrete subclasses must implement the abstract methods provided. + */ +class BaseListContent { + // * ABSTRACT METHODS * // + // #region ABSTRACT METHODS + /** + * Generates the item list to be drawn based on the current state of the list. + * This abstract method must be overridden by subclasses. + * Called in three cases: + * - 1. Window vertical size changed. + * - 2. Scroll position changed. + * - 3. BaseList cnt changed. + * @abstract + * @param {number} wy - The y-coordinate of the list. + * @param {number} wh - The height of the list. + * @param {number} row_shift - The shift in rows (shift_in_pixels/row_h) in the list. + * @param {number} pixel_shift - The shift in pixels (shift_in_pixels - row_shift) in the list. + * @param {number} row_h - The row height in the list. + * @returns {Array} An array of BaseListItem instances that represent the items to be drawn. + */ + generate_items_to_draw(wy, wh, row_shift, pixel_shift, row_h) { + throw new LogicError('generate_items_to_draw not implemented'); + } + + /** + * Sets a new width for the list items. + * This abstract method must be overridden by subclasses. + * @abstract + * @param {number} w - The new width for the items. + */ + update_items_w_size(w) { + throw new LogicError('update_items_w_size not implemented'); + } + + /** + * Calculates the total height of the list in rows. + * This abstract method must be overridden by subclasses. + * @abstract + * @returns {number} Total cnt height in rows, i.e. Total_h/row_h. + */ + calculate_total_h_in_rows() { + throw new LogicError('calculate_total_h_in_rows not implemented'); + } + // #endregion +} + + +/** + * A class that represents a list of rows as a basic content container. + * This concrete subclass of ListContent provides methods for generating and updating the items to draw, + * and for calculating the total height in rows. It is designed to contain only items with a uniform height of `row_h`. + * @augments {BaseListContent} + */ +class BaseListRowContent extends BaseListContent { + /** + * Creates the `BaseListRowContent` instance. + * @memberof BaseListRowContent + */ + constructor() { + super(); + + /** @protected @type {Array} The collection of list items. */ + this.rows = []; + } + + // * PUBLIC METHODS * // + // #region PUBLIC METHODS + /** + * Generates a list of items to draw based on the specified parameters. + * Implements the abstract method from the superclass ListContent. + * @param {number} wy - The starting y-coordinate of the drawing area. + * @param {number} wh - The height of the list where the items will be drawn. + * @param {number} row_shift - The index of the first row to start drawing from. + * @param {number} pixel_shift - The number of pixels to shift the starting position of the items vertically. + * @param {number} row_h - The height of each row in pixels. + * @returns {Array} An array of items to be drawn, called `items_to_draw`. + */ + generate_items_to_draw(wy, wh, row_shift, pixel_shift, row_h) { // Rewritten + if (!this.rows.length) { + return []; + } + + const items_to_draw = []; + const cur_x = PlaylistSetX(); + let cur_y = wy + pixel_shift; + + // Calculate the number of rows that can fit in the drawing area + const maxRows = Math.min(Math.floor((wh - pixel_shift) / row_h), this.rows.length - row_shift); + + for (let i = row_shift; i < row_shift + maxRows; ++i) { + this.rows[i].x = cur_x; + this.rows[i].y = cur_y; + items_to_draw.push(this.rows[i]); + cur_y += row_h; + } + + return items_to_draw; + } + + /** + * Updates the width of each item in the list of rows. + * Implements the abstract method from the superclass ListContent. + * @param {number} w - The new width for the items. + */ + update_items_w_size(w) { + for (const item of this.rows) { + item.set_w(w); + } + } + + /** + * Calculates the total number of rows in the list. + * Implements the abstract method from the superclass ListContent. + * @returns {number} The number of rows in the list. + */ + calculate_total_h_in_rows() { + return this.rows.length; + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-playlist.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-playlist.js new file mode 100644 index 00000000..ad9f15d4 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-playlist.js @@ -0,0 +1,1804 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +////////////////// +// * PLAYLIST * // +////////////////// +/** + * A class that draws the playlist rows and manages them. + * @augments {BaseList} + */ +class Playlist extends BaseList { + /** + * Creates the `Playlist` instance. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + */ + constructor(x, y) { + super(x, y, 0, 0, new PlaylistContent()); + + // * Constants + /** @private @type {number} */ + this.header_h_in_rows = this._calcHeaderRows(); + + // * Window state + /** @public @type {boolean} */ + this.was_on_size_called = false; + /** @public @type {boolean} */ + this.is_in_focus = false; + + // * Playback state + /** @public @type {number} */ + this.cur_playlist_idx = undefined; + /** @public @type {?PlaylistRow} */ + this.playing_item = undefined; + /** @public @type {?PlaylistRow} */ + this.focused_item = undefined; + + // * Mouse and key state + /** @public @type {boolean} */ + this.mouse_on_item = false; + /** @public @type {boolean} */ + this.key_down = false; + /** @public @type {boolean} */ + this.drag_event_invoked = false; + + // * Item events + /** @public @type {?PlaylistRow|?PlaylistBaseHeader|?BaseListItem} */ + this.last_hover_item = undefined; + /** @public @type {{x: ?number, y: ?number}} */ + this.last_pressed_coord = { + x: undefined, + y: undefined + }; + + // * Timers + /** @public @type {boolean} */ + this.drag_scroll_in_progress = false; + /** @private @type {number} */ + this.drag_scroll_timeout_timer = 0; + /** @private @type {number} */ + this.drag_scroll_repeat_timer = 0; + + // * Scrollbar props + /** @public @type {Array} float */ + this.scroll_pos_list = []; + + // * Objects + /** @public @type {?PlaylistImage} */ + this.header_artwork = new PlaylistImage(); + /** @public @type {?PlaylistMetaHandler} */ + this.meta_handler = new PlaylistMetaHandler(); + /** @public @type {?PlaylistKeyActionHandler} */ + this.key_handler = new PlaylistKeyActionHandler(); + /** @public @type {?PlaylistSelectionHandler} */ + this.selection_handler = undefined; + /** @public @type {?PlaylistQueueHandler} */ + this.queue_handler = undefined; + /** @public @type {?PlaylistCollapseHandler} */ + this.collapse_handler = undefined; + + /**@private @type {PlaylistContentNavigation} */ + this.cnt_helper = this.cnt.helper; + + /** + * Initializes and repaints the list with debouncing to prevent excessive calls. + * It is debounced to handle rapid invocation such as when swapping out playlist content + * which triggers the underlying method multiple times due to add/remove/changed callbacks. + * @param {Function} Debounce - The debouncing function to control invocation rate. + * @param {function(boolean): void} initialize_and_repaint_list - The function to initialize and repaint the list. + * @param {number} delay - The number of milliseconds to delay; here it's 10ms. + * @param {object} options - Configuration options for debouncing. + * @param {boolean} options.leading - If `true`, the function will be called on the leading edge of the timeout. + * @param {boolean} options.trailing - If `true`, the function will be called on the trailing edge of the timeout. + * @property {Function} debounced_initialize_and_repaint_list - The debounced version of initialize_and_repaint_list. + * @public + */ + this.debounced_initialize_and_repaint_list = Debounce((refocus) => { + this.initialize_and_repaint_list(refocus); + }, 10, { + leading: false, + trailing: true + }); + } + + // * PRIVATE METHODS * // + // #region PRIVATE METHODS + /** + * Initializes an array of rows based on the given playlist, where each row represents a playlist item. + * @param {FbMetadbHandleList} playlist_items - An object or array that can be converted to an array using the `Convert()` method. + * @returns {Array} An array of row objects. + * @private + */ + _initialize_rows(playlist_items) { // Rewritten + const playlist_items_array = playlist_items.Convert(); + const rows = new Array(playlist_items_array.length); + const showHeader = plSet.show_header; + + for (let i = 0; i < playlist_items_array.length; ++i) { + const row = new PlaylistRow(this.list_x, 0, this.list_w, this.row_h, playlist_items_array[i], i, this.cur_playlist_idx); + if (!showHeader) { + row.is_odd = !(i & 1); + } + rows[i] = row; + } + + return rows; + } + + /** + * Creates headers for the playlist based on the provided rows and metadata. + * @param {Array} rows - The rows of the playlist. + * @param {FbMetadbHandleList} rows_metadb - The metadb of the rows. + * @returns {Array} The result of calling the `PlaylistHeader.create_headers` function with the provided arguments. + * @private + */ + _create_headers(rows, rows_metadb) { + const prepared_rows = PlaylistHeader.prepare_initialization_data(rows, rows_metadb); + return PlaylistHeader.create_headers(/** @type {PlaylistContent} */ this.cnt, this.list_x, 0, this.list_w, this.row_h * this.header_h_in_rows, prepared_rows); + } + + /** + * Calculates the number of header rows based on the layout and font size setting. + * @returns {number} The number of rows to be displayed in the header section of the playlist. + * @private + */ + _calcHeaderRows() { + let numRows = plSet.use_compact_header ? plSet.rows_in_compact_header : plSet.rows_in_header; + const headerFontSize = grSet[`playlistHeaderFontSize_${grSet.layout}`]; + const rowFontSize = grSet[`playlistFontSize_${grSet.layout}`]; + const normalHeader = !plSet.use_compact_header; + const headerExceedsHeight = (headerFontSize * 2 + 3 + rowFontSize) > (numRows * plSet.row_h * 0.6); + + if (normalHeader && headerExceedsHeight) numRows++; + + return numRows; + } + + /** + * Determines the row and position (above or below) where an item should be dropped based on the mouse coordinates. + * @param {number} x - The x-coordinate. + * @param {number} y - The y-coordinate. + * @returns {{row: ?PlaylistRow, is_above: ?boolean}} An object with two properties: 'row', which is the playlist row (or null) + * where the item should be dropped, and 'is_above', which is a boolean indicating whether the item should be dropped above (true) + * or below (false) the row, or null if it cannot be determined. + * @private + */ + _get_drop_row_info(x, y) { + const drop_info = { + row: undefined, + is_above: undefined + }; + + let item = this.get_item_under_mouse(x, y); + if (!item) { + if (!this.trace_list(x, y) || !this.cnt.rows.length) { + return drop_info; + } + + item = Last(this.cnt.rows); + } + + const is_above = y < (item.y + item.h / 2); + + if (item instanceof PlaylistBaseHeader) { + const first_row_in_header = item.get_first_row(); + + if (is_above) { + if (first_row_in_header === this.cnt.rows[0]) { + drop_info.row = first_row_in_header; + drop_info.is_above = true; + } + else { + drop_info.row = this.cnt.rows[first_row_in_header.idx - 1]; + drop_info.is_above = false; + } + } + else { + drop_info.row = first_row_in_header; + drop_info.is_above = true; + } + } + else if (item instanceof PlaylistRow) { + if (is_above) { + drop_info.row = item; + drop_info.is_above = true; + } + else if (plSet.show_header && item.idx === Last(item.parent.sub_items).idx + || item === Last(this.cnt.rows)) { + drop_info.row = item; + drop_info.is_above = false; + } + else { + drop_info.row = this.cnt.rows[item.idx + 1]; + drop_info.is_above = true; + } + } + + return drop_info; + } + + /** + * Determines the row index of a target item in a drawn item list. + * Note: at worst has O (playlist_size) complexity. + * @param {PlaylistRow|PlaylistBaseHeader} target_item - The item that you want to find the row index for. It can be either a `PlaylistRow` object or a `PlaylistBaseHeader` object. + * @returns {number} The row index of the target item in the drawn item list. + * @private + */ + _get_item_draw_row_idx(target_item) { // Rewritten + let cur_row = 0; + + const iterate_level = (sub_items, target_item) => { + if (sub_items.length === 0) return false; + + const isHeader = sub_items[0] instanceof PlaylistBaseHeader; + const header_h_in_rows = isHeader ? Math.round(sub_items[0].h / this.row_h) : 0; + + for (let i = 0; i < sub_items.length; ++i) { + const item = sub_items[i]; + + if (item === target_item) { + return true; + } + + cur_row += isHeader ? header_h_in_rows : 1; + if (isHeader && !item.is_collapsed && iterate_level(item.sub_items, target_item)) { + return true; + } + } + return false; + }; + + const topLevelItems = plSet.show_header ? this.cnt.sub_items : this.cnt.rows; + if (!iterate_level(topLevelItems, target_item)) { + return 0; // Item not found, you could throw an error or handle it as per your requirements + } + + return cur_row; + } + + /** + * Determines the visibility state and the amount of invisibility of a given item in the playlist. + * @param {PlaylistRow|PlaylistBaseHeader} item_to_check - The item whose visibility is being determined. + * @returns {{visibility: PlaylistVisibilityState, invisible_part: number}} An object containing 'visibility', + * which is the visibility state of the item (none, partial_top, partial_bottom, or full), and 'invisible_part', + * which is the fraction of the item's height that is invisible due to scrolling (0 for fully visible, + * between 0 and 1 for partially visible, and 1 if completely invisible). + * @private + */ + _get_item_visibility_state(item_to_check) { + const item_state = { + visibility: PlaylistVisibilityState.none, + invisible_part: item_to_check.h / this.row_h + }; + + for (const item of this.items_to_draw) { + if (item === item_to_check) { + if (item.y < this.list_y && item.y + item.h > this.list_y) { + item_state.visibility = PlaylistVisibilityState.partial_top; + item_state.invisible_part = (this.list_y - item.y) / this.row_h; + } else if (item.y < this.list_y + this.list_h && item.y + item.h > this.list_y + this.list_h) { + item_state.visibility = PlaylistVisibilityState.partial_bottom; + item_state.invisible_part = ((item.y + item.h) - (this.list_y + this.list_h)) / this.row_h; + } else { + item_state.visibility = PlaylistVisibilityState.full; + item_state.invisible_part = 0; + } + break; + } + } + + return item_state; + } + + /** + * Sets the status of the last row based on whether the scrollbar is available and scrolled down. + * @private + */ + _set_rows_boundary_status() { + const last_row = Last(this.cnt.rows); + if (last_row) { + last_row.is_cropped = this.is_scrollbar_available ? this.scrollbar.is_scrolled_down : false; + } + } + // #endregion + + // * PUBLIC METHODS - GENERAL * // + // #region PUBLIC METHODS - GENERAL + /** + * Filters a drop effect based on the modifiers (Ctrl, Shift, Alt) pressed during the event. + * @param {number} effect - A bitmask that represents the available drop effects for a drag-and-drop action. + * @returns {number} Returns the filtered effect that can be one of the following: + * - Only link effect if Ctrl and Shift are pressed together or Alt is pressed alone. + * - Copy effect if Ctrl is pressed alone. + * - Move effect if Shift is pressed alone. + * - Move effect, then Copy effect, then Link effect if no modifiers are pressed. + */ + filter_effect_by_modifiers(effect) { + const ctrl_pressed = utils.IsKeyPressed(VK_CONTROL); + const shift_pressed = utils.IsKeyPressed(VK_SHIFT); + const alt_pressed = utils.IsKeyPressed(VK_MENU); + + if (ctrl_pressed && shift_pressed && !alt_pressed + || alt_pressed && !ctrl_pressed && !shift_pressed) { + // Link only + return (effect & PlaylistDropEffect.link); + } + + if (ctrl_pressed && !shift_pressed && !alt_pressed) { + // Copy (also via link) + return (effect & PlaylistDropEffect.copy) || (effect & PlaylistDropEffect.link); + } + + if (shift_pressed) { + // Move only + return (effect & PlaylistDropEffect.move); + } + + // Move > Copy > Link + return (effect & PlaylistDropEffect.move) || (effect & PlaylistDropEffect.copy) || (effect & PlaylistDropEffect.link); + } + + /** + * Collapses or expands the playlist header. + */ + header_auto_collapse() { + if (plSet.show_header && plSet.auto_collapse) { + this.collapse_handler.collapse_all_but_now_playing(); + if (this.collapse_handler.changed && grSet.playlistAutoScrollNowPlaying) { + this.scroll_to_now_playing_or_focused(); + } + } else { + this.collapse_handler.expand_all(); + } + } + + /** + * Collapses the playlist header. + */ + header_collapse() { + this.header_auto_collapse(); + } + + /** + * Expands the playlist header. + */ + header_expand() { + this.collapse_handler.expand_all(); + } + + /** + * Sets a hyperlink for the currently playing item if the current playlist index is "Search". + */ + hyperlink_set_now_playing() { + if (!fb.GetNowPlaying() || plman.GetPlaylistName(this.cur_playlist_idx) !== 'Search') { + return; + } + + const plIndex = plman.FindOrCreatePlaylist('Search', true); + const handles = plman.GetPlaylistItems(plIndex); + const index = handles.Find(fb.GetNowPlaying()); + + setTimeout(() => { // Wait for loadingThemeComplete + this.playing_item = this.cnt.rows[index]; + if (this.playing_item) { + this.playing_item.is_playing = true; + this.playing_item.clear_title_text(); + } + window.RepaintRect(this.x, this.y, this.w, this.h); + }, grm.ui.loadingThemeComplete); + } + + /** + * Callback for playlist init events. + */ + initialize() { + pl.plman.register_key_actions(this.key_handler); + this.register_key_actions(this.key_handler); + this.initialize_list(); + } + + /** + * Updates the playlist with the option to refocus on a specific playlist item. + * @param {boolean} refocus - Whether the playlist should be scrolled to the focused item after init. + */ + initialize_and_repaint_list(refocus) { + this.initialize_list(); + if (refocus) { + this.scroll_to_focused(); // Needed after drag-drop, because it might cause dropped (i.e. focused) item to be outside of drawn list + } + this.repaint(); + } + + /** + * Initializes and updates the playlist. + * Clearing its contents, initializing rows and headers and setting up various objects and handlers. + * This method does not contain any redraw calls, it's purely back-end. + */ + initialize_list() { + grm.ui.traceCall && console.log('initialize_list'); + const profiler = fb.CreateProfiler(); + const profiler_part = grm.ui.traceListPerformance && fb.CreateProfiler(); + + this.cur_playlist_idx = plman.ActivePlaylist; + + // * Clear contents + + this.cnt.rows = []; + this.cnt.sub_items = []; + + // * Initialize rows + + grm.ui.traceListPerformance && profiler_part.Reset(); + + const rows_metadb = plman.GetPlaylistItems(this.cur_playlist_idx); + this.cnt.rows = this._initialize_rows(plman.GetPlaylistItems(this.cur_playlist_idx)); + + grm.ui.traceListPerformance && console.log(`Rows initialized in ${profiler_part.Time}ms`); + + this.playing_item = undefined; + const playing_item_location = plman.GetPlayingItemLocation(); + if (playing_item_location.IsValid && playing_item_location.PlaylistIndex === this.cur_playlist_idx) { + this.playing_item = this.cnt.rows[playing_item_location.PlaylistItemIndex]; + this.playing_item.is_playing = true; + } + + this.focused_item = undefined; + const focus_item_idx = plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx); + if (focus_item_idx !== -1) { + this.focused_item = this.cnt.rows[focus_item_idx]; + this.focused_item.is_focused = true; + } + + // * Initialize headers + grm.ui.traceListPerformance && profiler_part.Reset(); + + PlaylistHeader.grouping_handler.set_active_playlist(plman.GetPlaylistName(this.cur_playlist_idx)); + this.cnt.sub_items = this._create_headers(this.cnt.rows, rows_metadb); + this.header_artwork.getHeaderArtwork(this.cnt.sub_items.slice(0, 10)); // Preload first 10 artworks + + grm.ui.traceListPerformance && console.log(`Headers initialized in ${profiler_part.Time}ms`); + + if (!this.was_on_size_called) { + // * First time init + this.collapse_handler = new PlaylistCollapseHandler(/** @type {PlaylistContent} */ this.cnt); + + if (plSet.show_header) { + if (plSet.collapse_on_start) { + this.collapse_handler.collapse_all(); + } + + this.collapse_handler.set_callback(() => { + this.on_list_items_change(); + }); + } + } + else { + // * Update list control + if (this.collapse_handler) { + this.collapse_handler.on_content_change(); + } + + if (plSet.show_header && plSet.auto_collapse) { + this.header_collapse(); + } + + this.scrollbar.stopScrolling(); + this.update_scrollbar(); + + this.on_list_items_change(); + } + + // * Initialize other objects + if (plSet.show_queue_position) { + this.queue_handler = new PlaylistQueueHandler(this.cnt.rows, this.cur_playlist_idx); + } + this.selection_handler = new PlaylistSelectionHandler(/** @type {PlaylistContent} */ this.cnt, this.cur_playlist_idx); + + this.hyperlink_set_now_playing(); + + (true || grm.ui.traceListPerformance) && console.log(`Playlist initialized in ${profiler.Time}ms`); + } + + /** + * Initializes and updates the scrollbar if it is visible. + * Used to update scrollbar colors. + */ + initScrollbar() { + if (this.is_scrollbar_visible) { + this.initialize_scrollbar(); + this.update_scrollbar(); + } + } + + /** + * Reinitializes and updates the content of the playlist. + */ + reinitialize() { + if (this.cur_playlist_idx !== plman.ActivePlaylist) { + plSet.scrollbar_pos = this.scroll_pos_list[plman.ActivePlaylist] == null ? 0 : this.scroll_pos_list[plman.ActivePlaylist]; + } + this.row_h = SCALE(plSet.row_h); + this.header_h_in_rows = this._calcHeaderRows(); + this.initialize_list(); + this.scroll_to_focused(); + } + + /** + * Registers key actions and handles key presses. + * @param {PlaylistKeyActionHandler} key_handler - The PlaylistKeyActionHandler object. + */ + register_key_actions(key_handler) { + key_handler.register_key_action(VK_UP, + (modifiers) => { + if (!this.cnt.rows.length) { + return; + } + + if (modifiers.ctrl && modifiers.shift) { + if (!this.selection_handler.has_selected_items()) { + return; + } + + this.selection_handler.move_selection_up(); + return; + } + + if (!this.focused_item) { + const top_item = this.items_to_draw[0]; + this.focused_item = top_item instanceof PlaylistRow ? top_item : top_item.get_first_row(); + } + + const visible_item = this.cnt_helper.is_item_visible(this.focused_item) ? this.focused_item : this.cnt_helper.get_visible_parent(this.focused_item); + let new_item = this.cnt_helper.get_navigable_neighbor(visible_item, -1); + if (!new_item) { + new_item = visible_item; + } + + this.selection_handler.update_selection(new_item, undefined, modifiers.shift); + this.repaint(); + }); + + key_handler.register_key_action(VK_DOWN, + (modifiers) => { + if (!this.cnt.rows.length) { + // Skip repaint + return; + } + + if (modifiers.ctrl && modifiers.shift) { + if (!this.selection_handler.has_selected_items()) { + return; + } + + this.selection_handler.move_selection_down(); + return; + } + + if (!this.focused_item) { + const top_item = this.items_to_draw[0]; + this.focused_item = top_item instanceof PlaylistRow ? top_item : top_item.get_first_row(); + } + + const visible_item = this.cnt_helper.is_item_visible(this.focused_item) ? this.focused_item : this.cnt_helper.get_visible_parent(this.focused_item); + let new_item = this.cnt_helper.get_navigable_neighbor(visible_item, 1); + if (!new_item) { + new_item = visible_item; + } + + this.selection_handler.update_selection(new_item, undefined, modifiers.shift); + this.repaint(); + }); + + key_handler.register_key_action(VK_LEFT, + (modifiers) => { + if (!this.collapse_handler || !this.cnt.rows.length) { + return; + } + + if (!this.focused_item) { + const top_item = this.items_to_draw[0]; + this.focused_item = top_item instanceof PlaylistRow ? top_item : top_item.get_first_row(); + } + /** @type {PlaylistBaseHeader|PlaylistRow} */ + let new_focus = this.focused_item; + + // Get top uncollapsed header + let visible_header = this.cnt_helper.get_visible_parent(this.focused_item); + if (visible_header) { + while (visible_header.parent instanceof PlaylistBaseHeader && visible_header.is_collapsed) { + visible_header = visible_header.parent; + } + + this.collapse_handler.collapse(visible_header); + new_focus = visible_header; + } + + this.selection_handler.update_selection(new_focus); + this.repaint(); + }); + + key_handler.register_key_action(VK_RIGHT, + (modifiers) => { + if (!this.collapse_handler || !this.cnt.rows.length) { + return; + } + + if (!this.focused_item) { + const top_item = this.items_to_draw[0]; + this.focused_item = top_item instanceof PlaylistRow ? top_item : top_item.get_first_row(); + } + /** @type {PlaylistBaseHeader|PlaylistRow} */ + let new_focus = this.focused_item; + + const visible_header = this.cnt_helper.get_visible_parent(this.focused_item); + const new_focus_item = visible_header.get_first_row(); + + this.collapse_handler.expand(visible_header); + if (this.collapse_handler.changed) { + this.scroll_to_row(this.focused_item, new_focus_item); + new_focus = new_focus_item; + } + + this.selection_handler.update_selection(new_focus); + this.repaint(); + }); + + key_handler.register_key_action(VK_PRIOR, + (modifiers) => { + if (!this.cnt.rows.length) { + return; + } + + if (!this.focused_item) { + const top_item = this.items_to_draw[0]; + this.focused_item = top_item instanceof PlaylistRow ? top_item : top_item.get_first_row(); + } + + let new_focus_item; + if (this.is_scrollbar_available) { + new_focus_item = this.items_to_draw[0]; + if (!this.cnt_helper.is_item_navigable(new_focus_item)) { + new_focus_item = this.cnt_helper.get_navigable_neighbor(new_focus_item, 1); + } + if (new_focus_item.y < this.list_y && new_focus_item.y + new_focus_item.h > this.list_y) { + new_focus_item = this.cnt_helper.get_navigable_neighbor(new_focus_item, 1); + } + if (new_focus_item === this.focused_item) { + this.scrollbar.shift_page(-1); + + new_focus_item = this.items_to_draw[0]; + if (!this.cnt_helper.is_item_navigable(new_focus_item)) { + new_focus_item = this.cnt_helper.get_navigable_neighbor(new_focus_item, 1); + } + } + } + else { + new_focus_item = this.items_to_draw[0]; + } + + this.selection_handler.update_selection(new_focus_item, undefined, modifiers.shift); + this.repaint(); + }); + + key_handler.register_key_action(VK_NEXT, + (modifiers) => { + if (!this.cnt.rows.length) { + return; + } + + if (!this.focused_item) { + this.focused_item = this.items_to_draw[0]; + if (!this.cnt_helper.is_item_navigable(this.focused_item)) { + // @ts-ignore + this.focused_item = this.cnt_helper.get_navigable_neighbor(this.focused_item, 1); + } + } + + let new_focus_item; + if (this.is_scrollbar_available) { + new_focus_item = Last(this.items_to_draw); + if (!this.cnt_helper.is_item_navigable(new_focus_item)) { + new_focus_item = this.cnt_helper.get_navigable_neighbor(new_focus_item, -1); + } + if (new_focus_item.y < this.list_y + this.list_h && new_focus_item.y + new_focus_item.h > this.list_y + this.list_h) { + new_focus_item = this.cnt_helper.get_navigable_neighbor(new_focus_item, -1); + } + if (new_focus_item === this.focused_item) { + this.scrollbar.shift_page(1); + new_focus_item = Last(this.items_to_draw); + if (!this.cnt_helper.is_item_navigable(new_focus_item)) { + new_focus_item = this.cnt_helper.get_navigable_neighbor(new_focus_item, -1); + } + } + } + else { + new_focus_item = Last(this.items_to_draw); + } + + this.selection_handler.update_selection(new_focus_item, undefined, modifiers.shift); + this.repaint(); + }); + + key_handler.register_key_action(VK_HOME, + (modifiers) => { + this.selection_handler.update_selection(this.cnt.rows[0], undefined, modifiers.shift); + this.scrollbar.scroll_to_start(); + }); + + key_handler.register_key_action(VK_END, + (modifiers) => { + this.selection_handler.update_selection(Last(this.cnt.rows), undefined, modifiers.shift); + this.scrollbar.scroll_to_end(); + }); + + key_handler.register_key_action(VK_DELETE, + (modifiers) => { + if (!this.selection_handler.has_selected_items() && this.focused_item) { + this.selection_handler.update_selection(this.focused_item); + } + plman.UndoBackup(this.cur_playlist_idx); + plman.RemovePlaylistSelection(this.cur_playlist_idx); + }); + + key_handler.register_key_action(VK_KEY_A, + (modifiers) => { + if (modifiers.ctrl) { + this.selection_handler.select_all(); + this.repaint(); + } + }); + + key_handler.register_key_action(VK_KEY_F, + (modifiers) => { + if (modifiers.ctrl) { + fb.RunMainMenuCommand('Edit/Search'); + } + else if (modifiers.shift) { + fb.RunMainMenuCommand('Library/Search'); + } + }); + + key_handler.register_key_action(VK_RETURN, + (modifiers) => { + if (!this.focused_item) { // Needed to reinit lost focus to prevent crash, e.g from 3rd party components using their own window + const top_item = this.items_to_draw[0]; + this.focused_item = top_item instanceof PlaylistRow ? top_item : top_item.get_first_row(); + } + plman.ExecutePlaylistDefaultAction(this.cur_playlist_idx, this.focused_item.idx); + }); + + key_handler.register_key_action(VK_KEY_O, + (modifiers) => { + if (modifiers.shift) { + fb.RunContextCommandWithMetadb('Open Containing Folder', this.focused_item.metadb); + } + }); + + key_handler.register_key_action(VK_KEY_Q, + (modifiers) => { + if (!this.queue_handler) { + return; + } + + if (modifiers.ctrl && modifiers.shift) { + this.queue_handler.flush(); + } + else if (this.selection_handler.selected_items_count() >= 1) { + const rows = this.cnt.rows; + if (modifiers.ctrl) { + const indexes = this.selection_handler.get_selected_items(); + for (const idx of indexes) { + this.queue_handler.add_row(rows[idx]); + } + } + else if (modifiers.shift) { + const indexes = this.selection_handler.get_selected_items(); + for (const idx of indexes) { + this.queue_handler.remove_row(rows[idx]); + } + } + } + }); + + key_handler.register_key_action(VK_F5, + (modifiers) => { + PlaylistHeader.img_cache.clear(); + this.initialize_and_repaint_list(true); + }); + + key_handler.register_key_action(VK_KEY_C, + (modifiers) => { + if (modifiers.ctrl) { + this.selection_handler.copy(); + } + }); + + key_handler.register_key_action(VK_KEY_X, + (modifiers) => { + if (modifiers.ctrl) { + this.selection_handler.cut(); + } + }); + + key_handler.register_key_action(VK_KEY_V, + (modifiers) => { + if (modifiers.ctrl && !plman.IsPlaylistLocked(this.cur_playlist_idx)) { + this.selection_handler.paste(); + } + }); + } + + /** + * Handles when playlist content has changed, i.e playlist items were added, reordered or removed. + * Sets the rows boundary status and fetches album art for playlist headers. + * @override + * @protected + */ + on_content_to_draw_change() { + this._set_rows_boundary_status(); + // @ts-ignore + BaseList.prototype.on_content_to_draw_change.apply(this); + if (plSet.show_album_art && !plSet.use_compact_header) { + this.header_artwork.getAlbumArt(this.items_to_draw); + } + } + + /** + * Updates the scroll position of a playlist and redraws the scrollbar. + * @override + * @protected + */ + scrollbar_redraw_callback() { + this.scroll_pos_list[this.cur_playlist_idx] = Math.round(this.scrollbar.scroll * 1e2) / 1e2; + // @ts-ignore + BaseList.prototype.scrollbar_redraw_callback.apply(this); + } + + /** + * Iterates through all playlist rows and updates the title color of each item. + * Used when updating title color in Reborn/Random theme. + */ + title_color_change() { + for (let i = 0; i < this.cnt.rows.length; i++) { + const item = this.cnt.rows[i]; + item.update_title_color(); + } + } + // #endregion + + // * PUBLIC METHODS - SCROLLING * // + // #region PUBLIC METHODS - SCROLLING + /** + * Scrolls the playlist to the current now playing track after some checks. + */ + show_now_playing() { + const playing_item_location = plman.GetPlayingItemLocation(); + if (!playing_item_location.IsValid) { + return; + } + + if (playing_item_location.PlaylistIndex !== this.cur_playlist_idx) { + plman.ActivePlaylist = playing_item_location.PlaylistIndex; + this.initialize_list(); + } + else if (this.playing_item && this.collapse_handler) { + this.collapse_handler.expand(this.playing_item.parent); + } + + this.selection_handler.update_selection(this.cnt.rows[playing_item_location.PlaylistItemIndex]); + + this.scroll_to_now_playing(); + } + + /** + * Scrolls the playlist to the current now playing track or focused item. + */ + scroll_to_now_playing_or_focused() { + if (this.playing_item) { + this.scroll_to_row(null, this.playing_item); + } + else if (this.focused_item) { + this.scroll_to_row(null, this.focused_item); + } + } + + /** + * Scrolls the playlist to the focused item or current now playing track. + */ + scroll_to_focused_or_now_playing() { + if (this.focused_item) { + this.scroll_to_row(null, this.focused_item); + } + else if (fb.CursorFollowPlayback && this.playing_item) { + this.scroll_to_row(null, this.playing_item); + } + } + + /** + * Scrolls the playlist to the focused item. + */ + scroll_to_focused() { + if (this.focused_item && !grm.ui.displayLibrarySplit()) { + this.scroll_to_row(null, this.focused_item); + } + } + + /** + * Scrolls the playlist to the current now playing track. + */ + scroll_to_now_playing() { + if (this.playing_item) { + this.scroll_to_row(null, this.playing_item); + } + } + + /** + * Scrolls the playlist to a specific row, taking into account the visibility state of the row. + * @param {?PlaylistRow} from_row - The starting row from which to scroll. + * @param {PlaylistRow} to_row - The row index to which to scroll. + * @throws {ArgumentError} If an invalid visibility state is encountered. + */ + scroll_to_row(from_row, to_row) { + if (!this.is_scrollbar_available) { + return; + } + + const has_headers = plSet.show_header; + + const visible_to_item = this.cnt_helper.is_item_visible(to_row) ? to_row : this.cnt_helper.get_visible_parent(to_row); + const to_item_state = this._get_item_visibility_state(visible_to_item); + + let shifted_successfully = false; + switch (to_item_state.visibility) { + case PlaylistVisibilityState.none: { + if (!from_row) { + break; + } + + const visible_from_item = this.cnt_helper.is_item_visible(from_row) ? from_row : this.cnt_helper.get_visible_parent(from_row); + + const from_item_state = this._get_item_visibility_state(visible_from_item); + if (from_item_state.visibility === PlaylistVisibilityState.none) { + break; + } + + const direction = (to_row.idx - from_row.idx) > 0 ? 1 : -1; + let scroll_shift = 0; + let neighbor_item = visible_from_item; + do { + const neighbor_item_state = this._get_item_visibility_state(neighbor_item); + scroll_shift += neighbor_item_state.invisible_part; + neighbor_item = direction > 0 ? this.cnt_helper.get_next_visible_item(neighbor_item) : this.cnt_helper.get_prev_visible_item(neighbor_item); + } while (neighbor_item && !this.cnt_helper.is_item_navigable(neighbor_item)); + + Assert(neighbor_item != null, LogicError, 'Failed to get navigable neighbor'); + + if (visible_to_item !== neighbor_item) { + // I.e. to_item and from_item are not neighbors + break; + } + + scroll_shift += visible_to_item.h / this.row_h; + + this.scrollbar.smooth_scroll_to(plSet.scrollbar_pos + direction * scroll_shift); + shifted_successfully = true; + break; + } + case PlaylistVisibilityState.partial_top: { + if (to_item_state.invisible_part % 1 > 0) { + this.scrollbar.shift_line(-1); + } + this.scrollbar.smooth_scroll_to(plSet.scrollbar_pos - Math.floor(to_item_state.invisible_part)); + shifted_successfully = true; + break; + } + case PlaylistVisibilityState.partial_bottom: { + if (to_item_state.invisible_part % 1 > 0) { + this.scrollbar.shift_line(1); + } + this.scrollbar.smooth_scroll_to(plSet.scrollbar_pos + Math.floor(to_item_state.invisible_part)); + shifted_successfully = true; + break; + } + case PlaylistVisibilityState.full: { + shifted_successfully = true; + break; + } + default: { + throw new ArgumentError('visibility_state', to_item_state.visibility); + } + } + + if (shifted_successfully) { + if (has_headers) { + let scroll_shift = 0; + let top_item = visible_to_item; + while (top_item.parent && top_item.parent instanceof PlaylistBaseHeader && top_item === top_item.parent.sub_items[0]) { + top_item = top_item.parent; + const header_state = this._get_item_visibility_state(top_item); + scroll_shift += header_state.invisible_part; + } + this.scrollbar.smooth_scroll_to(plSet.scrollbar_pos - scroll_shift); + } + } + else { + const item_draw_idx = this._get_item_draw_row_idx(visible_to_item); + const new_scroll_pos = Math.max(0, item_draw_idx - Math.floor(this.rows_to_draw_precise / 2)); + if (!pl.history_used) { + this.scrollbar.smooth_scroll_to(new_scroll_pos); + } + } + } + + /** + * Starts the drag scroll action when reordering playlist items. + * @param {string} key - The string value of 'up' or 'down'. + */ + start_drag_scroll(key) { + if (this.drag_scroll_timeout_timer) { + return; + } + + this.drag_scroll_timeout_timer = setTimeout(() => { + if (this.drag_scroll_repeat_timer) { + return; + } + + this.drag_scroll_repeat_timer = setInterval(() => { + if (!this.scrollbar.in_sbar && !this.selection_handler.is_dragging() || !this.drag_scroll_timeout_timer) { + return; + } + + this.drag_scroll_in_progress = true; + + let cur_marked_item; + if (key === 'up') { + this.scrollbar.shift_line(-1); + this.scrollbar.start_shift_timer(-1); + + cur_marked_item = this.items_to_draw[0]; + if (cur_marked_item instanceof PlaylistBaseHeader) { + this.collapse_handler.expand(cur_marked_item); + if (this.collapse_handler.changed) { + this.scrollbar.scroll_to(plSet.scrollbar_pos + cur_marked_item.get_sub_items_total_h_in_rows()); + } + + cur_marked_item = cur_marked_item.get_first_row(); + } + + this.selection_handler.drag(cur_marked_item, true); + cur_marked_item.is_drop_boundary_reached = true; + } + else if (key === 'down') { + this.scrollbar.shift_line(1); + this.scrollbar.start_shift_timer(1); + + cur_marked_item = Last(this.items_to_draw); + if (cur_marked_item instanceof PlaylistBaseHeader) { + this.collapse_handler.expand(cur_marked_item); + if (this.collapse_handler.changed) { + this.repaint(); + } + + cur_marked_item = this.cnt.rows[cur_marked_item.get_first_row().idx - 1]; + } + + this.selection_handler.drag(cur_marked_item, false); + cur_marked_item.is_drop_boundary_reached = true; + } + else { + throw new ArgumentError('drag_scroll_command', key); + } + + if (this.scrollbar.is_scrolled_down || this.scrollbar.is_scrolled_up) { + this.stop_drag_scroll(); + } + }, 50); + }, 350); + } + + /** + * Stops the drag scroll action when dropping reordered playlist items. + */ + stop_drag_scroll() { + if (this.drag_scroll_repeat_timer) { + clearInterval(this.drag_scroll_repeat_timer); + } + if (this.drag_scroll_timeout_timer) { + clearTimeout(this.drag_scroll_timeout_timer); + } + + this.drag_scroll_timeout_timer = undefined; + this.drag_scroll_repeat_timer = undefined; + this.drag_scroll_in_progress = false; + this.scrollbar.stop_shift_timer(); + } + // #endregion + + // * PUBLIC METHODS - CONTEXT MENU * // + // #region PUBLIC METHODS - CONTEXT MENU + /** + * Appends the playlist tools menu to the parent menu. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_pltools(parent_menu) { + const pltools = new ContextMenu('Playlist tools'); + pltools.appendItem('Playlist manager \tCtrl+M', () => { + fb.RunMainMenuCommand('View/Playlist Manager'); + }); + pltools.appendItem('Playlist search \tCtrl+F', () => { + fb.RunMainMenuCommand('View/Playlist search'); + }); + pltools.separator(); + + pltools.appendItem('Create new playlist \tCtrl+N', () => { + plman.CreatePlaylist(plman.PlaylistCount, ''); + plman.ActivePlaylist = plman.PlaylistCount; + }); + + const autopl = new ContextMenu('Create new auto playlist'); + pltools.append(autopl); + autopl.appendItem('Custom auto playlist', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) New auto playlist', '', '', 0); + plman.ActivePlaylist = plman.PlaylistCount; + plman.ShowAutoPlaylistUI(plman.PlaylistCount); + }); + autopl.separator(); + autopl.appendItem('Tracks from the library', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks from the library', 'ALL', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.separator(); + autopl.appendItem('Tracks most played', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks most played', '%play_count% GREATER 9', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks never played', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks never played', '%play_count% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks played in the last week', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks played in the last week', '%last_played% DURING LAST 1 WEEK', '%last_played%', 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks played in the last month', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks played in the last month', '%last_played% DURING LAST 4 WEEKS', '%last_played%', 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks played in the last year', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks played in the last year', '%last_played% DURING LAST 52 WEEKS', '%last_played%', 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.separator(); + autopl.appendItem('Tracks unrated', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks unrated', '%rating% MISSING', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks rated 1', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 1', '%rating% IS 1', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks rated 2', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 2', '%rating% IS 2', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks rated 3', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 3', '%rating% IS 3', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks rated 4', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 4', '%rating% IS 4', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.appendItem('Tracks rated 5', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Tracks rated 5', '%rating% IS 5', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + autopl.separator(); + autopl.appendItem('Loved tracks', () => { + plman.CreateAutoPlaylist(plman.PlaylistCount, '(Auto) Loved tracks', '%mood% GREATER 0', "%album artist% | $if(%album%,%date%,'9999') | %album% | %discnumber% | %tracknumber% | %title%", 0); + plman.ActivePlaylist = plman.PlaylistCount; + }); + pltools.separator(); + + pltools.appendItem('Save playlist \tCtrl+S', () => { + fb.RunMainMenuCommand('File/Save playlist...'); + }); + pltools.appendItem('Load playlist', () => { + fb.RunMainMenuCommand('File/Load playlist...'); + }); + const isAutoPl = !plman.PlaylistCount ? '' : plman.IsAutoPlaylist(this.cur_playlist_idx); + const isLocked = !plman.PlaylistCount ? '' : plman.IsPlaylistLocked(this.cur_playlist_idx); + pltools.appendItem(isLocked ? isAutoPl ? 'Unlock playlist (N/A for auto playlists)' : 'Unlock playlist' : 'Lock playlist', () => { + if (isLocked && !isAutoPl) { + plman.SetPlaylistLockedActions(this.cur_playlist_idx, null); + } else if (!isAutoPl) { + plman.SetPlaylistLockedActions(this.cur_playlist_idx, ['ExecuteDefaultAction']); + } + }, { is_grayed_out: isAutoPl }); + parent_menu.append(pltools); + } + + /** + * Appends the playlist edit menu to the parent menu based on the presence of selected items and data in the clipboard. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_edit(parent_menu) { + const has_selected_item = this.selection_handler.has_selected_items(); + const is_playlist_locked = !plman.PlaylistCount ? '' : plman.IsPlaylistLocked(this.cur_playlist_idx); + // Check only for data presence, since parsing it's contents might take a while + const has_data_in_clipboard = fb.CheckClipboardContents(); + if (has_selected_item || has_data_in_clipboard) { + if (!parent_menu.empty()) { + parent_menu.separator(); + } + + if (has_selected_item) { + parent_menu.appendItem('Cut', () => { + this.selection_handler.cut(); + }, { is_grayed_out: !has_selected_item }); + + parent_menu.appendItem('Copy', () => { + this.selection_handler.copy(); + }, { is_grayed_out: !has_selected_item }); + } + + if (has_data_in_clipboard) { + parent_menu.appendItem('Paste', () => { + this.selection_handler.paste(); + }, { is_grayed_out: !has_data_in_clipboard || is_playlist_locked }); + } + } + + if (has_selected_item) { + if (!parent_menu.empty()) { + parent_menu.separator(); + } + + parent_menu.appendItem('Remove', () => { + plman.RemovePlaylistSelection(this.cur_playlist_idx); + }, { is_grayed_out: is_playlist_locked }); + } + } + + /** + * Appends the playlist collapse menu to the parent menu. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_collapse(parent_menu) { + const ce = new ContextMenu('Collapse/Expand'); + parent_menu.append(ce); + + ce.appendItem('Collapse all', () => { + if (plSet.show_header) { + this.collapse_handler.collapse_all(); + if (this.collapse_handler.changed) { + this.scroll_to_focused_or_now_playing(); + } + } + }); + + if (plman.ActivePlaylist === plman.PlayingPlaylist && plSet.show_header) { + ce.appendItem('Collapse all but now playing', () => { + this.collapse_handler.collapse_all_but_now_playing(); + if (this.collapse_handler.changed) { + this.scroll_to_now_playing_or_focused(); + } + }); + } + + ce.appendItem('Expand all', () => { + if (plSet.show_header) { + this.collapse_handler.expand_all(); + if (this.collapse_handler.changed) { + this.scroll_to_focused_or_now_playing(); + } + } + }); + + ce.separator(); + + ce.appendItem('Auto', () => { + plSet.auto_collapse = !plSet.auto_collapse; + if (plSet.show_header && plSet.auto_collapse) { + this.collapse_handler.collapse_all_but_now_playing(); + if (this.collapse_handler.changed) { + this.scroll_to_now_playing_or_focused(); + } + } else { + this.collapse_handler.expand_all(); + } + }, { is_checked: plSet.auto_collapse }); + + ce.appendItem('Collapse on start', () => { + plSet.collapse_on_start = !plSet.collapse_on_start; + }, { is_checked: plSet.collapse_on_start }); + + ce.appendItem('Collapse on playlist switch', () => { + plSet.collapse_on_playlist_switch = !plSet.collapse_on_playlist_switch; + }, { is_checked: plSet.collapse_on_playlist_switch }); + } + + /** + * Appends the playlist appearance menu to the parent menu allowing to customize the appearance of the playlist. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_appearance(parent_menu) { + const appear = new ContextMenu('Appearance'); + parent_menu.append(appear); + + PlaylistManager.append_playlist_info_visibility_context_menu_to(appear); + + this.append_scrollbar_visibility_context_menu_to(appear); + + appear.appendItem('Show artist name on difference', () => { + grSet.showDifferentArtist = !grSet.showDifferentArtist; + + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: grSet.showDifferentArtist }); + + appear.appendItem('Show artist name in all rows', () => { + grSet.showArtistPlaylistRows = !grSet.showArtistPlaylistRows; + + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: grSet.showArtistPlaylistRows }); + + appear.appendItem('Show album title in all rows', () => { + grSet.showAlbumPlaylistRows = !grSet.showAlbumPlaylistRows; + + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: grSet.showAlbumPlaylistRows }); + + appear.appendItem('Show group header', () => { + plSet.show_header = !plSet.show_header; + if (plSet.show_header) { + this.collapse_handler = new PlaylistCollapseHandler(/** @type {PlaylistContent} */ this.cnt); + this.collapse_handler.expand_all(); + this.collapse_handler.set_callback(() => { + this.on_list_items_change(); + }); + } + else { + this.collapse_handler.expand_all(); + // this.collapse_handler = null; + } + + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.show_header }); + + if (plSet.show_header) { + const appear_header = new ContextMenu('Headers'); + appear.append(appear_header); + + appear_header.appendItem('Use compact group header', () => { + plSet.use_compact_header = !plSet.use_compact_header; + this.header_h_in_rows = this._calcHeaderRows(); + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.use_compact_header }); + + appear_header.appendItem('Show disc sub-header', () => { + plSet.show_disc_header = !plSet.show_disc_header; + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.show_disc_header }); + + appear_header.appendItem('Show rating', () => { + plSet.show_rating_header = !plSet.show_rating_header; + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.show_rating_header }); + + appear_header.appendItem('Show PLR value', () => { + plSet.show_PLR_header = !plSet.show_PLR_header; + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.show_PLR_header }); + + if (!plSet.use_compact_header) { + appear_header.appendItem('Show group info', () => { + plSet.show_group_info = !plSet.show_group_info; + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.show_group_info }); + + const art = new ContextMenu('Album art'); + appear_header.append(art); + + art.appendItem('Show', () => { + plSet.show_album_art = !plSet.show_album_art; + if (plSet.show_album_art) { + this.header_artwork.getAlbumArt(this.items_to_draw); + } + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, { is_checked: plSet.show_album_art }); + + art.appendItem('Auto-hide when no cover', () => { + plSet.auto_album_art = !plSet.auto_album_art; + if (plSet.show_album_art) { + this.header_artwork.getAlbumArt(this.items_to_draw); + } + this.initialize_list(); + this.scroll_to_focused_or_now_playing(); + }, + { + is_checked: plSet.auto_album_art, + is_grayed_out: !plSet.show_album_art + } + ); + } + } + + const appear_row = new ContextMenu('Rows'); + appear.append(appear_row); + + appear_row.appendItem('Alternate row color', () => { + plSet.show_row_stripes = !plSet.show_row_stripes; + }, { is_checked: plSet.show_row_stripes }); + + appear_row.appendItem('Show play count', () => { + plSet.show_playcount = !plSet.show_playcount; + }, { is_checked: plSet.show_playcount }); + + appear_row.appendItem('Show queue position', () => { + plSet.show_queue_position = !plSet.show_queue_position; + this.queue_handler = plSet.show_queue_position ? new PlaylistQueueHandler(this.cnt.rows, this.cur_playlist_idx) : undefined; + }, { is_checked: plSet.show_queue_position }); + + appear_row.appendItem('Show rating', () => { + plSet.show_rating = !plSet.show_rating; + }, { is_checked: plSet.show_rating }); + + appear_row.appendItem('Show PLR value', () => { + plSet.show_PLR = !plSet.show_PLR; + }, { is_checked: plSet.show_PLR }); + } + + /** + * Appends the playlist sort menu to a parent menu allowing to sort items in the playlist. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_sort(parent_menu) { + const has_multiple_selected_items = this.selection_handler.selected_items_count() > 1; + const is_auto_playlist = plman.IsAutoPlaylist(this.cur_playlist_idx); + + const sort = new ContextMenu(has_multiple_selected_items ? 'Sort selection' : 'Sort', + { is_grayed_out: is_auto_playlist } + ); + parent_menu.append(sort); + + sort.appendItem('Always auto-sort', () => { + grSet.playlistSortOrderAuto = !grSet.playlistSortOrderAuto; + }, { is_checked: grSet.playlistSortOrderAuto }); + sort.separator(); + + sort.appendItem('Sort by...', () => { + if (has_multiple_selected_items) { + fb.RunMainMenuCommand('Edit/Selection/Sort/Sort by...'); + } + else { + fb.RunMainMenuCommand('Edit/Sort/Sort by...'); + } + }); + + sort.separator(); + + const sortOrder = [ + ['Default', 'default'], + ['Artist | date', 'artistDate'], + ['Album', 'albumTitle'], + ['Album rating', 'albumRating'], + ['Album playcount', 'albumPlaycount'], + ['Track', 'trackTitle'], + ['Track number', 'trackNumber'], + ['Track rating', 'trackRating'], + ['Track playcount', 'trackPlaycount'], + ['Year', 'year'], + ['Genre', 'genre'], + ['Label', 'label'], + ['Country', 'country'], + ['File path', 'filePath'], + ['Custom', 'custom'] + ]; + + const sortOrderDirection = [ + ['Order by ascending', '_asc'], + ['Order by descending', '_dsc'] + ]; + + const setSorting = () => { + grm.ui.setPlaylistSortOrder(); + pl.call.on_size(grm.ui.ww, grm.ui.wh); + RepaintWindow(); + }; + + for (const direction of sortOrderDirection) { + const savedOrder = grSet.playlistSortOrder.slice(0, -4); + const sortOrderWithDirection = ['artistDate', 'albumRating', 'albumPlaycount', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country'].includes(savedOrder); + sort.appendItem(direction[0], () => { + grSet.playlistSortOrderDirection = direction[1]; + grSet.playlistSortOrder = sortOrderWithDirection ? `${savedOrder}${grSet.playlistSortOrderDirection}` : savedOrder; + setSorting(); + }, { + is_radio_checked: grSet.playlistSortOrderDirection === direction[1], + is_grayed_out: !sortOrderWithDirection + }); + } + + sort.separator(); + + for (const item of sortOrder) { + const sortOrderWithDirection = ['artistDate', 'albumRating', 'albumPlaycount', 'trackRating', 'trackPlaycount', 'year', 'genre', 'label', 'country'].includes(item[1]); + sort.appendItem(item[0], ((order) => { + const savedDirection = grSet.playlistSortOrderDirection; + grSet.playlistSortOrder = sortOrderWithDirection ? `${order}${savedDirection}` : order; + if (grSet.playlistSortOrder === 'custom') grm.inputBox.playlistSortCustom(); + setSorting(); + }).bind(null, item[1]), { + is_radio_checked: grSet.playlistSortOrderAuto && + (sortOrderWithDirection && item[1] === grSet.playlistSortOrder.slice(0, -4) || + !sortOrderWithDirection && item[1] === grSet.playlistSortOrder) + }); + } + + sort.separator(); + + sort.appendItem('Randomize', () => { + if (has_multiple_selected_items) { + fb.RunMainMenuCommand('Edit/Selection/Sort/Randomize'); + } + else { + fb.RunMainMenuCommand('Edit/Sort/Randomize'); + } + }); + + sort.appendItem('Reverse', () => { + if (has_multiple_selected_items) { + fb.RunMainMenuCommand('Edit/Selection/Sort/Reverse'); + } + else { + fb.RunMainMenuCommand('Edit/Sort/Reverse'); + } + }); + + sort.separator(); + + sort.appendItem('Save', () => { + fb.RunMainMenuCommand('File/Save playlist...'); + }); + + sort.appendItem('Load', () => { + fb.RunMainMenuCommand('File/Load playlist...'); + }); + + sort.appendItem('Undo', () => { + fb.RunMainMenuCommand('Edit/Undo'); + }); + } + + /** + * Appends the playlist weblinks menu to the parent menu, allowing the user to open various websites. + * @param {ContextMenu} parent_menu - The parent menu to append to. + * @param {FbMetadbHandle} metadb - The metadb of the track. + */ + ctx_menu_weblinks(parent_menu, metadb) { + const web = new ContextMenu('Weblinks'); + parent_menu.append(web); + + const web_links = [ + ['Google', 'google'], + ['Google Images', 'googleImages'], + ['Wikipedia', 'wikipedia'], + ['YouTube', 'youTube'], + ['Last.fm', 'lastFM'], + ['Discogs', 'discogs'], + ['MusicBrainz', 'musicbrainz'] + ]; + + for (const item of web_links) { + web.appendItem(item[0], ((url) => { + grm.utils.link(url, metadb); + }).bind(null, item[1])); + } + } + + /** + * Appends the "Send selection" menu to the parent menu, allowing to perform various actions on selected items in a playlist. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_send(parent_menu) { + const send = new ContextMenu('Send selection'); + parent_menu.append(send); + + send.appendItem('To top', () => { + plman.UndoBackup(this.cur_playlist_idx); + plman.MovePlaylistSelection(this.cur_playlist_idx, -plman.GetPlaylistFocusItemIndex(this.cur_playlist_idx)); + }); + + send.appendItem('To bottom', () => { + plman.UndoBackup(this.cur_playlist_idx); + plman.MovePlaylistSelection(this.cur_playlist_idx, plman.PlaylistItemCount(this.cur_playlist_idx) - plman.GetPlaylistSelectedItems(this.cur_playlist_idx).Count); + }); + + send.separator(); + + send.appendItem('Create New Playlist \tCtrl+N', () => { + plman.CreatePlaylist(plman.PlaylistCount, ''); + plman.InsertPlaylistItems(plman.PlaylistCount, 0, plman.GetPlaylistSelectedItems(this.cur_playlist_idx), true); + }); + + send.separator(); + + for (let i = 0; i < plman.PlaylistCount; ++i) { + let playlist_text = `${plman.GetPlaylistName(i)} [${plman.PlaylistItemCount(i)}]`; + + const is_item_autoplaylist = plman.IsAutoPlaylist(i); + if (is_item_autoplaylist) { + playlist_text += ' (Auto)'; + } + + if (i === plman.PlayingPlaylist) { + playlist_text += ' (Now Playing)'; + } + + send.appendItem(playlist_text, ((playlist_idx) => { + this.selection_handler.send_to_playlist(playlist_idx); + }).bind(undefined, i), { is_grayed_out: is_item_autoplaylist || i === this.cur_playlist_idx }); + } + } + + /** + * Appends the playlist "write playlist stats list" menu to the parent menu. + * @param {ContextMenu} parent_menu - The parent menu to append to. + */ + ctx_menu_playlist_stats(parent_menu) { + // * Set up metadata and playlist stats settings + const metadata = Array.from(this.meta_handler.get_metadata().values()); + const playlistName = plman.GetPlaylistName(plman.ActivePlaylist); + const playlistStatsMenu = new ContextMenu('Write playlist statistics to list'); + const sortBy = plSet.playlist_stats_sort_by; + const sortDirection = plSet.playlist_stats_sort_direction; + + // * Set up playlist stats option menu items + const sortByMenu = [ + ['Sort by artist', 'artist'], + ['Sort by album', 'albumTitle'], + ['Sort by track', 'trackTitle'], + ['Sort by year', 'year'], + ['Sort by genre', 'genre'], + ['Sort by label', 'label'], + ['Sort by country', 'country'], + ['Sort by stats', ''] + ]; + + const sortDirectionMenu = [ + ['Order by ascending', '_asc'], + ['Order by descending', '_dsc'] + ]; + + const statsTypeMenu = [ + ['Album rating', 'albumRating'], + ['Album playcount', 'albumPlaycount'], + ['Album playcount total', 'albumPlaycountTotal'], + ['Album track rating', 'albumTrackRating'], + ['Album track playcount', 'albumTrackPlaycount'], + ['Track rating', 'trackRating'], + ['Track playcount', 'trackPlaycount'], + ['Top rated', 'topRated'], + ['Top played', 'topPlayed'] + ]; + + // * Create playlist stats settings menu + playlistStatsMenu.appendItem('Include artist', () => { + plSet.playlist_stats_include_artist = !plSet.playlist_stats_include_artist; + }, { is_checked: plSet.playlist_stats_include_artist }); + playlistStatsMenu.appendItem('Include album', () => { + plSet.playlist_stats_include_album = !plSet.playlist_stats_include_album; + }, { is_checked: plSet.playlist_stats_include_album }); + playlistStatsMenu.appendItem('Include track', () => { + plSet.playlist_stats_include_track = !plSet.playlist_stats_include_track; + }, { is_checked: plSet.playlist_stats_include_track }); + playlistStatsMenu.appendItem('Include year', () => { + plSet.playlist_stats_include_year = !plSet.playlist_stats_include_year; + }, { is_checked: plSet.playlist_stats_include_year }); + playlistStatsMenu.appendItem('Include genre', () => { + plSet.playlist_stats_include_genre = !plSet.playlist_stats_include_genre; + }, { is_checked: plSet.playlist_stats_include_genre }); + playlistStatsMenu.appendItem('Include label', () => { + plSet.playlist_stats_include_label = !plSet.playlist_stats_include_label; + }, { is_checked: plSet.playlist_stats_include_label }); + playlistStatsMenu.appendItem('Include country', () => { + plSet.playlist_stats_include_country = !plSet.playlist_stats_include_country; + }, { is_checked: plSet.playlist_stats_include_country }); + playlistStatsMenu.appendItem('Include stats', () => { + plSet.playlist_stats_include_stats = !plSet.playlist_stats_include_stats; + }, { is_checked: plSet.playlist_stats_include_stats }); + + playlistStatsMenu.separator(); + + for (const sortBy of sortByMenu) { + playlistStatsMenu.appendItem(sortBy[0], () => { + plSet.playlist_stats_sort_by = sortBy[1]; + }, { is_radio_checked: plSet.playlist_stats_sort_by === sortBy[1] }); + } + + playlistStatsMenu.separator(); + + for (const direction of sortDirectionMenu) { + playlistStatsMenu.appendItem(direction[0], () => { + plSet.playlist_stats_sort_direction = direction[1]; + }, { is_radio_checked: plSet.playlist_stats_sort_direction === direction[1] }); + } + + playlistStatsMenu.separator(); + + playlistStatsMenu.appendItem('Reset settings', () => { + plSet.playlist_stats_include_artist = true; + plSet.playlist_stats_include_album = true; + plSet.playlist_stats_include_track = true; + plSet.playlist_stats_include_year = false; + plSet.playlist_stats_include_genre = false; + plSet.playlist_stats_include_label = false; + plSet.playlist_stats_include_country = false; + plSet.playlist_stats_include_stats = true; + plSet.playlist_stats_sort_direction = '_dsc'; + plSet.playlist_stats_sort_by = ''; + }); + + playlistStatsMenu.separator(); + + // * Create playlist stats list menu + for (const item of statsTypeMenu) { + const statsTypeAlbums = item[1].startsWith('album'); + const statsTypeTracks = statsTypeAlbums && plSet.playlist_stats_sort_by.startsWith('track'); + + playlistStatsMenu.appendItem(item[0], ((statsType) => { + const newStatsType = sortBy ? `${sortBy}${sortDirection}_${statsType}` : `${statsType}${sortDirection}`; + const metadataType = + item[1].startsWith('track') ? 'track' : + item[1].startsWith('album') ? 'album' : + item[1].endsWith('topRated') ? 'topRated' : + item[1].endsWith('topPlayed') ? 'topPlayed' : + 'album'; + + const statsName = item[0]; + const filePath = `${fb.ProfilePath}cache\\playlist\\${playlistName}_${statsName}${statsType.startsWith('top') ? '_statistics' : `_[${newStatsType}]`}.txt`; + + const ratingType = + statsType === 'albumRating' ? 'albumAverage' : + statsType === 'albumRatingTotal' ? 'albumTotal' : + statsType === 'albumTrackRating' ? 'albumTracks' : + false; + const playcountType = + statsType === 'albumPlaycount' ? 'albumAverage' : + statsType === 'albumPlaycountTotal' ? 'albumTotal' : + statsType === 'albumTrackPlaycount' ? 'albumTracks' : + false; + + this.meta_handler.write_stats_to_text_file(metadata, metadataType, filePath, statsName, newStatsType, ratingType, playcountType); + fb.ShowPopupMessage(`${statsName} list was saved in:\n\n${filePath}`, `${statsName} list`); + }).bind(this, item[1]), { is_grayed_out: statsTypeTracks }); + } + + parent_menu.append(playlistStatsMenu); + } + // #endregion +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-properties.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-properties.js new file mode 100644 index 00000000..ead6a5c2 --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-properties.js @@ -0,0 +1,97 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Properties * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +//////////////////// +// * PROPERTIES * // +//////////////////// +/** + * Adds main playlist panel properties to the SMP properties. + * Default values for grouping data are set in its class constructor. + */ +plSet.addProperties( + { + show_plman: ['Panel Playlist - User: Playlist_manager.show', true], + + rows_in_header: ['Panel Playlist - User: Header.normal.row_count', 4], + rows_in_compact_header: ['Panel Playlist - User: Header.compact.row_count', 3], + show_header: ['Panel Playlist - User: Header.show', true], + use_compact_header: ['Panel Playlist - User: Header.use_compact', false], + show_album_art: ['Panel Playlist - User: Header.this.art.show', true], + auto_album_art: ['Panel Playlist - User: Header.this.art.auto', false], + show_group_info: ['Panel Playlist - User: Header.info.show', true], + show_disc_header: ['Panel Playlist - User: Header.disc_header.show', true], + show_rating_header: ['Panel Playlist - User: Header.rating.show', true], + show_PLR_header: ['Panel Playlist - User: Header.peak_loudness_ratio.show', false], + auto_collapse: ['Panel Playlist - User: Header.collapse.auto', false], + collapse_on_playlist_switch: ['Panel Playlist - User: Header.collapse.on_playlist_switch', false], + collapse_on_start: ['Panel Playlist - User: Header.collapse.on_start', false], + + row_h: ['Panel Playlist - User: Row.height', 20], + show_row_stripes: ['Panel Playlist - User: Row.stripes.show', false], + show_playcount: ['Panel Playlist - User: Row.play_count.show', true], + show_PLR: ['Panel Playlist - User: Row.peak_loudness_ratio.show', false], + show_rating: ['Panel Playlist - User: Row.rating.show', true], + use_rating_from_tags: ['Panel Playlist - User: Row.rating.from_tags', false], + show_queue_position: ['Panel Playlist - User: Row.queue_position.show', true], + + show_scrollbar: ['Panel Playlist - User: Scrollbar.show', true], + scrollbar_right_pad: ['Panel Playlist - User: Scrollbar.pad.right', 0], + scrollbar_top_pad: ['Panel Playlist - User: Scrollbar.pad.top', 0], + scrollbar_bottom_pad: ['Panel Playlist - User: Scrollbar.pad.bottom', 3], + scrollbar_w: ['Panel Playlist - User: Scrollbar.width', ''], + scrollbar_wheel_scroll_page: ['Panel Playlist - User: Scrollbar.wheel_whole_page', false], + + playlist_stats_include_artist: ['Panel Playlist - User: Misc.playlist_stats_include_artist', true], + playlist_stats_include_album: ['Panel Playlist - User: Misc.playlist_stats_include_album', true], + playlist_stats_include_track: ['Panel Playlist - User: Misc.playlist_stats_include_track', true], + playlist_stats_include_year: ['Panel Playlist - User: Misc.playlist_stats_include_year', false], + playlist_stats_include_genre: ['Panel Playlist - User: Misc.playlist_stats_include_genre', false], + playlist_stats_include_label: ['Panel Playlist - User: Misc.playlist_stats_include_label', false], + playlist_stats_include_country: ['Panel Playlist - User: Misc.playlist_stats_include_country', false], + playlist_stats_include_stats: ['Panel Playlist - User: Misc.playlist_stats_include_stats', true], + playlist_stats_sort_by: ['Panel Playlist - User: Misc.playlist_stats_sort_by', ''], + playlist_stats_sort_direction: ['Panel Playlist - User: Misc.playlist_stats_sort_direction', '_dsc'], + + load_playlist_async: ['Panel Playlist - System: Load.playlist.asynchronously', true], + list_top_pad: ['Panel Playlist - System: List.padding.top', 0], + list_right_pad: ['Panel Playlist - System: List.padding.right', 0], + list_bottom_pad: ['Panel Playlist - System: List.padding.bottom', 15], + list_left_pad: ['Panel Playlist - System: List.padding.left', 0], + playlist_group_data: ['Panel Playlist - System: Playlist.grouping.data_list', ''], + playlist_custom_group_data: ['Panel Playlist - System: Playlist.grouping.custom_data_list', ''], + default_group_name: ['Panel Playlist - System: Playlist.grouping.default_preset_name', ''], + group_presets: ['Panel Playlist - System: Playlist.grouping.presets', ''], + scrollbar_pos: ['Panel Playlist - System: Scrollbar.position', 0] + } +); + + +////////////////////////// +// * FIXUP PROPERTIES * // +////////////////////////// +plSet.row_h = Math.max(10, plSet.row_h); +plSet.rows_in_header = Math.max(0, plSet.rows_in_header); +plSet.rows_in_compact_header = Math.max(0, plSet.rows_in_compact_header); + +if (!grSet.playlistAutoHideScrollbar && !plSet.show_scrollbar) { + plSet.show_scrollbar = true; +} + +// * Fix playlist panel state at startup +if (grSet.libraryLayoutSplitPreset || grSet.libraryLayoutSplitPreset3 || grSet.libraryLayoutSplitPreset4) { + plSet.auto_collapse = grSet.showPanelOnStartup === 'library'; +} else if (grSet.libraryLayoutSplitPreset2) { + plSet.show_header = grSet.showPanelOnStartup === 'playlist'; +} diff --git a/profile/georgia-reborn/scripts/Playlist/scripts/pl-setup.js b/profile/georgia-reborn/scripts/Playlist/scripts/pl-setup.js new file mode 100644 index 00000000..348d95da --- /dev/null +++ b/profile/georgia-reborn/scripts/Playlist/scripts/pl-setup.js @@ -0,0 +1,109 @@ +///////////////////////////////////////////////////////////////////////////////// +// * Georgia-ReBORN: A Clean - Full Dynamic Color Reborn - Foobar2000 Player * // +// * Description: Georgia-ReBORN Playlist Globals * // +// * Author: TT * // +// * Org. Author: extremeHunter, TheQwertiest * // +// * Website: https://github.com/TT-ReBORN/Georgia-ReBORN * // +// * Version: 3.0-DEV * // +// * Dev. started: 22-12-2017 * // +// * Last change: 18-02-2024 * // +///////////////////////////////////////////////////////////////////////////////// + + +'use strict'; + + +/////////////// +// * ENUMS * // +/////////////// +/** + * A set of drag and drop action settings. + * @global + * @enum {number} + */ +const PlaylistDropEffect = { + none: 0, + copy: 1, + move: 2, + link: 4, + scroll: 0x80000000 +}; + +/** + * A set of playlist history state settings. + * @global + * @enum {string} + */ +const PlaylistMutation = { + Added: 'Playlist added', + Init: 'Playlist initializing history', + Removed: 'Playlist removed', + Reordered: 'Playlist reordered', + Switch: 'Playlist switch' +} + +/** + * A set of playlist row state settings. + * @global + * @enum {number} + */ +const PlaylistRowState = { + normal: 0, + hovered: 1, + pressed: 2 +}; + +/** + * A set of playlist item visibility state settings. + * @global + * @enum {number} + */ +const PlaylistVisibilityState = { + none: 0, + partial_top: 1, + partial_bottom: 2, + full: 3 +}; + + +/////////////////// +// * VARIABLES * // +/////////////////// +/** + * The instance of `PanelProperties` class for playlist panel property settings. + * @typedef {PanelProperties} + * @global + */ +const plSet = new PanelProperties(); + +/** + * A collection of various playlist objects and variables. + * @typedef {object} pl - The playlist main object. + * @property {object} col - The playlist colors object. + * @property {object} font - The playlist fonts object. + * @property {object} geo - The playlist geometry object. + * @property {PlaylistHistory} history - The playlist history object. + * @property {boolean} history_used - The playlist history state, used for playlist scroll. + * @property {Map} album_ratings - The playlist album ratings cached. + * @property {Map} track_ratings - The playlist track ratings cached. + * @property {FbMetadbHandle[]} thumbnail_list - The list of handles that we are loading artwork for. + * @property {number} thumbnail_size - The playlist thumbnail size defaults are 64 pixels in HD 128 in 4K. + * @property {PlaylistCallbacks} call - The instance of `PlaylistCallbacks` class for callback operations. + * @property {PlaylistManager} plman - The instance of `PlaylistManager` class for playlist manager operations. + * @property {Playlist} playlist - The instance of `Playlist` class for main playlist operations. + */ +/** @global @type {pl} */ +const pl = { + col: {}, + font: {}, + geo: {}, + history: undefined, + history_used: false, + album_ratings: new Map(), + track_ratings: new Map(), + thumbnail_list: new Set(), + thumbnail_size: undefined, + call: undefined, + plman: undefined, + playlist: undefined +};