Skip to main content
Topic: JScript Panel script discussion/help (Read 75249 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

JScript Panel script discussion/help

Reply #26
Help needed - Is this a bug?

The method "GetPlaylistSelectedItems" with callback "on_item_focus_change" does not seem well in my scripts.
I used scripts like this:
Code: [Select]
var font = gdi.Font(fb.TitleFormat("%theme_font%").Eval(), 12, 2);
var margin_x = 30;
var handles, displayed;

function reload() {
    handles = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
    if (handles.Count < 2) {
        displayed = plman.PlaylistItemCount(plman.ActivePlaylist) + " items / " + utils.FormatDuration(plman.GetPlaylistItems(plman.ActivePlaylist).CalcTotalDuration()) + " / " + utils.FormatFileSize(plman.GetPlaylistItems(plman.ActivePlaylist).CalcTotalSize());
    } else {
        displayed = "Selected " + handles.Count + " of " + plman.PlaylistItemCount(plman.ActivePlaylist) + " items / " + utils.FormatDuration(handles.CalcTotalDuration()) + " / " + utils.FormatFileSize(handles.CalcTotalSize());
    }
}

function on_size() {
    if (!window.Width || !window.Height) return;
    handles = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
    reload();
}

function on_paint(gr) {
    gr.SetSmoothingMode(4)
    gr.FillSolidRect(0, 0, window.Width, window.Height, 0xaa111111);
    gr.FillGradRect(0, window.Height - 12, window.Width, 12, 90, 0x00000000, 0x88000000);
    gr.GdiDrawText(displayed, font, 0xffffffff, margin_x, 4, window.Width - 2 * margin_x, 16, 0x00000001 | 0x00000004 | 0x00008000);
}

function on_item_focus_change(playlist, from, to) {
    reload();
    window.Repaint();
}

And the displayed number of selected tracks went wrong. Everytime I use main menu command "Edit/Select all", the displayed number never changed.
When using ELPlaylist, selecting any single track worked correctly, but if selected a whole group, things went wrong like above.
In addition, I tried to avoid the GPSI method, and used the scripts below instead:
Code: [Select]
function reload() {
    var nppl = plman.ActivePlaylist;
    var total = plman.PlaylistItemCount(nppl);
    var evtg = plman.GetPlaylistItems(nppl)
    var selected = 0;
    var dur = 0;
    var siz = 0;
    for (var i=0; i<total; i++) {
        if (plman.IsPlaylistItemSelected(nppl, i)) {
            selected++;
            dur += evtg.Item(i).Length;
            siz += evtg.Item(i).FileSize;
        };
    }
    
    if (selected < 0) {
        displayed = total + " items / " + utils.FormatDuration(evtg.CalcTotalDuration()) + " / " + utils.FormatFileSize(evtg.CalcTotalSize());
    } else {
        displayed = "Selected " + selected + " of " + total + " items / " + utils.FormatDuration(dur) + " / " + utils.FormatFileSize(siz);
    }
}

But things just remained the same.

JScript Panel script discussion/help

Reply #27
Alright, here's some further discovery.
The callback "on_item_focus_change()" is not called when using "Edit/Select all".
As in ELPlaylist, the callback is called, but its output is still not ideal.
Still wondering why.

JScript Panel script discussion/help

Reply #28
replace on_item_focus_change() with on_selection_changed().

JScript Panel script discussion/help

Reply #29
OK, it seemed to be solved with the method "on_playlist_items_selection_change()".
I found it in Br3tt's JS Smooth Playlist (thanks). Why isn't it referred in the docs?

Code: [Select]
on_playlist_items_selection_change()
on_selection_changed()
on_item_selection_change()
on_item_focus_change()


They look so similar. Could anyone explain their difference to me?


JScript Panel script discussion/help

Reply #31
Code: [Select]
on_playlist_items_selection_change()


That was missing from the docs so I need to update them. According to the original docs, it was added as a workaround for 3rd party playlist viewers not working with on_selection_changed(). So they're basically the same thing.

on_item_focus_change only triggers when the focused item has changed. Using Edit>Select all doesn't change the focused item.

And there is no such thing as on_item_selection_change().

JScript Panel script discussion/help

Reply #32
Code: [Select]
on_playlist_items_selection_change()


That was missing from the docs so I need to update them. According to the original docs, it was added as a workaround for 3rd party playlist viewers not working with on_selection_changed(). So they're basically the same thing.

on_item_focus_change only triggers when the focused item has changed. Using Edit>Select all doesn't change the focused item.

And there is no such thing as on_item_selection_change().

Yes, that's the exact answer to the problem.
Thanks for the help.

JScript Panel script discussion/help

Reply #33
I just realised on_playlist_items_selection_change() wasn't in the original docs either. My previous post about the workaround was taken from the changelog.

If you want the updated file to go in your docs folder, you can download it here:

https://raw.githubusercontent.com/19379/foo...s/Callbacks.txt

I'll update the component download later on.

JScript Panel script discussion/help

Reply #34
Another question.
When drawing a gradrect over a semitransparent solidrect, an unexpected black border appears.
Just like this: http://imgur.com/qjrTKl6
(It's very dark; a black horizontal line in the middle; you may miss it)
I tried to change the smoothing mode, but it only got lightened, not disappeared.
Is this a bug in GDI or something like that? or I just used the wrong interface function?

JScript Panel script discussion/help

Reply #35
This issue has been raised before and the original developer of WSH panel mod tried to fix it but it didn't go to plan....

https://www.hydrogenaud.io/forums/index.php...st&p=705543

It seems from the preceding post that you can workaround it by increasing/decreasing the rectangle height by 1px.

JScript Panel script discussion/help

Reply #36
This issue has been raised before and the original developer of WSH panel mod tried to fix it but it didn't go to plan....

https://www.hydrogenaud.io/forums/index.php...st&p=705543

It seems from the preceding post that you can workaround it by increasing/decreasing the rectangle height by 1px.


Thanks for the answer. May it be fixed in the component, anyway? It may be problematic in adjustable DPIs...

JScript Panel script discussion/help

Reply #37
From what I've read, the only "fixes" are hacks like adjusting the width/height/angle. From some quick testing, adjusting the angle by a degree or 2 can work rather well, assuming the dimensions aren't too small.

While I could implement this in the component, there's a possibility it could break some existing scripts that have worked fine up until this point. I'm not prepared to take that kind of risk so I'd rather keep the component behaviour consistent and let script writers fix these issues as they encounter them.

JScript Panel script discussion/help

Reply #38
From what I've read, the only "fixes" are hacks like adjusting the width/height/angle. From some quick testing, adjusting the angle by a degree or 2 can work rather well, assuming the dimensions aren't too small.

While I could implement this in the component, there's a possibility it could break some existing scripts that have worked fine up until this point. I'm not prepared to take that kind of risk so I'd rather keep the component behaviour consistent and let script writers fix these issues as they encounter them.

Adjusting the angle by 1 degree worked. This idea is brilliant.

JScript Panel script discussion/help

Reply #39
Another question:
In a single callback, is window.Repaint() only available to be called once?
i.e. If I use more than one window.Repaint() in one callback, is it that only the first one worked, and the others are ignored?

The situation is:
I have multiple rectangles on screen. The width of each rectangle is determined by the width of texts in it.
The method gr.CalcTextWidth(str, IGdiFont) can only be called within on_paint(gr), so the widths are not certain until a window.Repaint() is called.
In a callback, all the texts could be changed, so everything (including the text widths) needs to be refreshed. After the refreshment, some calculation should be done, and it need to be repainted again.

JScript Panel script discussion/help

Reply #40
First of all, you can call window.Repaint as often as you like.  The consequence of that may be increased CPU usage especially with large panels layering text/rectangles over images. For plain backgrounds, it usually isn't an issue. It's crude but I use Windows Task Manager to check...

However, in this instance you don't need to call window.Repaint. You can use temporary graphics like this...

Code: [Select]
function CalcTextWidth(value, font) {
    var temp_bmp = gdi.CreateImage(1, 1);
    var temp_gr = temp_bmp.GetGraphics();
    var width = temp_gr.CalcTextWidth(value, font);
    temp_bmp.ReleaseGraphics(temp_gr);
    temp_bmp.Dispose();
    temp_gr = null;
    temp_bmp = null;
    return width;
}


then

Code: [Select]
var font = gdi.Font("Segoe UI", 16);
var text = "fooby dooby doo";
var text_width = CalcTextWidth(text, font);


I don't think this has been documented - I just picked it up from other people's scripts. I'll stick something on my wiki pages in the next day or two with some more examples.

JScript Panel script discussion/help

Reply #41
That's really a good idea - I should take a note.
Thanks.

JScript Panel script discussion/help

Reply #42
Sorry it's me again......
Now I'm learning about timers, and wondering that, within
Code: [Select]
window.SetInterval(function() {
HERE
},interval)

, what is the behaviour of keyword "this"?
Code: [Select]
this.refresh = function () {
    if (this.refresh_frame_count != 0) {
        window.ClearInterval(this.refresh_timer);
        this.refresh_frame_count = 0;
        this.refresh_timer = false;
    }
    window.RepaintRect(this.x, this.y, this.w, this.h);
    this.refresh_timer = window.SetInterval(this.refresh_nested, this.refresh_interval);
}

this.refresh_nested = function () {
    if (this.refresh_frame_count >= 1000 / this.refresh_interval) {
        window.ClearInterval(this.refresh_timer);
        this.refresh_frame_count = 0;
        this.refresh_timer = false;
    } else {
        this.refresh_frame_count ++;
        window.RepaintRect(this.x, this.y, this.w, this.h);
    }
}

This part of code belong to example_object.
In this example, if I call "example_object.refresh", it only refreshes once, and the timer doesn't seem to work.
But once I changed all "this" in the code of "this.refresh_nested" to "example_object", the code worked properly, to refresh 25 times in a second.

I am confused by now. the keyword "this" is too convenient for me to be adapted. And given that the exact name of the used reference is uncertain:
Code: [Select]
var panel = new Array();
panel.push(new example_object);
panel.push(new example_object2);

(I used this kind of code because it's easy to form something like below)
Code: [Select]
function on_mouse_lbtn_down(x, y) {
    for (i = 0; i < panel.length; i++) {
        if (x >= panel[i].x && x < panel[i].x + panel[i].w && y >= panel[i].y && y < panel[i].y + panel[i].h) {
            panel[i].lbtn_down(x, y);
        }
    }
}

I even don't know how to reference the correct variable name in "this.refresh_nested", if "this" is not an option.
Wait. This is hilarious. there actually is a "this" in the workable version of code.
Code: [Select]
this.refresh_timer = window.SetInterval(this.refresh_nested, this.refresh_interval);

"this.refresh_nested". How can that "this" work while the "this"'s within itself cannot?
Desperate.

Appendix: the full code
Code: [Select]
// ==PREPROCESSOR==
// @name "Seekbar"
// ==/PREPROCESSOR==
//--------

// General Functions
function RGB(r,g,b){ return (0xff000000|(r<<16)|(g<<8)|(b)); }
function RGBA(r,g,b,a){ return ((a<<24)|(r<<16)|(g<<8)|(b)); }

function TimeFmt(t){
    var zpad = function(n){
        var str = n.toString();
        return (str.length<2) ? "0"+str : str;
    }
    var m = Math.floor(t/60); t-=m*60;
    var s = Math.floor(t);
    return m.toString()+":"+zpad(s);
}

// Global Variables

var theme_font = fb.TitleFormat("%theme_font%").Eval(true);
var panelh, panelw;

// for time display
/*
var
*/
// Seekbar

seekbar = function () {

    this.init = function (x, y, w, h) {
        this.name = seekbar;
        this.font = gdi.Font(theme_font, 14, 2);
        this.timewidth = window.GetProperty("seekbar_time_width",70);
        this.iscd = window.GetProperty("seekbar_is_countdown",true);
        this.handle_size = window.GetProperty("seekbar_handle_size__even_only",12);
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.drag = false;
        this.seek = 0;
        this.refresh_interval = window.GetProperty("seekbar_refresh_interval",40)
        this.refresh_frame_count = 0;
        this.refresh_timer = false;
        this.bar = {
            x: this.timewidth,
            y: this.x + Math.floor(this.h / 2) - this.handle_size / 2,
            w: this.w - 2 * this.timewidth
        };
    }
    
    this.draw = function (gr) {
        var newt = 0;
        var pos = 0;
        var t1, t2;
        var length = fb.PlaybackLength;
        
        if (length > 0) {
            if (this.drag) {
                newt = length * this.seek;
                pos = this.bar.w * this.seek;
            } else {
                var pbkt = fb.PlaybackTime;
                newt = pbkt;
                pos = this.bar.w * (pbkt / length);
            }
            newt = (length < newt) ? length: (newt < 0) ? 0: newt;
            t1 = TimeFmt (newt);
            t2 = (this.iscd) ? " -" + TimeFmt(length - newt): TimeFmt(length);
            
        } else {
        t1 = t2 = "-:--";
        }
        
        gr.SetSmoothingMode(2);
        gr.FillSolidRect(this.bar.x, this.bar.y + this.handle_size / 2 + 1, this.bar.w, 1, RGB(67,67,67));
        gr.FillSolidRect(this.bar.x, this.bar.y + this.handle_size / 2 - 1, this.bar.w, 2, RGB(80,80,80));
        gr.FillSolidRect(this.bar.x, this.bar.y + this.handle_size / 2 - 1, pos + 5, 2, RGB(223,232,4));
        gr.FillEllipse(this.bar.x + pos - this.handle_size / 2 - 1, this.bar.y, this.handle_size, this.handle_size, RGB(180,180,180));
        gr.FillEllipse(this.bar.x + pos - this.handle_size / 2, this.bar.y + 1, this.handle_size, this.handle_size, RGB(255,255,255));
        
        var tformat = 0x00000001 | 0x00000004 | 0x00000400 | 0x00000800 | 0x00008000;

        gr.GdiDrawText(t2, this.font, RGB(255,255,255), this.w - this.timewidth, this.y, this.timewidth, this.h, tformat);
        gr.GdiDrawText(t1, this.font, RGB(255,255,255), this.x, this.y, this.timewidth, this.h, tformat);
    }

    this.key_down = function (key) {
        switch (key) {
        }
    }
    
    this.lbtn_down = function (x, y) {
        if (x >= this.bar.x && x < this.bar.x + this.bar.w && y >= this.bar.y && y < this.bar.y + this.handle_size) this.drag = 1;
    }
    
    this.lbtn_up = function (x, y) {
        if (this.drag) {
            this.drag = 0;
            this.seek = (x - this.bar.x) / this.bar.w;
            this.seek = (this.seek < 0) ? 0 : (this.seek < 1) ? this.seek : 1;
            fb.PlaybackTime = fb.PlaybackLength * this.seek;
        } else if (x > this.bar.x + this.bar.w) {
            var val;
            val = this.iscd? false: true;
            window.SetProperty("use_countdown", val)
            this.iscd = val;
        }
    }
    
    this.move = function (x, y) {
        if (this.drag) {
            this.seek = (x - this.bar.x) / this.bar.w;
            this.seek = (this.seek < 0) ? 0 : (this.seek < 1) ? this.seek : 1;
            window.RepaintRect(this.x, this.y, this.w, this.h);
        }
    }
    
    this.refresh = function () {
        if (this.refresh_frame_count != 0) {
            window.ClearInterval(this.refresh_timer);
            this.refresh_frame_count = 0;
            this.refresh_timer = false;
        }
        window.RepaintRect(this.x, this.y, this.w, this.h);
        this.refresh_timer = window.SetInterval(this.refresh_nested, this.refresh_interval);
    }

    this.refresh_nested = function () {
        if (this.refresh_frame_count >= 1000 / this.refresh_interval) {
            window.ClearInterval(this.refresh_timer);
            this.refresh_frame_count = 0;
            this.refresh_timer = false;
        } else {
            this.refresh_frame_count ++;
            window.RepaintRect(this.x, this.y, this.w, this.h);
        }
    }

}

// Panel global variables

var panel = new Array();
panel.push(new seekbar);

function on_paint(gr) {
    for (i = 0; i < panel.length; i++) {
        panel[i].draw(gr);
    }
}

function on_size() {
    panelw = window.Width;
    panelh = window.Height;
    panel[0].init(0, 0, panelw, panelh);
}

function on_key_down(key) {
}

function on_mouse_lbtn_down(x, y) {
    for (i = 0; i < panel.length; i++) {
        if (x >= panel[i].x && x < panel[i].x + panel[i].w && y >= panel[i].y && y < panel[i].y + panel[i].h) {
            panel[i].lbtn_down(x, y);
        }
    }
}

function on_mouse_lbtn_up(x, y) {
    for (i = 0; i < panel.length; i++) {
        if (x >= panel[i].x && x < panel[i].x + panel[i].w && y >= panel[i].y && y < panel[i].y + panel[i].h) {
            panel[i].lbtn_up(x, y);
        }
    }
}

function on_mouse_move(x, y) {
    for (i = 0; i < panel.length; i++) {
        panel[i].move(x, y);
    }
}

function on_mouse_wheel(delta) {
}

function on_playback_starting(cmd, paused){
}

function on_playback_new_track(info){
    panel[0].refresh();
}

function on_playback_stop(){
    window.Repaint();
}

function on_playback_seek(time){
    panel[0].refresh();
}

function on_playback_pause(state){
    panel[0].refresh();
}

function on_playback_edited(){
}

function on_playback_dynamic_info(){
}

function on_playback_dynamic_info_track(){
}

function on_playback_time(time){
    panel[0].refresh();
}

function on_volume_change(val){
}

JScript Panel script discussion/help

Reply #43
You're having a problem with variable scope. That's something you might want to research when you have the time.

Workarounds:

1) When inside your on_playback_seek callback, you're calling your Refresh function using the external name. There's no reason why you can't do the same from inside your SetInterval function.

2) If you google the term I mentioned, you'll find assigning this to another variable works.

Code: [Select]
function timer() {
    this.x = 0;
    var that = this;
    
    window.SetInterval(function () {
        that.x++;
        fb.trace(that.x);
    }, 200);
}

var t = new timer();


3) If you were developing in an environment that supports ECMAscript 5, you could use the bind function. Unfortunately, windows script host only support ECMAscript 3. However, you can work around that by adding a polyfill

If you added that snippet in to your code, you can use it like this...

Code: [Select]
function timer() {
    this.func = function () {
        this.x++;
        fb.trace(this.x);
    }
    
    this.x = 0;
    
    window.SetInterval(this.func.bind(this), 200);
}

var t = new timer();


4) Rather than using a polyfill for each new fangled javascript function you might want to try, you could use a library like lodash which is what I currently do. I have the same issue not being able to use this inside the xmlhttp onreadystatechange function. You can see an example of how I use that here...

https://github.com/19379/js-marc2003/blob/0...ist.js#L693L702

If you do decide to try lodash, get the compat version and import it in your preprocessor section.

I know you didn't ask for other advice but I'm giving it anyway... 

1) I wouldn't bother creating/clearing/recreating timers. I just create one on startup and let it run indefinitely. In a seekbar example, I'd do this...

Code: [Select]
window.SetInterval(function () {
    if (fb.IsPlaying && !fb.IsPaused && fb.PlaybackLength > 0) {
        refresh();
    }
}, 100);


2) This is rather unnecessary.

Code: [Select]
var val;
val = this.iscd? false: true;
window.SetProperty("use_countdown", val)
this.iscd = val;


You can toggle true/false like this and without creating extra variables...

Code: [Select]
this.iscd = !this.iscd
window.SetProperty("use_countdown", this.iscd)


Also, you could tidy up other areas of the code using the same principal as above.

JScript Panel script discussion/help

Reply #44
Thanks for the reply! It's far more useful than I expected.
I'm feeling silly now for the additional advise 2.

JScript Panel script discussion/help

Reply #45
It's official... hell has finally frozen over. Last.fm are now allowing new API key signups.

http://www.last.fm/api/account/create

Before anyone gets too excited, large portions of it are utterly broken with no acknowledgement to the numerous repeated reports on their forums.

JScript Panel script discussion/help

Reply #46
Hi marc2003, thanks for creating JScript Panel!
I wonder is it possible to modify last.fm bio script so that there will be artist photo behind the text? And I cant find where to edit script in order to change text color/placement etc.

JScript Panel script discussion/help

Reply #47
Hi marc2003, thanks for creating JScript Panel!
I wonder is it possible to modify last.fm bio script so that there will be artist photo behind the text? And I cant find where to edit script in order to change text color/placement etc.


OK, I've found other sample script - Now Playing - it is what I was looking for. Only one problem - artist art doesn't download automatically.

JScript Panel script discussion/help

Reply #48
Help needed for 2 questions:

1. Does fb.VolumeMute() have a different behaviour to the main menu command?
When first called, it mutes the volume, but when called for the second time, the volume won't resume.

2. Is there any function to draw a "glowing" shape?
I've seen the "glow text example", but did not find any reference to a shape to do so.

Appreciate any replies.

JScript Panel script discussion/help

Reply #49
Only one problem - artist art doesn't download automatically.


That's by design and I'm not changing it.

1. Does fb.VolumeMute() have a different behaviour to the main menu command?


No. If you put that command in a blank panel, keep the configuration window open and repeatedly click Apply, it toggles the volume between mute and the current value like you would expect.

Quote
I've seen the "glow text example", but did not find any reference to a shape to do so.


If you try the Guifx v2 Transports font, it has a few basic shapes. If that's not good enough, I'd probably use gdi.Image on some image created in another program.

 
SimplePortal 1.0.0 RC1 © 2008-2018