eurekagliese

Fluent Control Panel

Jun 28th, 2025
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 14.97 KB | Source Code | 0 0
  1. 'use strict';
  2.  
  3. window.DefineScript('Fluent Control Panel 0.7', {author:'marc2003 / eurekagliese', options:{grab_focus:false}});
  4. include(fb.ComponentPath + 'samples\\complete\\js\\lodash.min.js');
  5. include(fb.ComponentPath + 'samples\\complete\\js\\helpers.js');
  6. include(fb.ComponentPath + 'samples\\complete\\js\\panel.js');
  7. include(fb.ComponentPath + 'samples\\complete\\js\\seekbar.js');
  8. include(fb.ComponentPath + 'samples\\complete\\js\\rating.js');
  9. include(fb.ComponentPath + 'samples\\complete\\js\\volume.js');
  10.  
  11. // Used in window.GetColorCUI()
  12. const ColourTypeCUI = {
  13.     text: 0,
  14.     selection_text: 1,
  15.     inactive_selection_text: 2,
  16.     background: 3,
  17.     selection_background: 4,
  18.     inactive_selection_background: 5,
  19.     active_item_frame: 6
  20. };
  21.  
  22. // Used in window.GetColourDUI()
  23. const ColourTypeDUI = {
  24.     text: 0,
  25.     background: 1,
  26.     highlight: 2,
  27.     selection: 3
  28. };
  29.  
  30. let g_is_default_ui = window.InstanceType;
  31. let g_textcolour = 0;
  32. let g_textcolour_hl = 0;
  33. let g_backcolour = 0;
  34. let g_hot = false;
  35.  
  36. let g_metadb = null;
  37. let g_img = null;
  38. let blur_img = null;
  39. let blur_radius = 24;
  40. let overlay = 128;
  41.  
  42. let ww = 0;
  43. let wh = 0;
  44.  
  45. let bx = 0;
  46. let by = 0;
  47. let cx = 0;
  48. let cy = 0;
  49.  
  50. let colours = {
  51.     buttons : _RGB(255, 255, 255),
  52.     background : _RGB(30, 30, 30),
  53.     title : _RGB(255, 255, 255),
  54.     artist : _RGB(240, 240, 240),
  55.     time : _RGB(240, 240, 240),
  56.     red : _RGB(255, 0, 0),
  57.     love : _RGB(255, 0, 0),
  58.     seekbar_background : _RGBA(128, 128, 128, 128),
  59.     seekbar_progress : _RGB(255, 255, 255),
  60.     seekbar_knob : _RGB(196, 30, 35)
  61. };
  62.  
  63. let properties = {
  64.     art : new _p('FLUENT.ALBUMART.SHOW', true),
  65.     vol : new _p('FLUENT.VOLUME.SHOW.', true),
  66.     blur : new _p('FLUENT.BLUR.SHOW', false),
  67.     sbar : new _p('FLUENT.SEEKBAR.SHOW', true),
  68.     rate : new _p('FLUENT.BUTTON.RATING', _RGB(255, 200, 0))
  69. };
  70.  
  71. let tfo = {
  72.     artist : fb.TitleFormat('%artist%'),
  73.     title : fb.TitleFormat('%title%'),
  74.     playback_time : fb.TitleFormat('%playback_time%  '),
  75.     length : fb.TitleFormat('  %length%'),
  76.     lfm_loved: fb.TitleFormat('$if2(%lfm_loved%,0)')
  77. };
  78.  
  79. // font sizes
  80. let fluent_font = gdi.Font('Segoe Fluent Icons', 48);
  81. let fluent_font_h = gdi.Font('Segoe Fluent Icons', 54);
  82. let chara = {
  83.     heart_on : '\ueb52',
  84.     heart_off : '\ueb51',
  85.     heart_break : '\ue00C',
  86.     stop : '\uE71A',
  87.     prev : '\uE892',
  88.     play : '\uE768',
  89.     pause : '\uE769',
  90.     next : '\uE893',
  91.     add_queue : '\ue109',
  92.     search : '\uE721',
  93.     consol : '\ue8e4',
  94.     settings : '\uE713',
  95.     //
  96.     check_on : '\ue73a',
  97.     check_off : '\ue739',
  98.     radio_on : '\ueccb',
  99.     radio_off : '\uecca',
  100.     rating_on : '\ue735',
  101.     rating_off : '\ue734',
  102.     list : '\uea37',
  103.     lock : '\ue72e',
  104.     working : '\ue916',
  105.     up : '\ue70e',
  106.     down : '\ue70d',
  107.     left : '\ue76b',
  108.     right : '\ue76c',
  109.     close : '\uef2c',
  110.     menu : '\ue700',
  111.     music : '\uec4f',
  112.     volume : '\ue767',
  113.     repeat_all : '\ue8ee',
  114.     repeat_one : '\ue8ed',
  115.     repeat_off : '\uf5e7',
  116.     shuffle : '\ue8b1',
  117.     random : '\ue9ce',
  118.     album : '\ue93c',
  119.     folder : '\ued25',
  120.     autoplaylist : '\uea69',
  121.  
  122. };
  123. //////////////////////////////////////////////////////////////
  124.  
  125. let panel = new _panel();
  126. let seekbar = new _seekbar(0, 0, 0, 0);
  127. let buttons = new _buttons();
  128. let rating = new _rating(0, 0, 12, 0); // x, y, size, colour
  129. let volume = new _volume(0, 0, 100, 40);
  130.  
  131. const bs = _scale(32);
  132.  
  133. get_colours();
  134. panel.item_focus_change();
  135. update_album_art(fb.GetNowPlaying());
  136.  
  137. buttons.update = () => {
  138.     const x = ((panel.w - (bs * 7)) / 2);
  139.     const y = _scale(4);
  140.     let fav = tfo.lfm_loved.Eval();
  141.     buttons.buttons.love = new _button(x, y, bs, bs, {normal : fav == 0 ? _chrToImg(chara.heart_off, g_textcolour, fluent_font) : _chrToImg(chara.heart_on, colours.love, fluent_font), hover : fav == 0 ? _chrToImg(chara.heart_on, colours.red, fluent_font_h) : _chrToImg(chara.heart_break, colours.love, fluent_font_h)}, () => {
  142.         let selected_items = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
  143.         if (selected_items && selected_items.Count !== 0) {
  144.         fb.RunContextCommandWithMetadb("Last.fm Playcount Sync/" + (fav == 1 ? "Unlove" : "Love"), selected_items);
  145.         }
  146.      }, '');
  147.     buttons.buttons.stop = new _button(x + bs, y, bs, bs, {normal : fb.StopAfterCurrent ? _chrToImg(chara.stop, colours.red, fluent_font) : _chrToImg(chara.stop, g_textcolour, fluent_font), hover : _chrToImg(chara.stop, g_textcolour_hl, fluent_font_h)}, () => { fb.Stop(); }, '');
  148.     buttons.buttons.previous = new _button(x + (bs * 2), y, bs, bs, {normal : _chrToImg(chara.prev, g_textcolour, fluent_font), hover : _chrToImg(chara.prev, g_textcolour_hl, fluent_font_h)}, () => { fb.Prev(); }, '');
  149.     buttons.buttons.play = new _button( x + (bs * 3), y, bs, bs, {normal : !fb.IsPlaying || fb.IsPaused ? _chrToImg(chara.play, g_textcolour, fluent_font) : _chrToImg(chara.pause, g_textcolour, fluent_font), hover : !fb.IsPlaying || fb.IsPaused ? _chrToImg(chara.play, g_textcolour_hl, fluent_font_h) : _chrToImg(chara.pause, g_textcolour_hl, fluent_font_h)}, () => { fb.PlayOrPause(); }, !fb.IsPlaying || fb.IsPaused ? '' : '');
  150.     buttons.buttons.next = new _button(x + (bs * 4), y, bs, bs, {normal : _chrToImg(chara.next, g_textcolour, fluent_font), hover : _chrToImg(chara.next, g_textcolour_hl, fluent_font_h)}, () => { fb.Next(); }, '');
  151.     buttons.buttons.shuffle = new _button(x + (bs * 5), y, bs, bs, {normal : _chrToImg(chara.shuffle, plman.PlaybackOrder == 0 ? g_textcolour : g_textcolour_hl, fluent_font), hover : _chrToImg(chara.shuffle, colours.red, fluent_font_h)}, () => { plman.PlaybackOrder = (plman.PlaybackOrder !== 4) ? 4 : 0; }, '');
  152.     buttons.buttons.add_queue = new _button(bx + (bs * 6), y, bs, bs, {normal : _chrToImg(chara.add_queue, g_textcolour, fluent_font), hover : _chrToImg(chara.add_queue, g_textcolour_hl, fluent_font_h)}, () => {
  153.         let selected_items = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
  154.         if (selected_items && selected_items.Count !== 0) {
  155.             fb.RunContextCommandWithMetadb("Add to playback queue", selected_items);
  156.         }
  157.     }, '');
  158.    
  159.     const xx = (panel.w - (bs * 2));
  160.     const yy = Math.round((panel.h - bs) / 2);
  161.     buttons.buttons.volume = new _button(xx - (bs * 3), yy, bs, bs, {normal : _chrToImg(chara.volume, g_textcolour, fluent_font), hover : _chrToImg(chara.volume, g_textcolour_hl, fluent_font_h)}, () => { fb.VolumeMute(); }, '');
  162.     buttons.buttons.search = new _button(xx - (bs * 2), yy, bs, bs, {normal : _chrToImg(chara.search, g_textcolour, fluent_font), hover : _chrToImg(chara.search, g_textcolour_hl, fluent_font_h)}, () => { fb.RunMainMenuCommand('Library/Search'); }, '');
  163.     buttons.buttons.consol = new _button(xx -  bs, yy, bs, bs, {normal : _chrToImg(chara.consol, g_textcolour, fluent_font), hover : _chrToImg(chara.consol, g_textcolour_hl, fluent_font_h)}, () => { fb.ShowConsole(); }, '');
  164.     buttons.buttons.settings = new _button(xx, yy, bs, bs, {normal : _chrToImg(chara.settings, g_textcolour, fluent_font), hover : _chrToImg(chara.settings, g_textcolour_hl, fluent_font_h)}, () => { fb.ShowPreferences(); }, '');
  165. }
  166.  
  167. function on_size() {
  168.     panel.size();
  169.  
  170.     ww = window.Width;
  171.     wh = window.Height;
  172.    
  173.     bx = ((panel.w - (bs * 7)) / 2);
  174.     by = seekbar.y - _scale(36);
  175.    
  176.     cx = (panel.w - (bs * 2));
  177.     cy = Math.round((panel.h - bs) / 2);
  178.    
  179.     seekbar.x = Math.round(panel.w * 0.22);
  180.     seekbar.w = panel.w - seekbar.x - _scale(280);
  181.     seekbar.h = _scale(14);
  182.     seekbar.y = panel.h * 0.6;
  183.    
  184.     rating.x = bx - (bs * 3);
  185.     rating.y = _scale(14);
  186.  
  187.     volume.x = panel.w - (bs * 5);
  188.     volume.y = seekbar.y + _scale(12);
  189.     volume.h = _scale(4);
  190.    
  191.     buttons.update();
  192. }
  193.  
  194. function on_paint(gr) {
  195.     gr.FillSolidRect(0, 0, panel.w, panel.h, g_backcolour);
  196.    
  197.     if (properties.blur.enabled) {
  198.         if (g_img) {
  199.         _drawImage(gr, blur_img, 0, 0, panel.w, panel.h, image.crop);
  200.         gr.FillSolidRect(0, 0, panel.w, panel.h, _RGBA(0, 0, 0, overlay));
  201.         }
  202.     }
  203.  
  204.     buttons.paint(gr);
  205.    
  206.     rating.colour = properties.rate.value;
  207.  
  208.     if (properties.sbar.enabled) {
  209.         gr.FillSolidRect(seekbar.x, seekbar.y + _scale(4), seekbar.w + _scale(6), _scale(4), colours.seekbar_background); //seekbar_bg
  210.     }
  211.  
  212.     if (fb.IsPlaying) {
  213.         rating.paint(gr);
  214.         if (properties.art.enabled) {
  215.             if (g_img) {
  216.             _drawImage(gr, g_img, 0, 0, panel.h, panel.h, image.crop_top);
  217.             }
  218.         }
  219.         gr.GdiDrawText(tfo.title.Eval(), panel.fonts.title, g_textcolour, properties.art.enabled ? panel.h + 10 : _scale(12), cy + _scale(4), properties.art.enabled ? panel.w /5 : panel.w /4, 0, LEFT);
  220.         gr.GdiDrawText(tfo.artist.Eval(), panel.fonts.normal, g_textcolour,  properties.art.enabled ? panel.h + 10 : _scale(12), cy + _scale(28), properties.art.enabled ? panel.w /9 : seekbar.x - panel.h - _scale(4), 0, LEFT);
  221.  
  222.         gr.SetSmoothingMode(2);
  223.         // seekbar
  224.         if (fb.PlaybackLength > 0) {
  225.             const pos = seekbar.pos();
  226.             if (properties.sbar.enabled) {
  227.                 gr.FillSolidRect(seekbar.x, seekbar.y + _scale(4), pos, _scale(4), g_textcolour); //progress_bar
  228.                 gr.FillEllipse(seekbar.x + pos - 4, seekbar.y, _scale(12), _scale(12),g_textcolour_hl); //knob
  229.             }
  230.                 gr.GdiDrawText(tfo.playback_time.Eval(), panel.fonts.normal, g_textcolour, seekbar.x - _scale(45), seekbar.y + _scale(5), _scale(45), 0, RIGHT);
  231.                 gr.GdiDrawText(tfo.length.Eval(), panel.fonts.normal, g_textcolour, seekbar.x + seekbar.w + _scale(6), seekbar.y + _scale(5), _scale(45), 0, LEFT);
  232.         }
  233.  
  234.         // volume
  235.         if (properties.vol.enabled) {
  236.             gr.FillSolidRect(volume.x, volume.y, volume.w, volume.h, colours.seekbar_background);
  237.             gr.FillSolidRect(volume.x, volume.y, volume.pos(), volume.h, g_textcolour);
  238.             gr.GdiDrawText(fb.Volume.toFixed(2) + ' dB', panel.fonts.normal, g_textcolour, _scale(8), volume.y  + _scale(2), volume.x + _scale(128), 0, RIGHT);
  239.         }      
  240.     }
  241. }
  242.  
  243. function on_metadb_changed() {
  244.     rating.metadb_changed();
  245. }
  246.  
  247. function on_playback_new_track() {
  248.     update_album_art(g_metadb);
  249.     panel.item_focus_change();
  250.     buttons.update();
  251. }
  252.  
  253. function update_album_art(metadb) {
  254.     g_metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
  255.     g_img = utils.GetAlbumArtV2(g_metadb, 0);
  256.     if (g_img && g_img.Width > 200) {
  257.         var r = 200 / g_img.Width;
  258.         g_img = g_img.Resize(200, g_img.Height * r, 2);
  259.         if (g_img != null) {
  260.             blur_img = g_img.Clone(0, 0, g_img.Width, g_img.Height);
  261.             blur_img.StackBlur(blur_radius);
  262.         }
  263.     }
  264.     window.Repaint();
  265. }
  266.  
  267. function on_playback_edited() {
  268.     buttons.update();
  269.     window.Repaint();
  270. }
  271.  
  272. function on_playback_seek() {
  273.     seekbar.playback_seek();
  274. }
  275.  
  276. function on_playback_stop() {
  277.     buttons.update();
  278.     window.Repaint();
  279. }
  280.  
  281. function on_playback_stop(reason) {
  282.     if (reason != 2) {
  283.         panel.item_focus_change();
  284.     }
  285. }
  286.  
  287. function on_playback_pause() {
  288.     buttons.update();
  289.     window.Repaint();
  290. }
  291.  
  292. function on_playback_starting() {
  293.     buttons.update();
  294.     window.Repaint();
  295. }
  296.  
  297. function on_playback_dynamic_info_track() {
  298.     panel.item_focus_change();
  299. }
  300.  
  301. function on_playback_order_changed() {
  302.     buttons.update();
  303.     window.Repaint();
  304. }
  305.  
  306. function on_playlist_switch() {
  307.     panel.item_focus_change();
  308. }
  309.  
  310. function on_item_focus_change() {
  311.     panel.item_focus_change();
  312. }
  313.  
  314. function on_colours_changed() {
  315.     get_colours();
  316.     buttons.update();
  317.     panel.colours_changed();
  318.     window.Repaint();
  319. }
  320.  
  321. function on_volume_change() {
  322.     buttons.update();
  323.     volume.volume_change();
  324.     window.Repaint();
  325. }
  326.  
  327. // mouse events
  328. function on_mouse_wheel(s) {
  329.     volume.wheel(s);
  330.  
  331.     let easy_vol = vol2pos(fb.Volume);
  332.         easy_vol += s / 20;
  333.         fb.Volume = pos2vol(easy_vol);
  334. }
  335.  
  336. function on_mouse_move(x, y) {
  337.     if (!g_hot) {
  338.         g_hot = true;
  339.         window.SetCursor(IDC_HAND);
  340.         window.Repaint();
  341.     }
  342.     if (buttons.move(x, y) || rating.move(x, y) || seekbar.move(x, y) || volume.move(x, y)) return;
  343. }
  344.  
  345. function on_mouse_leave() {
  346.     if (g_hot) {
  347.         g_hot = false;
  348.         window.Repaint();
  349.     }
  350.     buttons.leave();
  351.     rating.leave();
  352. }
  353.  
  354. function on_mouse_lbtn_down(x, y) {
  355.     seekbar.lbtn_down(x, y);
  356.     volume.lbtn_down(x, y);
  357. }
  358.  
  359. function on_mouse_lbtn_up(x, y) {
  360.     if (buttons.lbtn_up(x, y)) {
  361.         return;
  362.     }
  363.     if (seekbar.lbtn_up(x, y)) {
  364.         return;
  365.     }
  366.     if (rating.lbtn_up(x, y)) {
  367.         return;
  368.     }
  369.     if (volume.lbtn_up(x, y)) {
  370.         return;
  371.     }
  372.     fb.RunMainMenuCommand('View/Show now playing in playlist');
  373.     // search artist in library
  374.     if (x < _scale(200) && x > _scale(12) && y < _scale(72) && y > _scale(12)) {
  375.         fb.ShowLibrarySearchUI(tfo.artist.Eval())
  376.     } return;
  377. }
  378.  
  379. function on_mouse_rbtn_up(x, y) {
  380.     if (buttons.buttons.stop.trace(x, y)) {
  381.         fb.StopAfterCurrent = !fb.StopAfterCurrent;
  382.         buttons.update();
  383.         return true
  384.     }  
  385.     // flush playback queue with right click
  386.     if (buttons.buttons.add_queue.trace(x, y)) {
  387.         fb.RunMainMenuCommand("Flush playback queue");
  388.         return true
  389.     }  
  390.     if (buttons.buttons.search.trace(x, y)) {
  391.         plman.SetActivePlaylistContext(); //activated the current playlist
  392.         fb.RunMainMenuCommand('Edit/Search'); // search in current playlist
  393.         return true
  394.     }
  395.     if (rating.trace(x, y)) {
  396.         return panel.rbtn_up(x, y, rating);
  397.     }
  398.  
  399.     // menu
  400.     let m = window.CreatePopupMenu();
  401.     let s = window.CreatePopupMenu();
  402.     let c = fb.CreateContextMenuManager();
  403.     let col = window.CreatePopupMenu();
  404.     if (fb.IsPlaying) {
  405.         c.InitNowPlaying();
  406.         c.BuildMenu(s, 1);
  407.         s.AppendTo(m, MF_STRING, 'Now playing');
  408.         m.AppendMenuSeparator();
  409.     }
  410.     m.AppendMenuItem(MF_STRING, 10000, 'Show album art');
  411.     m.CheckMenuItem(10000, properties.art.enabled);
  412.     m.AppendMenuItem(MF_STRING, 10001, 'Show volume slider');
  413.     m.CheckMenuItem(10001, properties.vol.enabled);
  414.     m.AppendMenuItem(MF_STRING, 10002, 'Show blur effect');
  415.     m.CheckMenuItem(10002, properties.blur.enabled);   
  416.     m.AppendMenuItem(MF_STRING, 10003, 'Show seekbar');
  417.     m.CheckMenuItem(10003, properties.sbar.enabled);
  418.  
  419.     m.AppendMenuSeparator();
  420.     col.AppendMenuItem(MF_STRING, 10005, 'Rating...');
  421.     col.AppendTo(m, MF_STRING, 'Colours');
  422.     m.AppendMenuSeparator();
  423.     m.AppendMenuItem(MF_STRING, 10009, 'Reload...');
  424.     m.AppendMenuItem(MF_STRING, 10010, 'Configure...');
  425.     const idx = m.TrackPopupMenu(x, y);
  426.     switch (idx) {
  427.     case 0:
  428.         break;
  429.     case 10000:
  430.         properties.art.toggle();       
  431.         window.Repaint();
  432.         break;
  433.     case 10001:
  434.         properties.vol.toggle();       
  435.         window.Repaint();
  436.         break;
  437.     case 10002:
  438.         properties.blur.toggle();
  439.         window.Repaint();
  440.         break;     
  441.     case 10003:
  442.         properties.sbar.toggle();
  443.         window.Repaint();
  444.         break;
  445.     case 10005:
  446.         properties.rate.value = utils.ColourPicker(window.ID, properties.rate.value);
  447.         window.Repaint();
  448.         break;     
  449.     case 10009:
  450.         window.Reload();
  451.         break;
  452.     case 10010:
  453.         window.ShowConfigure();
  454.         break;
  455.     default:
  456.         c.ExecuteByID(idx - 1);
  457.         break;
  458.     }
  459.     return true;
  460. }
  461.  
  462. function get_colours() {
  463.     if (g_is_default_ui) { // DUI
  464.         g_textcolour = window.GetColourDUI(ColourTypeDUI.text);
  465.         g_textcolour_hl = window.GetColourDUI(ColourTypeDUI.highlight);
  466.         g_backcolour = window.GetColourDUI(ColourTypeDUI.background);
  467.     } else { // CUI
  468.         g_textcolour = window.GetColourCUI(ColourTypeCUI.text);
  469.         g_textcolour_hl = window.GetColourCUI(ColourTypeCUI.text);
  470.         g_backcolour = window.GetColourCUI(ColourTypeCUI.background);
  471.     }
  472. }
  473.  
  474. function vol2pos(v) {
  475.     return (Math.pow(10, v / 50) - 0.01) / 0.99;
  476. }
  477.  
  478. function pos2vol(pos) {
  479.     return 50 * Math.log(0.99 * pos + 0.01) / Math.LN10;
  480. }
Add Comment
Please, Sign In to add comment