blockbench/js/interface/themes.js

482 lines
14 KiB
JavaScript
Raw Normal View History

2019-12-16 03:04:31 +08:00
const CustomTheme = {
data: {
2021-08-24 04:17:59 +08:00
id: 'dark',
name: '',
author: '',
2021-08-24 21:05:51 +08:00
customized: false,
borders: false,
2019-12-16 03:04:31 +08:00
main_font: '',
headline_font: '',
code_font: '',
css: '',
2021-07-31 19:52:00 +08:00
colors: {},
2019-12-16 03:04:31 +08:00
},
2021-08-24 04:17:59 +08:00
themes: [
2021-08-08 20:51:00 +08:00
...CustomThemeOptions
],
2019-12-16 03:04:31 +08:00
defaultColors: {
ui: '#282c34',
back: '#21252b',
dark: '#17191d',
border: '#181a1f',
2021-05-22 05:48:39 +08:00
selected: '#474d5d',
2019-12-16 03:04:31 +08:00
button: '#3a3f4b',
bright_ui: '#f4f3ff',
accent: '#3e90ff',
2020-04-26 02:25:07 +08:00
frame: '#181a1f',
2019-12-16 03:04:31 +08:00
text: '#cacad4',
light: '#f4f3ff',
accent_text: '#000006',
2021-08-28 00:41:18 +08:00
bright_ui_text: '#000006',
2021-01-02 22:08:07 +08:00
subtle_text: '#848891',
2019-12-16 03:04:31 +08:00
grid: '#495061',
wireframe: '#576f82',
checkerboard: '#1c2026',
2019-12-16 03:04:31 +08:00
},
2021-08-24 21:05:51 +08:00
sideloaded_themes: [],
2019-12-16 03:04:31 +08:00
setup() {
function saveChanges() {
localStorage.setItem('theme', JSON.stringify(CustomTheme.data));
}
2021-08-24 21:05:51 +08:00
if (isApp && localStorage.getItem('themes_sideloaded')) {
try {
let sideloaded = JSON.parse(localStorage.getItem('themes_sideloaded'));
if (sideloaded instanceof Array && sideloaded.length) {
CustomTheme.sideloaded_themes = sideloaded;
Blockbench.read(CustomTheme.sideloaded_themes, {}, files => {
files.forEach(file => {
let data = JSON.parse(file.content);
data.id = file.name.replace(/\.\w+$/, '');
if (!data.name) data.name = data.id;
CustomTheme.themes.push(data);
})
})
}
} catch (err) {}
}
2019-12-16 03:04:31 +08:00
2021-07-31 19:52:00 +08:00
CustomTheme.dialog = new Dialog({
id: 'theme',
title: 'dialog.settings.theme',
singleButton: true,
width: 920,
title_menu: new Menu([
'settings_window',
'keybindings_window',
'theme_window',
'about_window',
]),
sidebar: {
pages: {
2021-08-08 20:51:00 +08:00
select: tl('layout.select'),
2021-08-24 04:17:59 +08:00
options: tl('layout.options'),
2021-07-31 19:52:00 +08:00
color: tl('layout.color'),
css: tl('layout.css'),
2019-12-16 03:04:31 +08:00
},
2021-08-08 20:51:00 +08:00
page: 'select',
2021-07-31 19:52:00 +08:00
actions: [
{
name: 'layout.documentation',
icon: 'fa-book',
click() {
2019-12-16 03:04:31 +08:00
}
2021-07-31 19:52:00 +08:00
},
'import_theme',
'export_theme',
],
onPageSwitch(page) {
CustomTheme.dialog.content_vue.open_category = page;
if (page == 'color' && !CustomTheme.dialog_is_setup) {
CustomTheme.setupDialog()
}
}
},
onOpen() {
if (!CustomTheme.remote_themes_loaded) {
CustomTheme.remote_themes_loaded = true;
$.getJSON('https://api.github.com/repos/JannisX11/blockbench-themes/contents/themes').then(files => {
files.forEach(async file => {
let {content} = await $.getJSON(file.git_url);
let theme = JSON.parse(atob(content));
theme.id = file.name.replace(/\.\w+/, '');
CustomTheme.themes.push(theme);
})
}).catch(console.error)
2021-08-24 21:05:51 +08:00
}
},
2021-07-31 19:52:00 +08:00
component: {
data: {
data: CustomTheme.data,
2021-08-08 20:51:00 +08:00
open_category: 'select',
2021-08-24 04:17:59 +08:00
themes: CustomTheme.themes
2021-07-31 19:52:00 +08:00
},
components: {
VuePrismEditor
},
watch: {
'data.main_font'() {
2021-08-24 04:17:59 +08:00
CustomTheme.updateSettings();
2021-07-31 19:52:00 +08:00
saveChanges();
},
'data.headline_font'() {
2021-08-24 04:17:59 +08:00
CustomTheme.updateSettings();
2019-12-16 03:04:31 +08:00
saveChanges();
},
2021-07-31 19:52:00 +08:00
'data.code_font'() {
2021-08-24 04:17:59 +08:00
CustomTheme.updateSettings();
2021-07-31 19:52:00 +08:00
saveChanges();
},
'data.css'() {
2021-08-24 04:17:59 +08:00
CustomTheme.updateSettings();
2021-07-31 19:52:00 +08:00
saveChanges();
},
'data.colors': {
handler() {
2021-08-24 04:17:59 +08:00
CustomTheme.updateColors();
2021-07-31 19:52:00 +08:00
saveChanges();
},
deep: true
}
2019-12-16 03:04:31 +08:00
},
2021-08-24 04:17:59 +08:00
methods: {
selectTheme(theme) {
CustomTheme.loadTheme(theme);
saveChanges();
2021-08-24 04:17:59 +08:00
},
2021-08-24 21:05:51 +08:00
customizeTheme() {
CustomTheme.customizeTheme();
},
2021-08-24 04:17:59 +08:00
getThemeThumbnailStyle(theme) {
let style = {};
for (let key in theme.colors) {
style[`--color-${key}`] = theme.colors[key];
}
return style;
}
},
2021-08-24 21:05:51 +08:00
computed: {
listed_themes() {
let themes = this.themes.slice();
if (this.data.customized) {
themes.splice(0, 0, this.data);
}
return themes;
}
},
2021-07-31 19:52:00 +08:00
template: `
<div id="theme_editor">
2021-08-08 20:51:00 +08:00
<div v-if="open_category == 'select'">
<h2 class="i_b">${tl('layout.select')}</h2>
<div id="theme_list">
2021-08-24 21:05:51 +08:00
<div v-for="theme in listed_themes" :key="theme.id" class="theme" :class="{selected: theme.id == data.id}" @click="selectTheme(theme)">
<div class="theme_preview" :class="{borders: theme.borders}" :style="getThemeThumbnailStyle(theme)">
<div class="theme_preview_header">
<span class="theme_preview_text" style="width: 20px;" />
<div class="theme_preview_menu_header">
<span class="theme_preview_text" style="width: 34px;" />
</div>
<span class="theme_preview_text" style="width: 45px;" />
</div>
<div class="theme_preview_menu">
<span class="theme_preview_text" style="width: 23px;" />
<span class="theme_preview_text" style="width: 16px;" />
<span class="theme_preview_text" style="width: 40px;" />
</div>
<div class="theme_preview_window">
<div class="theme_preview_sidebar"></div>
<div class="theme_preview_center"></div>
<div class="theme_preview_sidebar"></div>
</div>
</div>
2021-08-24 04:17:59 +08:00
<div class="theme_name">{{ theme.name }}</div>
2021-08-24 21:05:51 +08:00
<div class="theme_author">{{ theme.author }}</div>
2021-08-08 20:51:00 +08:00
</div>
</div>
2021-07-31 19:52:00 +08:00
</div>
<div v-show="open_category == 'color'">
<h2 class="i_b">${tl('layout.color')}</h2>
<div id="color_wrapper">
<div class="color_field" v-for="(color, key) in data.colors" :id="'color_field_' + key">
<div class="layout_color_preview color_input" :style="{'background-color': color}"></div>
<div class="desc">
<h4>{{ tl('layout.color.'+key) }}</h4>
<p>{{ tl('layout.color.'+key+'.desc') }}</p>
</div>
</div>
</div>
</div>
2021-08-24 04:17:59 +08:00
<div v-if="open_category == 'options'">
<h2 class="i_b">${tl('layout.options')}</h2>
2021-08-24 21:05:51 +08:00
<div class="dialog_bar" v-if="data.customized">
2021-08-24 04:17:59 +08:00
<label class="name_space_left" for="layout_name">${tl('layout.name')}</label>
2021-08-24 21:05:51 +08:00
<input @input="customizeTheme($event)" type="text" class="half dark_bordered" id="layout_name" v-model="data.name">
2021-08-24 04:17:59 +08:00
</div>
2021-08-24 21:05:51 +08:00
<div class="dialog_bar" v-if="data.customized">
2021-08-24 04:17:59 +08:00
<label class="name_space_left" for="layout_name">${tl('layout.author')}</label>
2021-08-24 21:05:51 +08:00
<input @input="customizeTheme($event)" type="text" class="half dark_bordered" id="layout_name" v-model="data.author">
2021-08-24 04:17:59 +08:00
</div>
<hr />
2021-07-31 19:52:00 +08:00
<div class="dialog_bar">
<label class="name_space_left" for="layout_font_main">${tl('layout.font.main')}</label>
2021-08-24 21:05:51 +08:00
<input @input="customizeTheme($event)" style="font-family: var(--font-main)" type="text" class="half dark_bordered" id="layout_font_main" v-model="data.main_font">
2021-07-31 19:52:00 +08:00
</div>
<div class="dialog_bar">
<label class="name_space_left" for="layout_font_headline">${tl('layout.font.headline')}</label>
2021-08-24 21:05:51 +08:00
<input @input="customizeTheme($event)" style="font-family: var(--font-headline)" type="text" class="half dark_bordered" id="layout_font_headline" v-model="data.headline_font">
2021-07-31 19:52:00 +08:00
</div>
<div class="dialog_bar">
<label class="name_space_left" for="layout_font_cpde">${tl('layout.font.code')}</label>
2021-08-24 21:05:51 +08:00
<input @input="customizeTheme($event)" style="font-family: var(--font-code)" type="text" class="half dark_bordered" id="layout_font_cpde" v-model="data.code_font">
2021-07-31 19:52:00 +08:00
</div>
</div>
<div v-if="open_category == 'css'">
<h2 class="i_b">${tl('layout.css')}</h2>
<div id="css_editor">
2021-08-24 21:05:51 +08:00
<vue-prism-editor v-model="data.css" @change="customizeTheme(1, $event)" language="css" :line-numbers="true" />
2021-07-31 19:52:00 +08:00
</div>
</div>
</div>`
},
onButton() {
Settings.save();
2019-12-16 03:04:31 +08:00
}
})
},
2021-02-20 05:10:56 +08:00
setupDialog() {
var wrapper = $('#color_wrapper');
for (var key in CustomTheme.defaultColors) {
(() => {
var scope_key = key;
var hex = CustomTheme.data.colors[scope_key];
var last_color = hex;
var field = wrapper.find(`#color_field_${scope_key} .layout_color_preview`);
2021-02-20 05:10:56 +08:00
field.spectrum({
preferredFormat: "hex",
color: hex,
showAlpha: false,
showInput: true,
defaultColor: CustomTheme.defaultColors[key],
resetText: tl('generic.reset'),
cancelText: tl('dialog.cancel'),
chooseText: tl('dialog.confirm'),
2021-02-20 05:10:56 +08:00
move(c) {
CustomTheme.data.colors[scope_key] = c.toHexString();
2021-08-24 21:05:51 +08:00
CustomTheme.customizeTheme();
2021-02-20 05:10:56 +08:00
},
change(c) {
last_color = c.toHexString();
},
hide(c) {
CustomTheme.data.colors[scope_key] = last_color;
field.spectrum('set', last_color);
2021-03-02 02:56:14 +08:00
},
beforeShow(a, b) {
last_color = CustomTheme.data.colors[scope_key];
field.spectrum('set', last_color);
2021-02-20 05:10:56 +08:00
}
});
})()
}
CustomTheme.dialog_is_setup = true;
},
2021-08-24 21:05:51 +08:00
customizeTheme() {
if (!CustomTheme.data.customized) {
CustomTheme.data.customized = true;
CustomTheme.data.name = CustomTheme.data.name ? ('Copy of ' + CustomTheme.data.name) : 'Custom Theme';
CustomTheme.data.author = settings.username.value;
CustomTheme.data.id = 'custom_theme';
let i = 0;
while (CustomTheme.themes.find(theme => theme.id == CustomTheme.data.id)) {
i++;
CustomTheme.data.id = 'custom_theme_'+i;
}
localStorage.setItem('theme', JSON.stringify(CustomTheme.data));
}
},
2021-08-24 04:17:59 +08:00
updateColors() {
for (var key in CustomTheme.data.colors) {
var hex = CustomTheme.data.colors[key];
document.body.style.setProperty('--color-'+key, hex);
}
$('meta[name=theme-color]').attr('content', CustomTheme.data.colors.frame);
2019-12-16 03:04:31 +08:00
2021-08-24 04:17:59 +08:00
if (typeof gizmo_colors != 'undefined') {
var c_outline = parseInt('0x'+CustomTheme.data.colors.accent.replace('#', ''))
if (c_outline !== gizmo_colors.outline.getHex()) {
gizmo_colors.outline.set(c_outline)
Canvas.outlineMaterial.color = gizmo_colors.outline
2019-12-16 03:04:31 +08:00
}
2021-08-24 04:17:59 +08:00
var c_wire = parseInt('0x'+CustomTheme.data.colors.wireframe.replace('#', ''))
if (c_wire !== gizmo_colors.wire.getHex()) {
gizmo_colors.wire.set(c_wire);
Canvas.wireframeMaterial.color = gizmo_colors.wire;
2019-12-16 03:04:31 +08:00
}
2021-08-24 04:17:59 +08:00
var c_grid = parseInt('0x'+CustomTheme.data.colors.grid.replace('#', ''))
if (c_grid !== gizmo_colors.grid.getHex()) {
gizmo_colors.grid.set(c_grid);
three_grid.children.forEach(c => {
if (c.name === 'grid' && c.material) {
c.material.color = gizmo_colors.grid;
}
})
2019-12-16 03:04:31 +08:00
}
}
2021-08-24 04:17:59 +08:00
},
updateSettings() {
document.body.style.setProperty('--font-custom-main', CustomTheme.data.main_font);
document.body.style.setProperty('--font-custom-headline', CustomTheme.data.headline_font);
document.body.style.setProperty('--font-custom-code', CustomTheme.data.code_font);
$('style#theme_css').text(CustomTheme.data.css);
},
loadTheme(theme) {
var app = CustomTheme.data;
Merge.string(app, theme, 'id')
Merge.string(app, theme, 'name')
Merge.string(app, theme, 'author')
Merge.boolean(app, theme, 'borders')
2021-08-24 04:17:59 +08:00
Merge.string(app, theme, 'main_font')
Merge.string(app, theme, 'headline_font')
Merge.string(app, theme, 'code_font')
for (var key in app.colors) {
if (theme.colors[key]) {
Merge.string(app.colors, theme.colors, key);
} else {
CustomTheme.data.colors[key] = CustomTheme.defaultColors[key];
}
2019-12-16 03:04:31 +08:00
}
2021-08-24 04:17:59 +08:00
Merge.string(app, theme, 'css');
2021-08-24 21:05:51 +08:00
app.customized = false;
2021-08-24 04:17:59 +08:00
this.updateColors();
this.updateSettings();
2019-12-16 03:04:31 +08:00
},
import(file) {
var data = JSON.parse(file.content)
var app = CustomTheme.data;
if (pathToExtension(file.path) == 'bbstyle') {
//legacy import
if (data.main) app.main_font = data.main.font;
if (data.headline) app.headline_font = data.headline.font;
if (data.css) app.css = data.css;
for (var key in app.colors) {
if (data[key] && data[key].hex) {
app.colors[key] = data[key].hex;
}
}
if (data.text_acc) {
2021-08-28 00:41:18 +08:00
app.colors.accent_text = data.text_acc;
app.colors.bright_ui_text = data.text_acc;
2019-12-16 03:04:31 +08:00
}
2021-08-24 21:05:51 +08:00
} else if (data && data.colors) {
data.id = file.name.replace(/\.\w+$/, '');
if (!data.name) data.name = data.id;
CustomTheme.loadTheme(data);
CustomTheme.themes.push(data);
if (isApp) {
CustomTheme.sideloaded_themes.push(file.path);
localStorage.setItem('themes_sideloaded', JSON.stringify(CustomTheme.sideloaded_themes));
2019-12-16 03:04:31 +08:00
}
}
}
2021-08-24 04:17:59 +08:00
};
(function() {
var stored_theme = 0;
try {
if (localStorage.getItem('theme')) {
stored_theme = JSON.parse(localStorage.getItem('theme'))
}
} catch (err) {}
for (var key in CustomTheme.defaultColors) {
CustomTheme.data.colors[key] = CustomTheme.defaultColors[key];
}
if (stored_theme) {
CustomTheme.loadTheme(stored_theme);
2021-08-24 21:05:51 +08:00
if (stored_theme.customized) CustomTheme.data.customized = true;
2021-08-24 04:17:59 +08:00
}
})()
2019-12-16 03:04:31 +08:00
BARS.defineActions(function() {
new Action('theme_window', {
name: tl('dialog.settings.theme') + '...',
icon: 'style',
category: 'blockbench',
click: function () {
CustomTheme.dialog.show();
}
})
2019-12-16 03:04:31 +08:00
new Action('import_theme', {
icon: 'folder',
category: 'blockbench',
click: function () {
Blockbench.import({
2021-04-26 01:40:15 +08:00
resource_id: 'config',
2019-12-16 03:04:31 +08:00
extensions: ['bbstyle', 'bbtheme'],
type: 'Blockbench Theme'
}, function(files) {
CustomTheme.import(files[0]);
})
}
})
new Action('export_theme', {
icon: 'style',
category: 'blockbench',
click: function () {
2021-08-24 21:05:51 +08:00
let theme = {};
Object.assign(theme, CustomTheme.data);
delete theme.customized;
delete theme.id;
2019-12-16 03:04:31 +08:00
Blockbench.export({
2021-04-26 01:40:15 +08:00
resource_id: 'config',
2019-12-16 03:04:31 +08:00
type: 'Blockbench Theme',
extensions: ['bbtheme'],
2021-08-24 21:05:51 +08:00
name: theme.id,
2021-04-26 01:40:15 +08:00
content: compileJSON(CustomTheme.data)
2019-12-16 03:04:31 +08:00
})
}
})
//Only interface
new Action('reset_layout', {
icon: 'replay',
category: 'blockbench',
click: function () {
Interface.data = $.extend(true, {}, Interface.default_data)
Interface.data.left_bar.forEach((id) => {
$('#left_bar').append(Interface.Panels[id].node)
})
Interface.data.right_bar.forEach((id) => {
$('#right_bar').append(Interface.Panels[id].node)
})
updateInterface()
}
})
BarItems.import_theme.toElement('#layout_title_bar')
BarItems.export_theme.toElement('#layout_title_bar')
})