Merge pull request #200 from samuelthomas2774/scheme-icons

Settings scheme icons
This commit is contained in:
Alexei Stukov 2018-08-07 06:15:21 +03:00 committed by GitHub
commit ce23a39f83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 169 additions and 45 deletions

View File

@ -225,6 +225,10 @@ export default class {
setting.setContentPath(contentPath);
}
for (let scheme of userConfig.config.schemes) {
scheme.setContentPath(contentPath);
}
Utils.deepfreeze(defaultConfig, object => object instanceof Combokeys);
const configs = {

View File

@ -32,6 +32,21 @@ export default class SettingsScheme {
return this.args.icon_url;
}
/**
* The path of the scheme's icon relative to the content path.
*/
get icon_path() {
return this.args.icon_path;
}
/**
* The MIME type of the scheme's icon.
* This is only needed when using `icon_path` and the MIME type cannot be determined from the file contents. (Usually when using an SVG.)
*/
get icon_type() {
return this.args.icon_type;
}
/**
* The scheme's name.
*/
@ -49,8 +64,20 @@ export default class SettingsScheme {
/**
* An array of stripped settings categories this scheme manages.
*/
get categories() {
return this.args.categories || this.args.settings || [];
}
get settings() {
return this.args.settings || [];
return this.categories;
}
/**
* The path of the plugin/theme this scheme is part of.
* Use scheme.setContentPath to change.
*/
get path() {
return this.args.path;
}
/**
@ -59,21 +86,21 @@ export default class SettingsScheme {
* @return {Boolean}
*/
isActive(set) {
for (let schemeCategory of this.settings) {
const category = set.categories.find(c => c.category === schemeCategory.category);
for (let schemeCategory of this.categories) {
const category = set.categories.find(c => c.id === (schemeCategory.id || schemeCategory.category));
if (!category) {
Logger.warn('SettingsScheme', `Category ${schemeCategory.category} does not exist`);
Logger.warn('SettingsScheme', `Category ${schemeCategory.id || schemeCategory.category} does not exist`);
return false;
}
for (let schemeSetting of schemeCategory.settings) {
const setting = category.settings.find(s => s.id === schemeSetting.id);
if (!setting) {
Logger.warn('SettingsScheme', `Setting ${schemeCategory.category}/${schemeSetting.id} does not exist`);
Logger.warn('SettingsScheme', `Setting ${category.category}/${schemeSetting.id} does not exist`);
return false;
}
if (!Utils.compare(setting.value, schemeSetting.value)) return false;
if (!Utils.compare(setting.args.value, schemeSetting.value)) return false;
}
}
@ -89,4 +116,12 @@ export default class SettingsScheme {
return set.merge(this);
}
/**
* Sets the path of the plugin/theme this setting is part of.
* @param {String} contentPath The plugin/theme's directory path
*/
setContentPath(contentPath) {
this.args.path = contentPath;
}
}

View File

@ -372,9 +372,9 @@ export default class SettingsSet extends AsyncEventEmitter {
if (!categories) return [];
for (let newCategory of categories) {
const category = this.find(category => category.category === newCategory.category);
const category = this.find(category => category.id === (newCategory.id || newCategory.category));
if (!category) {
Logger.warn('SettingsCategory', `Trying to merge category ${newCategory.id}, which does not exist.`);
Logger.warn('SettingsSet', `Trying to merge category ${newCategory.id}, which does not exist.`);
continue;
}
@ -398,14 +398,14 @@ export default class SettingsSet extends AsyncEventEmitter {
*/
async merge(newSet, emit_multi = true) {
let updatedSettings = [];
// const categories = newSet instanceof Array ? newSet : newSet.settings;
const categories = newSet && newSet.args ? newSet.args.settings : newSet ? newSet.settings : newSet;
const categories = newSet && newSet.args ? newSet.args.categories || newSet.args.settings :
newSet ? newSet.categories || newSet.settings : newSet;
if (!categories) return [];
for (let newCategory of categories) {
const category = this.find(category => category.category === newCategory.category);
const category = this.find(category => category.id === (newCategory.id || newCategory.category));
if (!category) {
Logger.warn('SettingsCategory', `Trying to merge category ${newCategory.id}, which does not exist.`);
Logger.warn('SettingsSet', `Trying to merge category ${newCategory.id}, which does not exist.`);
continue;
}

View File

@ -21,7 +21,7 @@ export default class ArraySetting extends Setting {
constructor(args, ...merge) {
super(args, ...merge);
this.args.settings = this.settings.map(category => new SettingsCategory(category));
this.args.categories = this.categories.map(category => new SettingsCategory(category));
this.args.schemes = this.schemes.map(scheme => new SettingsScheme(scheme));
this.args.items = this.value ? this.value.map(item => this.createItem(item.args || item)) : [];
@ -143,7 +143,7 @@ export default class ArraySetting extends Setting {
const set = new SettingsSet({
id: item ? item.args ? item.args.id : item.id : Math.random(),
settings: Utils.deepclone(this.settings),
categories: this.categories.map(c => c.clone()),
schemes: this.schemes
}, item ? item.args || item : undefined);

View File

@ -46,7 +46,8 @@
props: ['item'],
data() {
return {
iconURL: undefined
iconURL: undefined,
updatingIcon: false
};
},
components: {
@ -68,10 +69,27 @@
} catch (err) {
Logger.err('ContentCard', ['Invalid icon URL', this.item]);
}
},
async refreshIcon() {
if (this.updatingIcon) return;
this.updatingIcon = true;
this.iconURL = await this.getIconURL();
this.updatingIcon = false;
}
},
async created() {
this.iconURL = await this.getIconURL();
watch: {
'item.contentPath'() {
this.refreshIcon();
},
'item.icon'() {
this.refreshIcon();
},
'item.info.icon_type'() {
this.refreshIcon();
}
},
created() {
this.refreshIcon();
}
}
</script>

View File

@ -12,13 +12,7 @@
<div class="bd-settings-panel">
<div class="bd-settings-schemes" v-if="schemes && schemes.length">
<div class="bd-settings-schemes-container">
<template v-for="scheme in schemes">
<div class="bd-settings-scheme" :class="{'bd-active': scheme.isActive(settings)}" @click="() => scheme.applyTo(settings)">
<div class="bd-settings-scheme-icon" :style="{'background-image': `url(&quot;${scheme.icon_url}&quot;)`}"></div>
<div class="bd-settings-scheme-name" v-if="scheme.name">{{ scheme.name }}</div>
<div class="bd-settings-scheme-hint" v-if="scheme.hint">{{ scheme.hint }}</div>
</div>
</template>
<SettingsScheme v-for="scheme in schemes" :key="scheme.id" :scheme="scheme" :is-active="scheme.isActive(settings)" @apply="scheme.applyTo(settings)" />
</div>
</div>
@ -49,12 +43,14 @@
<script>
// Imports
import { Utils } from 'common';
import SettingsScheme from './SettingsScheme.vue';
import Setting from './setting/Setting.vue';
import Drawer from '../common/Drawer.vue';
export default {
props: ['settings', 'schemes'],
components: {
SettingsScheme,
Setting,
Drawer
}

View File

@ -0,0 +1,65 @@
/**
* BetterDiscord Settings Scheme Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<template>
<div class="bd-settings-scheme" :class="{'bd-active': isActive}" @click="$emit('apply')">
<div class="bd-settings-scheme-icon" :style="{'background-image': iconURL}"></div>
<div class="bd-settings-scheme-name" v-if="scheme.name">{{ scheme.name }}</div>
<div class="bd-settings-scheme-hint" v-if="scheme.hint">{{ scheme.hint }}</div>
</div>
</template>
<script>
import { FileUtils, ClientLogger as Logger } from 'common';
import path from 'path';
export default {
props: ['scheme', 'is-active'],
data() {
return {
iconURL: undefined,
updatingIcon: false
};
},
methods: {
async getIconURLFromPath() {
if (!this.scheme.icon_path) return;
try {
const iconPath = path.join(this.scheme.path, this.scheme.icon_path);
const iconURL = await FileUtils.toDataURI(iconPath, this.scheme.icon_type);
return `url(${iconURL})`;
} catch (err) {
Logger.err('SettingsScheme', ['Invalid icon URL', this.scheme, err]);
}
},
async refreshIcon() {
if (this.updatingIcon) return;
this.updatingIcon = true;
this.iconURL = this.scheme.icon_url || await this.getIconURLFromPath();
this.updatingIcon = false;
}
},
watch: {
'scheme.path'() {
this.refreshIcon();
},
'scheme.icon_path'() {
this.refreshIcon();
},
'scheme.icon_type'() {
this.refreshIcon();
}
},
created() {
this.refreshIcon();
}
}
</script>

View File

@ -385,7 +385,7 @@ export class FileUtils {
*/
static async toDataURI(buffer, type) {
if (typeof buffer === 'string') buffer = await this.readFileBuffer(buffer);
if (!type) type = this.getFileType(buffer).mime;
if (!type) type = (await this.getFileType(buffer)).mime;
return `data:${type};base64,${buffer.toString('base64')}`;
}
}

View File

@ -32,9 +32,10 @@ exports.main = (Plugin, { Logger, Settings, Modals, BdMenu: { BdMenuItems }, Com
const scheme = await set.addScheme({
id: 'scheme-1',
name: 'Test scheme',
icon_url: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg',
icon_path: 'scheme-icon.jpg',
settings: [{ category: 'default', settings: [{ id: 'test', value: 'Some\npresent\n\nmultiline\n\ntext' }] }]
});
scheme.setContentPath(__dirname);
set.on('settings-updated', async updatedSettings => {
Logger.log('Updated settings', updatedSettings);

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -4,7 +4,8 @@
"authors": [ "Jiiks" ],
"version": 1.0,
"description": "Example Theme 1 Description",
"icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMDAwIDIwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9IiMzRTgyRTUiIGQ9Ik0xNDAyLjIsNjMxLjdjLTkuNy0zNTMuNC0yODYuMi00OTYtNjQyLjYtNDk2SDY4LjR2NzE0LjFsNDQyLDM5OFY0OTAuN2gyNTdjMjc0LjUsMCwyNzQuNSwzNDQuOSwwLDM0NC45SDU5Ny42djMyOS41aDE2OS44YzI3NC41LDAsMjc0LjUsMzQ0LjgsMCwzNDQuOGgtNjk5djM1NC45aDY5MS4yYzM1Ni4zLDAsNjMyLjgtMTQyLjYsNjQyLjYtNDk2YzAtMTYyLjYtNDQuNS0yODQuMS0xMjIuOS0zNjguNkMxMzU3LjcsOTE1LjgsMTQwMi4yLDc5NC4zLDE0MDIuMiw2MzEuN3oiLz48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMTI2Mi41LDEzNS4yTDEyNjIuNSwxMzUuMmwtNzYuOCwwYzI2LjYsMTMuMyw1MS43LDI4LjEsNzUsNDQuM2M3MC43LDQ5LjEsMTI2LjEsMTExLjUsMTY0LjYsMTg1LjNjMzkuOSw3Ni42LDYxLjUsMTY1LjYsNjQuMywyNjQuNmwwLDEuMnYxLjJjMCwxNDEuMSwwLDU5Ni4xLDAsNzM3LjF2MS4ybDAsMS4yYy0yLjcsOTktMjQuMywxODgtNjQuMywyNjQuNmMtMzguNSw3My44LTkzLjgsMTM2LjItMTY0LjYsMTg1LjNjLTIyLjYsMTUuNy00Ni45LDMwLjEtNzIuNiw0My4xaDcyLjVjMzQ2LjIsMS45LDY3MS0xNzEuMiw2NzEtNTY3LjlWNzE2LjdDMTkzMy41LDMxMi4yLDE2MDguNywxMzUuMiwxMjYyLjUsMTM1LjJ6Ii8+PC9nPjwvc3ZnPg==",
"icon": "icon.svg",
"icon_type": "image/svg+xml",
"type": "sass"
},
"main": "index.scss",
@ -142,7 +143,7 @@
"max": 100,
"step": 1,
"unit": "%",
"multi": "0.01",
"multi": 0.01,
"text": "Span border opacity",
"hint": "test"
},
@ -244,10 +245,10 @@
"id": "scheme-1",
"name": "Test scheme",
"hint": "Sets the span border opacity to 1%.",
"icon_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg",
"settings": [
"icon_path": "scheme-icon.jpg",
"categories": [
{
"category": "default",
"id": "default",
"settings": [
{
"id": "spanOpacity",
@ -261,10 +262,10 @@
"id": "scheme-2",
"name": "Another test scheme",
"hint": "Sets the primary colour to red and the span border opacity to 99%.",
"icon_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg",
"settings": [
"icon_path": "scheme-icon.jpg",
"categories": [
{
"category": "default",
"id": "default",
"settings": [
{
"id": "divBg",
@ -286,18 +287,14 @@
"id": "scheme-3",
"name": "Final test scheme",
"hint": "Sets the primary colour to transparent and the span border opacity to 50%.",
"icon_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Cow_female_black_white.jpg/220px-Cow_female_black_white.jpg",
"settings": [
"icon_path": "scheme-icon.jpg",
"categories": [
{
"category": "default",
"id": "default",
"settings": [
{
"id": "divBg",
"type": "text",
"value": "transparent",
"text": "Primary colour",
"hint": "A colour setting type would be nice here",
"scss_raw": true
"value": "transparent"
},
{
"id": "spanOpacity",
@ -311,10 +308,10 @@
"id": "scheme-4",
"name": "Reset to default background",
"hint": "Better than editing user.config.json.",
"icon_url": "https://rawgit.com/samuelthomas2774/BetterDiscordApp/cfdf3e0b4a6f853bc984c77bfbdf664592bd5e42/tests/themes/Example/background.jpg",
"settings": [
"icon_path": "background.jpg",
"categories": [
{
"category": "default",
"id": "default",
"settings": [
{
"id": "relative-file-test",

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 2000 2000" enable-background="new 0 0 2000 2000" xml:space="preserve">
<g>
<path fill="#3E82E5" d="M1402.2,631.7c-9.7-353.4-286.2-496-642.6-496H68.4v714.1l442,398V490.7h257c274.5,0,274.5,344.9,0,344.9H597.6v329.5h169.8c274.5,0,274.5,344.8,0,344.8h-699v354.9h691.2c356.3,0,632.8-142.6,642.6-496c0-162.6-44.5-284.1-122.9-368.6C1357.7,915.8,1402.2,794.3,1402.2,631.7z"/>
<path fill="#FFFFFF" d="M1262.5,135.2L1262.5,135.2l-76.8,0c26.6,13.3,51.7,28.1,75,44.3c70.7,49.1,126.1,111.5,164.6,185.3c39.9,76.6,61.5,165.6,64.3,264.6l0,1.2v1.2c0,141.1,0,596.1,0,737.1v1.2l0,1.2c-2.7,99-24.3,188-64.3,264.6c-38.5,73.8-93.8,136.2-164.6,185.3c-22.6,15.7-46.9,30.1-72.6,43.1h72.5c346.2,1.9,671-171.2,671-567.9V716.7C1933.5,312.2,1608.7,135.2,1262.5,135.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB