Skip to main content
Topic: CaTRoX (QWR Edition): fb2k theme (Read 11483 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #75
So, this is slightly tangential to the purpose of this thread, but I need some help ripping off your shit ;)

I finally decided I needed to switch from my version of Catrox's playlist circa 2013 to your version, after trying out your theme and seeing how silky smooth everything operated. Really awesome work, and the code is a billion times cleaner. So I copied all the code from your playlist panel, and tried to cram it into my theme.

My theme is just a single JScript panel, with no PSS's to be found. Your playlist assumes that it will take up the entire space of a panel. It wasn't too much work to shrink the playlist to the appropriate size in on_size, but now positioning it is turning out to be a pain in the ass.




It looks like PlaylistManager and Playlist both get initialized with x/y set to 0, but it doesn't look like there's a way to reposition them after creation, and when I try to hack it in the on_size, things go terribly wrong.

Do you have any ideas here? I'll keep plugging away at it, but at the moment I'm not making much progress on this front.

Edit: One other thing you might be able to help with. Everytime I update the playlist (i.e. due to a on_playlist_XXXX event) I get a crash in this.init_rows Fixed this. Hadn't deleted my Row constructor  O:)

Re: CaTRoX (QWR Edition): fb2k theme

Reply #76
Nevermind. Took me about 4 hours, and some head to desk, but I finally got it.
Spoiler (click to show/hide)

I saw you had some comments in initialize_rows about array[ i] being a lot faster, but I noticed you didn't maximize your gains. Using direct index instead of .push ought to show noticeable savings on really large playlists as it's twice as fast in modern browsers. I'd expect IE9's difference might even be more pronounced.

Code: [Select]
function initialize_rows(playlist_items, playlist_size) {
        // Magic! For some reason using array[i] instead of IFbMetadbHandleList.Item(i) greatly increases performance :\
        var playlist_items_arr = [];
        for (var i = 0; i < playlist_size; ++i) {
            playlist_items_arr[i] = playlist_items.Item(i);
        }

        var rows = [];
        for (var i = 0; i < playlist_size; ++i) {
            rows[i] = new Row(that.list_x, 0, that.list_w, that.row_h, playlist_items_arr[i], i, cur_playlist_idx);
            if (!g_properties.show_header) {
                rows[i].is_odd = (i + 1) % 2;
            }
        }

        return rows;
    }

 

Re: CaTRoX (QWR Edition): fb2k theme

Reply #77
I saw you had some comments in initialize_rows about array[ i] being a lot faster, but I noticed you didn't maximize your gains. Using direct index instead of .push ought to show noticeable savings on really large playlists as it's twice as fast in modern browsers. I'd expect IE9's difference might even be more pronounced.
Welp, dunno about twice the performance, but it did yield me 10-15% in rows initialization speed and 6.5-7% in total playlist initialization speed, thanks!

PS: See https://stackoverflow.com/questions/614126/why-is-array-push-sometimes-faster-than-arrayn-value, i.e. it's not always viable to replace push() with [idx] =)

Nevermind. Took me about 4 hours, and some head to desk, but I finally got it.
Congrats =) But, IMO, you should've added this to PlaylistPanel instead (+ pass the x,y to the PlaylistManager and Playlist ctors).

Adding 'Disc' separator Row (like you did in your theme) won't be that simple though...
Also there is another problem: playlist's bottom part is cropped by the panel in my theme. Since you don't have that you'll have to crop it yourself somehow.
Additionally, FontAwesome is needed for some icons (like the Lock icon above the scrollbar).

Fell free to ask if you have any further questions about the script ;)

Re: CaTRoX (QWR Edition): fb2k theme

Reply #78
Welp, dunno about twice the performance, but it did yield me 10-15% in rows initialization speed and 6.5-7% in total playlist initialization speed, thanks!
Awesome! I meant just the insert to the array was 2x over .push. I'm pleasantly surprised the gains were that big (I didn't have time to test last night). Figured it would definitely be noticeable on 1-2k entry playlists.

Congrats =) But, IMO, you should've added this to PlaylistPanel instead (+ pass the x,y to the PlaylistManager and Playlist ctors).

Adding 'Disc' separator Row (like you did in your theme) won't be that simple though...
Also there is another problem: playlist's bottom part is cropped by the panel in my theme. Since you don't have that you'll have to crop it yourself somehow.
Additionally, FontAwesome is needed for some icons (like the Lock icon above the scrollbar).

Fell free to ask if you have any further questions about the script ;)
I mistyped. I added it to the PlaylistPanel.on_size, and not Playlist.on_size. I'm nervous about the disc separator, but I'm also determined, so we'll see how it goes :D

I solved the cropping problem last night. You were already using a clipImg (which wasn't being clipped  8) ) so I updated Playlist.on_paint to have this code:
Code: [Select]
        if (this.items_to_draw.length) {
            var that = this;
            _.forEachRight(this.items_to_draw, function (item) {
    item.draw(gr, that.y, that.y + that.h);
            }, that);
Then I have Header.draw call those two variables top and bottom, and threw this code at the very bottom of Header.draw_normal_header():
Code: [Select]
		clipImg.ReleaseGraphics(grClip);
var y = this.y;
var h = this.h;
var 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(clipImg, this.x, y, this.w, h, 0, srcY, this.w, h, 0, 255);
        clipImg.Dispose();
The individual rows don't draw to a clip image, so no way to clip the top and bottom rows, but your code is already set up to cover up rows if the list has some spacing around it, so I just used that.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #79
BTW, my To-Do list is:
  • Disc header rows - maybe special case of Row, or a new DiscHeader object, can't decide yet
  • Caching artwork on playlist refresh to avoid reloading images
  • playlist_info to bottom of window - I might hate how this ends up looking
  • Various cosmetic stuff you probably won't care about

To avoid spamming up this thread, are you interested in the first three at all? It might be a little hard for me to do PRs in github since I've changed a few things that I know you DON'T want in the main branch, but I can at least open issues and share code snippets there if you prefer. I can also continue pasting stuff here if you like, or just STFU. :) Let me know what you want.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #80
BTW, my To-Do list is:
  • Disc header rows - maybe special case of Row, or a new DiscHeader object, can't decide yet - actually I've wanted to implement it myself =)
  • Caching artwork on playlist refresh to avoid reloading images - the idea is nice, but it all depends on the implementation (I'm not a fan of quick and dirty solutions =) )
  • playlist_info to bottom of window - I might hate how this ends up looking - this won't be compatible with my theme's looks, so that's a no

To avoid spamming up this thread, are you interested in the first three at all? It might be a little hard for me to do PRs in github since I've changed a few things that I know you DON'T want in the main branch, but I can at least open issues and share code snippets there if you prefer. I can also continue pasting stuff here if you like, or just STFU. :) Let me know what you want.
New feature suggestions or suggestions about modifying the existing ones are fine here, I think.


Re: CaTRoX (QWR Edition): fb2k theme

Reply #82
After a lot of thinking, I went with the simpler solution of making discHeader just a special case of Row. I'm not entirely happy with this, but the interaction with Header got complicated, and it allowed me to handle one special case really well.

Code: [Select]
    function initialize_rows(playlist_items, playlist_size) {
        // Magic! For some reason using array[i] instead of IFbMetadbHandleList.Item(i) greatly increases performance :\
        var playlist_items_arr = [];
        for (var i = 0; i < playlist_size; ++i) {
            playlist_items_arr[i] = playlist_items.Item(i);
        }

var rows = [];
var disc, lastDisc = '';
var tfo = fb.TitleFormat('$ifgreater(%totaldiscs%,1,,false)[' + tf.disc_subtitle + ']');
var disc_group = fb.TitleFormat('%album artist% %album% %edition% %discnumber%' + tf.disc_subtitle);

for (var i = 0; i < playlist_size; ++i) {
discHeader = false;
if (tfo.EvalWithMetadb(playlist_items_arr[i]) !== 'false') {
disc = disc_group.EvalWithMetadb(playlist_items_arr[i]);
if (disc != lastDisc) {
discHeader = true;
lastDisc = disc;
}
}
            rows[i] = new Row(that.list_x, 0, that.list_w, discHeader ? that.row_h * 2 : that.row_h, playlist_items_arr[i], i, cur_playlist_idx, discHeader);
            if (!g_properties.show_header) {
                rows[i].is_odd = (i + 1) % 2;
}
}
_.dispose(tfo);
_.dispose(disc_group);

        return rows;
    }
So discHeaders are a row that's double height, and in the Row.draw I just check for the discHeader flag, and adjust what and where I draw the text. This solution handles multi-disc releases with and without subtitles, single disc releases with a subtitle (I just don't show the Disc 1 in that case), and if for example, I've got a single disc reissue which combines multiple releases, I can just tag tracks 10-15 with a discsubtitle of "1985 Demo" and the header will show in the middle of the track list which is what I want.

The one problem with doing this is that it almost doubles the speed of initialize_rows, although I can still load 30k entries in <700ms which seems acceptable. I also think that's a problem even creating a DiscHeader object type would still run into because you're going to be forced to do a bunch of TF queries. The other problem is that I can't think of a "clean" way to get the run time of the individual disc, something that would be much simpler with DiscHeader.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #83
Well, I didn't think this thoroughly yet, but I wanted disc sub-header to be something like grouping header, i.e. selecting entirety of one disc, when clicked on, maybe collapsible and surely 100% optional. The actual implementation idea is yet to form, but we'll see how it goes...

Re: CaTRoX (QWR Edition): fb2k theme

Reply #84
No, of course. Like I said, I went for the simplest solution at the moment. Doing it my way, I miss out on collapsible/selectable functionality which would be great to have... and it's much uglier if you want it disabled or if you're already grouping by discnumber.

I also ultimately decided that this early in my theme development I didn't want to drift too far from your main branch if I could help it :)

Just now though I did have another idea of how to approach this as it's own item when the groups are being created, so I might give this another pass and see how difficult it is.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #85
So I'm trying to implement DiscHeaders using the new BaseHeader stuff, but I'm running into some issues with interactions with sub_items and iterate_level() inside generate_all_items_to_draw().

I'm doing my DiscHeaders inside Headers' initialize_items() method. This allows me to create them in one pass which seemed far more efficient than my old method. It works so long as I don't have to scroll or calculate what to draw:

Here's my code:
Code: [Select]
    this.initialize_items = function (rows_to_process) {
        this.sub_items = [];
        if (!rows_to_process.length) {
            return 0;
        }

        var rows_processed = 0;
        var disc_header = undefined;
        var header = this;
        var num_disc_headers = 0;

        var query = grouping_handler.get_query();
        var tfo = fb.TitleFormat(query ? query : ''); // workaround a bug, because of which '' is sometimes treated as null :\

        var disc_tfo = fb.TitleFormat('$ifgreater(%totaldiscs%,1,%discnumber%,)[%discsubtitle%]');
        var disc_group = disc_tfo.EvalWithMetadb(_.head(rows_to_process).metadb);
        if (disc_group.length) {
            disc_header = new DiscHeader(this.x, 0, this.w, g_properties.row_h, _.head(rows_to_process).metadb, 0);
            disc_header.parent = this;
            disc_header.is_odd = true;
            disc_header.idx_in_header = num_disc_headers++;
            disc_header.idx = 0;
            header = disc_header;
            this.sub_items.push(disc_header);
        }

        var group = tfo.EvalWithMetadb(_.head(rows_to_process).metadb);
        _.forEach(rows_to_process, _.bind(function (item, i) {
            var cur_group = tfo.EvalWithMetadb(item.metadb);
            if (group !== cur_group) {
                return false;
            }
            var cur_disc = disc_tfo.EvalWithMetadb(item.metadb);
            if (cur_disc !== disc_group) {
                disc_group = cur_disc;
                if (disc_header) {
                    disc_header.initialize_items(disc_header.sub_items);
                }
                disc_header = new DiscHeader(this.x, 0, this.w, g_properties.row_h, item.metadb, 0);
                disc_header.parent = this;
                disc_header.idx_in_header = i + num_disc_headers++;
                disc_header.idx = num_disc_headers;
                header = disc_header;
                this.sub_items.push(disc_header);
            }
            item.idx_in_header = i + num_disc_headers;
            if (g_properties.show_header) {
                item.is_odd = !(i & 1);
            }
            item.parent = header;
            header.sub_items.push(item);
            rows_processed++;
        }, this));

        if (disc_header) {
            disc_header.initialize_items(disc_header.sub_items);
        }

        _.dispose(tfo);
        _.dispose(disc_tfo);

        metadb = _.head(this.sub_items).metadb;

        return rows_processed;
    };

Here's my confusion. What do idx and idx_in_header mean for DiscHeaders? Say I've got the following layout:

Header1
 * DiscHeader1
 - - Row1
 - - Row2
 * DiscHeader2
 - - Row3

Does the Header1 just have two sub_items (DiscHeader1 and 2) or does it have 5? DiscHeader1 presumably has two sub_items, right? Does Row3's idx_in_header referring to DiscHeader2 or Header? i.e. is does Row3 have idx_in_header = 0, 3, or 5?

I coded this assuming that Header1 would have two sub_items, and DiscHeader1 would also have two sub_items, but something isn't working right (or I've got a bug I haven't spotted).

Re: CaTRoX (QWR Edition): fb2k theme

Reply #86
Here's my confusion. What do idx and idx_in_header mean for DiscHeaders? Say I've got the following layout:

Header1
 * DiscHeader1
 - - Row1
 - - Row2
 * DiscHeader2
 - - Row3

Does the Header1 just have two sub_items (DiscHeader1 and 2) or does it have 5? DiscHeader1 presumably has two sub_items, right? Does Row3's idx_in_header referring to DiscHeader2 or Header? i.e. is does Row3 have idx_in_header = 0, 3, or 5?

I coded this assuming that Header1 would have two sub_items, and DiscHeader1 would also have two sub_items, but something isn't working right (or I've got a bug I haven't spotted).
For BaseHeader derived objects idx field contains the position of the object in the parent's sub_items field.
For Row objects idx contains a track's index in playlist instead. So idx_in_header is used to deduce it's position in parent. I.e. only Row objects have idx_in_header field.

sub_items hierarchy:

PlaylistContent.sub_items = [
Header1.sub_items = [
 * DiscHeader1.sub_items = [
 - - Row1
 - - Row2
  ];
 * DiscHeader2.sub_items = [
 - - Row3
  ];
 ];
];

Index enumeration:

Header1 | idx: 0
 * DiscHeader1 | idx: 0
 - - Row1 | idx_in_header: 0, idx: 0
 - - Row2 | idx_in_header: 1, idx: 1
 * DiscHeader2 | idx: 1
 - - Row3 | idx_in_header: 0, idx: 2

Re: CaTRoX (QWR Edition): fb2k theme

Reply #87
Thanks for the info, updated my DiscHeader initialization accordingly.

You've got a bug in all three instances of your iterate_level code. Took me a while to find it, but basically you can remove every assignment of:
var is_next_level_row = ...

and then replace all 3 lines that are
Code: [Select]
    if (!is_next_level_row) {
with
Code: [Select]
    if (_.isInstanceOf(_.head(header.sub_items), BaseHeader)) {

That'll get you iterating through every BaseHeader, and not just if the current BaseHeader's children are rows. Once I made the change magically everything started working.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #88
Once I made the change magically everything started working.
Not really magically, since I've tested the code with some dummy nested headers =)

Actually I've wanted to make this change at one point of the time, but kinda forgot >_<
But thanks for pointing it out ;)

Re: CaTRoX (QWR Edition): fb2k theme

Reply #89
NP! There's still one issue with nested headers in the following case:

Header
 - Row 0
 - Row 1
 - DiscHeader 0
 - - Row 2

Your code as written will show DiscHeader0, but never draw any of its children. I've created a fix for it in generate_all_items_to_draw(), but for reasons I haven't fully debugged yet, if DiscHeader 0 is the first row to draw (due to scrolling) then nothing else in the playlist draws outside that DiscHeader. I should hopefully have a fix for that later today or tonight and I can share the code. This stuff can get complex! :D

Re: CaTRoX (QWR Edition): fb2k theme

Reply #90
Found a crash in QueueHandler. If I enqueue something from a filter, and then change filters, the PlaylistItemIndex of the first item is -1 (not found) and cur_queued_row is undefined, and we crash trying to reference it's queue_idx.

Inside queue_contents.forEach first lines should be:
Code: [Select]
            if (queued_item.PlaylistIndex !== cur_playlist_idx || queued_item.PlaylistItemIndex === -1) {
                return;
            }

Re: CaTRoX (QWR Edition): fb2k theme

Reply #91
Inside queue_contents.forEach first lines should be:
Code: [Select]
            if (queued_item.PlaylistIndex !== cur_playlist_idx || queued_item.PlaylistItemIndex === -1) {
                return;
            }
Thanks! Never would've caught that one, since I use only static playlists =)

Re: CaTRoX (QWR Edition): fb2k theme

Reply #92
Okay, this one's kind of a doozy. I was able to fix the draw issue with a Header whose sub_items consists of Rows, and then a BaseHeader with its own Rows. Unfortunately the code was really unmanageable, so I took a step back and rewrote generate_all_items_to_draw(), and managed to cut iterate_levels in half, and the code is a lot more intuitive IMO. No more checking isInstanceOf, no more iterating through header_rows.sub_items individually... just find the header of first_item, then start at that header, and loop through all its sub_items, and siblings until cur_y > playlist height.

Code: [Select]
    function generate_all_items_to_draw(wy, wh, first_item) {
        var items_to_draw = [first_item];
        var cur_y = first_item.y + first_item.h;

        var done = false;
        var start_item_found = false;
        var first_item_header = first_item;
        while (first_item_header && first_item_header.parent !== _.head(that.sub_items).parent) {
            first_item_header = first_item_header.parent;
        }

        /**
         *
         * @param {Array<BaseHeader>|Array<Row>} sub_items
         * @param {Row|BaseHeader} start_item
         * @return {boolean} true, if start_item was used
         */
        function iterate_level(sub_items, start_item) {
            var start_idx = 0;
            if (first_item_header) {
                start_idx = first_item_header.idx;
                first_item_header = null;
            }
            for (var i = start_idx; i < sub_items.length; i++) {
                var item = sub_items[i];
                if (start_item_found) {
                    item.set_y(cur_y);

                    items_to_draw.push(item);
                    cur_y += item.h;
                }

                if (item === start_item) {
                    start_item_found = true;
                }

                if (cur_y >= wy + wh) {
                    done = true;
                    break;
                }

                if (item.is_collapsed) {
                    continue;
                }

                if (item.sub_items && item.sub_items.length) {
                    done = iterate_level(item.sub_items, start_item);
                    if (done) {
                        break;
                    }
                }
            }
            return done;
        }

        iterate_level(that.sub_items, first_item);

        return items_to_draw;
    }

I was feeling pretty good, and set out to rewrite generate_first_item_to_draw(), but my result was 10x-20x slower on very large playlists, and had to be scrapped. This means there's a "bug" in the Rows before BaseHeader case where the sub_items of BaseHeader can never be set as first_item_to_draw. I can't think of a way around this. It ought to be a really uncommon use case (mainly bad tagging) so I'm happy to live with it, but if you can figure out a way to target those rows without iterating through every. single. item. in the list, then you're a better programmer than I am.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #93
Okay, this one's kind of a doozy. I was able to fix the draw issue with a Header whose sub_items consists of Rows, and then a BaseHeader with its own Rows.
I'm not sure what draw issues are you talking about: I've just implemented a quick and dirty CD subheader and it works without any bugs :\
See https://github.com/TheQwertiest/CaTRoX_QWR/commit/382f2ee14043d8a57fb69e7ee237c4b2ef285f61


just find the header of first_item, then start at that header, and loop through all its sub_items, and siblings until cur_y > playlist height.
...
It ought to be a really uncommon use case
That is not really a performance-friendly solution: what if a header has a lot of items? Don't forget that header query is customizable, so it might not have any relation to artist, album name or cd number (i.e. it might have nothing to do with bad tagging).

You are correct, though, in that it might be possible to simplify iterator by collapsing Row iteration into the generic iteration.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #94
Yours works without bugs because you ensure that if a Header has a TestHeader, then ALL of it's sub_items are TestHeaders. My code doesn't make the same assumption, mainly because I add DiscHeaders when I encounter a different %disc_subtitle% tag, and I prefer not to show a DiscHeader in the single disc case. e.g.: https://i.imgur.com/7K5C6Bm.png

I do this by building out DiscHeaders in the Header.initialize_items meaning I only need to make a single pass through every item in the playlist, where yours will make two passes. Performance wise I feel that's a bigger concern than if people are grouping things in dumb ways :)

I'm thinking now that it may not be worth the trouble though and I should follow your method of enforcing all sub_items to be the same type. Your initialization is cleaner than mine, and you can make smarter assumptions in that case.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #95
Yours works without bugs because you ensure that if a Header has a TestHeader, then ALL of it's sub_items are TestHeaders.
Well, yeah =)
https://github.com/TheQwertiest/CaTRoX_QWR/blob/84c17d9d393be36d29838f7ca56bcca71d82c7a8/theme/Scripts/Panel_Playlist.js#L2932

I do this by building out DiscHeaders in the Header.initialize_items meaning I only need to make a single pass through every item in the playlist, where yours will make two passes. Performance wise I feel that's a bigger concern than if people are grouping things in dumb ways :)
It's just a quick mock-up (thus the 'TestHeader' class name), so it's initialization code is not optimal as well. Also see https://github.com/TheQwertiest/CaTRoX_QWR/commit/08665c72ab48008df280459a10be200f60cae69b

I prefer not to show a DiscHeader in the single disc case. e.g.: https://i.imgur.com/7K5C6Bm.png
...
I'm thinking now that it may not be worth the trouble though and I should follow your method of enforcing all sub_items to be the same type.
I don't see any reason for sub_items elements not to have the same type. If you have some tracks in the Header that do not have CD#, but all other tracks in that Header do, then, IMO, it's much cleaner UI-wise to give those untagged tracks a fake CD sub-header (like it is done in my mock-up implementation), since it will be hard to visually separate those track groups from each other otherwise (e.g. CD-numbered track group followed by unnumbered track group). Also, I think that such scenario is exactly the case of bad-tagging :P

Re: CaTRoX (QWR Edition): fb2k theme

Reply #96
I don't see any reason for sub_items elements not to have the same type. If you have some tracks in the Header that do not have CD#, but all other tracks in that Header do, then, IMO, it's much cleaner UI-wise to give those untagged tracks a fake CD sub-header (like it is done in my mock-up implementation), since it will be hard to visually separate those track groups from each other otherwise (e.g. CD-numbered track group followed by unnumbered track group). Also, I think that such scenario is exactly the case of bad-tagging :P
Haha, whether it was caused by bad tagging or was done intentionally, I was trying to handle it properly! Also, from a UI/UX perspective, I don't think there's any difficulty visually separating those groups in the screen shot I posted, but to each their own. :)

I think what I'm going to do is follow the TestHeader model for my DiscHeaders, and create fake DiscHeaders in the outlier case (in 2500 albums I think I have 5 or 6 that I want displayed this way), but flag them with a .do_not_draw property and then skip entries with that flag in the various iterate_levels.

In other experiments, I spent last night and this morning working on creating "hyperlinks" in the Header. I've got it working so I can click on Year and Artist to load a playlist with just albums with that Year or Artist. I'm planning on doing album and record labels as well. It's been kind of a pain in the ass because I've had to reimplement a bunch of on_mouse_ methods for Header, but it's starting to come together.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #97
Hello,

I'm using an alternative font rendering engine on windows and was wondering why it doesn't apply to anything except for library tree and lyrics panel on your skin. It works okay on another skin that is purely jscript.

Re: CaTRoX (QWR Edition): fb2k theme

Reply #98
I'm using an alternative font rendering engine on windows and was wondering why it doesn't apply to anything except for library tree and lyrics panel on your skin. It works okay on another skin that is purely jscript.
Well, all of the fonts I'm using in the theme are hard-coded (Segoe UI usually), so fb2k settings (and consequently Windows settings) do not affect the theme.
The only way to use custom fonts is to change them manually in the script files (most of the fonts are defined on the top of the script).

Re: CaTRoX (QWR Edition): fb2k theme

Reply #99
I've changed the font to a different one, but the rendering engine hasn't. Well, I guess the problem may be elsewhere. Thanks anyway. :)

 
SimplePortal 1.0.0 RC1 © 2008-2018