Spin off VideoSettings component into subcomponents
This commit is contained in:
parent
13bfd63dc2
commit
b9b021f466
@ -0,0 +1,276 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <div class="flex flex-row">
|
||||||
|
<mdicon name="crop" :size="32" />
|
||||||
|
<h1>Crop video:</h1>
|
||||||
|
</div> -->
|
||||||
|
<div class="sub-panel-content flex flex-row flex-wrap">
|
||||||
|
<ShortcutButton
|
||||||
|
v-for="(command, index) of settings?.active.commands.crop"
|
||||||
|
class="flex b3 button"
|
||||||
|
:class="{active: editMode ? index === editModeOptions?.crop?.selectedIndex : isActiveCrop(command)}"
|
||||||
|
:key="index"
|
||||||
|
:label="command.label"
|
||||||
|
:shortcut="getKeyboardShortcutLabel(command)"
|
||||||
|
@click="editMode ? editAction(command, index, 'crop') : execAction(command)"
|
||||||
|
>
|
||||||
|
</ShortcutButton>
|
||||||
|
|
||||||
|
<!-- "Add new" button -->
|
||||||
|
<ShortcutButton
|
||||||
|
v-if="editMode"
|
||||||
|
class="button b3"
|
||||||
|
:class="{active: editMode ? editModeOptions?.crop?.selectedIndex === null : isActiveCrop(command)}"
|
||||||
|
label="Add new"
|
||||||
|
@click="editAction(
|
||||||
|
{action: 'set-ar', label: 'New aspect ratio', arguments: {type: AspectRatioType.Fixed}},
|
||||||
|
null,
|
||||||
|
'crop'
|
||||||
|
)"
|
||||||
|
></ShortcutButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EDIT MODE PANEL -->
|
||||||
|
<div
|
||||||
|
v-if="editMode && !editModeOptions?.crop?.selected"
|
||||||
|
class="sub-panel-content"
|
||||||
|
>
|
||||||
|
<div class="edit-action-area">
|
||||||
|
Click a button to edit
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="editMode && editModeOptions?.crop?.selected" class="sub-panel-content">
|
||||||
|
<div class="edit-action-area-header">
|
||||||
|
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.crop?.selected?.label}}</b>
|
||||||
|
<template v-if="editModeOptions?.crop?.selectedIndex === null && editModeOptions?.crop?.selected?.label !== 'New aspect ratio'">(New ratio)</template>
|
||||||
|
</div>
|
||||||
|
<div class="edit-action-area">
|
||||||
|
<!-- Some options are only shown for type 4 (fixed) crops -->
|
||||||
|
<template v-if="editModeOptions?.crop?.selected?.arguments?.type === AspectRatioType.Fixed">
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">
|
||||||
|
Ratio:
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<!-- We do an ugly in order to avoid spamming functions down at the bottom -->
|
||||||
|
<input
|
||||||
|
v-model="editModeOptions.crop.selected.arguments.ratio"
|
||||||
|
@blur="editModeOptions.crop.selected.label === 'New aspect ratio' ? editModeOptions.crop.selected.label = editModeOptions.crop.selected.arguments.ratio : null"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
You can enter a ratio in width:height format (e.g. "21:9" or "1:2.39"), or just the factor
|
||||||
|
(in this case, "1:2.39" would become "2.39" and "21:9" would become "2.33"). You should enter
|
||||||
|
your numbers without quote marks. Number will be converted to factor form on save.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">
|
||||||
|
Label:
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<input v-model="editModeOptions.crop.selected.label">
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
Label for the button. You can make it say something other than ratio.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- editing keyboard shortcuts is always allowed -->
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Shortcut:</div>
|
||||||
|
<div class="">
|
||||||
|
<EditShortcutButton
|
||||||
|
:shortcut="editModeOptions?.crop?.selected?.shortcut"
|
||||||
|
@shortcutChanged="updateSelectedShortcut($event, 'crop')"
|
||||||
|
>
|
||||||
|
</EditShortcutButton>
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
<b>Note:</b> Your browser and OS already use certain key combinations that involve Ctrl and Meta (Windows) keys — and, to a lesser extent, Alt.
|
||||||
|
The extension doesn't (and cannot) check whether the keyboard shortcut you enter is actually free for you to use. The extension also won't override
|
||||||
|
any keyboard shortcuts defined by the site itself.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-end">
|
||||||
|
<div
|
||||||
|
v-if="editModeOptions?.crop?.selected?.arguments?.type === AspectRatioType.Fixed && editModeOptions?.crop?.selectedIndex !== null"
|
||||||
|
class="button"
|
||||||
|
@click="deleteAction('crop')"
|
||||||
|
>
|
||||||
|
<mdicon name="delete"></mdicon> Delete
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow"></div>
|
||||||
|
<div class="button" @click="cancelEdit('crop')">Cancel</div>
|
||||||
|
<div class="button" @click="saveShortcut('crop')">
|
||||||
|
<mdicon name="floppy"></mdicon>
|
||||||
|
|
||||||
|
<template v-if="editModeOptions?.crop?.selectedIndex === null">Add</template>
|
||||||
|
<template v-else>Save</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-action-area">
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Default for this site</div>
|
||||||
|
<div class="select">
|
||||||
|
<select
|
||||||
|
:value="siteDefaultCrop"
|
||||||
|
@click="setDefaultCrop($event, 'site')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(command, index) of settings?.active.commands.crop"
|
||||||
|
:key="index"
|
||||||
|
:value="JSON.stringify(command.arguments)"
|
||||||
|
>
|
||||||
|
{{command.label}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Extension default</div>
|
||||||
|
<div class="select">
|
||||||
|
<select
|
||||||
|
:value="extensionDefaultCrop"
|
||||||
|
@click="setDefaultCrop($event, 'global')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(command, index) of settings?.active.commands.crop"
|
||||||
|
:key="index"
|
||||||
|
:value="JSON.stringify(command.arguments)"
|
||||||
|
>
|
||||||
|
{{command.label}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ShortcutButton from '../../../../../common/components/ShortcutButton.vue';
|
||||||
|
import EditShortcutButton from '../../../../../common/components/EditShortcutButton';
|
||||||
|
import EditModeMixin from '../../../utils/EditModeMixin';
|
||||||
|
import KeyboardShortcutParserMixin from '../../../utils/KeyboardShortcutParserMixin';
|
||||||
|
import CommsMixin from '../../../utils/CommsMixin';
|
||||||
|
import AspectRatioType from '../../../../../common/enums/AspectRatioType.enum';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
AspectRatioType: AspectRatioType,
|
||||||
|
|
||||||
|
// TODO: this should be mixin?
|
||||||
|
resizerConfig: {
|
||||||
|
crop: null,
|
||||||
|
stretch: null,
|
||||||
|
zoom: null,
|
||||||
|
pan: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [
|
||||||
|
// ComputeActionsMixin,
|
||||||
|
EditModeMixin,
|
||||||
|
KeyboardShortcutParserMixin,
|
||||||
|
CommsMixin
|
||||||
|
],
|
||||||
|
props: [
|
||||||
|
'settings',
|
||||||
|
'frame',
|
||||||
|
'eventBus',
|
||||||
|
'site',
|
||||||
|
'exec',
|
||||||
|
'isEditing'
|
||||||
|
],
|
||||||
|
components: {
|
||||||
|
ShortcutButton,
|
||||||
|
EditShortcutButton,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
extensionDefaultCrop() {
|
||||||
|
return JSON.stringify(
|
||||||
|
this.settings?.active.crop?.default ?? {type: AspectRatioType.Automatic}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
siteDefaultCrop() {
|
||||||
|
return JSON.stringify(
|
||||||
|
this.settings?.getDefaultCrop(this.site) ?? {type: AspectRatioType.Automatic}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isEditing(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.enableEditMode();
|
||||||
|
} else {
|
||||||
|
this.disableEditMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Sets default crop, for either site or global
|
||||||
|
*/
|
||||||
|
setDefaultCrop($event, scope) {
|
||||||
|
const commandArguments = JSON.parse($event.target.value);
|
||||||
|
|
||||||
|
if (scope === 'site') {
|
||||||
|
if (!this.settings.active.sites[this.site]) {
|
||||||
|
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
|
||||||
|
}
|
||||||
|
this.settings.active.sites[this.site].defaultCrop = commandArguments;
|
||||||
|
} else {
|
||||||
|
// eventually, this 'if' will be safe to remove (and we'll be able to only
|
||||||
|
// get away with the 'else' section) Maybe in 6 months or so.
|
||||||
|
if (!this.settings.active.crop) {
|
||||||
|
this.settings.active['crop'] = {
|
||||||
|
default: commandArguments
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.settings.active.crop.default = commandArguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settings.saveWithoutReload();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given crop command is the currently active one
|
||||||
|
*/
|
||||||
|
isActiveCrop(cropCommand) {
|
||||||
|
if (! this.resizerConfig.crop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultCrop = this.settings.getDefaultCrop(this.site);
|
||||||
|
|
||||||
|
if (cropCommand.arguments.type === AspectRatioType.Automatic) {
|
||||||
|
return this.resizerConfig.crop.type === AspectRatioType.Automatic
|
||||||
|
|| this.resizerConfig.crop.type === AspectRatioType.AutomaticUpdate
|
||||||
|
|| this.resizerConfig.crop.type === AspectRatioType.Initial && defaultCrop === AspectRatioType.Automatic;
|
||||||
|
}
|
||||||
|
if (cropCommand.arguments.type === AspectRatioType.Reset) {
|
||||||
|
return this.resizerConfig.crop.type === AspectRatioType.Reset
|
||||||
|
|| this.resizerConfig.crop.type === AspectRatioType.Initial && defaultCrop !== AspectRatioType.Automatic;
|
||||||
|
}
|
||||||
|
if (cropCommand.arguments.type === AspectRatioType.Fixed) {
|
||||||
|
return this.resizerConfig.crop.type === AspectRatioType.Fixed
|
||||||
|
&& this.resizerConfig.crop.ratio === cropCommand.arguments.ratio;
|
||||||
|
}
|
||||||
|
// only legacy options (fitw, fith) left to handle:
|
||||||
|
return cropCommand.arguments.type === this.resizerConfig.crop.type;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" src="../../../../../res/css/flex.scss" scoped></style>
|
||||||
|
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
||||||
|
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
@ -0,0 +1,272 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-row flex-wrap">
|
||||||
|
<ShortcutButton
|
||||||
|
v-for="(command, index) of settings?.active.commands.stretch"
|
||||||
|
class="b3 button"
|
||||||
|
:class="{active: editMode ? index === editModeOptions?.stretch?.selectedIndex : isActiveStretch(command)}"
|
||||||
|
:key="index"
|
||||||
|
:label="command.label"
|
||||||
|
:shortcut="getKeyboardShortcutLabel(command)"
|
||||||
|
@click="editMode ? editAction(command, index, 'stretch') : execAction(command)"
|
||||||
|
>
|
||||||
|
</ShortcutButton>
|
||||||
|
|
||||||
|
<!-- "Add new" button -->
|
||||||
|
<ShortcutButton
|
||||||
|
v-if="editMode"
|
||||||
|
class="button b3"
|
||||||
|
label="Add new"
|
||||||
|
@click="editAction(
|
||||||
|
{action: 'set-stretch', label: 'Stretch to ...', arguments: {type: StretchType.FixedSource}},
|
||||||
|
null,
|
||||||
|
'stretch'
|
||||||
|
)"
|
||||||
|
></ShortcutButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EDIT MODE PANEL -->
|
||||||
|
<div
|
||||||
|
v-if="editMode && !editModeOptions?.stretch?.selected"
|
||||||
|
class="sub-panel-content"
|
||||||
|
>
|
||||||
|
<div class="edit-action-area">
|
||||||
|
Click a button to edit
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="editMode && editModeOptions?.stretch?.selected" class="sub-panel-content">
|
||||||
|
<div class="edit-action-area-header">
|
||||||
|
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.stretch?.selected?.label}}</b>
|
||||||
|
<template v-if="editModeOptions?.stretch?.selectedIndex === null && editModeOptions?.stretch?.selected?.label !== 'Stretch to ...'">(New option)</template>
|
||||||
|
</div>
|
||||||
|
<div class="edit-action-area">
|
||||||
|
<!-- There are some special options for 'thin borders' -->
|
||||||
|
<template v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.Conditional">
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">
|
||||||
|
Limit:
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<input
|
||||||
|
v-model="editModeOptions.stretch.selected.arguments.limit"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
If vertical borders would take up less than this much of screen width, the image will be stretched. If the borders are too thick, image will not be stretched.
|
||||||
|
Value of 1 means 100%. Value of 0.1 means vertical black bars can take up 10% of the width at most. There's no validation on this, use common sense.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Some options are only shown for type 5 (fixed) stretch -->
|
||||||
|
<template v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.FixedSource">
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">
|
||||||
|
Ratio:
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<!-- We do an ugly in order to avoid spamming functions down at the bottom -->
|
||||||
|
<input
|
||||||
|
v-model="editModeOptions.stretch.selected.arguments.ratio"
|
||||||
|
@blur="editModeOptions.stretch.selected.label === 'Stretch to ...' ? editModeOptions.stretch.selected.label = `Stretch to ${editModeOptions.stretch.selected.arguments.ratio}` : null"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
You can enter a ratio in width:height format (e.g. "21:9" or "1:2.39"), or just the factor
|
||||||
|
(in this case, "1:2.39" would become "2.39" and "21:9" would become "2.33"). You should enter
|
||||||
|
your numbers without quote marks. Number will be converted to factor form on save.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">
|
||||||
|
Label:
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<input v-model="editModeOptions.stretch.selected.label">
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
Label for the button. You can make it say something other than ratio.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- editing keyboard shortcuts is always allowed -->
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Shortcut:</div>
|
||||||
|
<div class="">
|
||||||
|
<EditShortcutButton
|
||||||
|
:shortcut="editModeOptions?.stretch?.selected?.shortcut"
|
||||||
|
@shortcutChanged="updateSelectedShortcut($event, 'stretch')"
|
||||||
|
>
|
||||||
|
</EditShortcutButton>
|
||||||
|
</div>
|
||||||
|
<div class="hint">
|
||||||
|
<b>Note:</b> Your browser and OS already use certain key combinations that involve Ctrl and Meta (Windows) keys — and, to a lesser extent, Alt.
|
||||||
|
The extension doesn't (and cannot) check whether the keyboard shortcut you enter is actually free for you to use. The extension also won't override
|
||||||
|
any keyboard shortcuts defined by the site itself.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-end">
|
||||||
|
<div
|
||||||
|
v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.FixedSource && editModeOptions?.stretch?.selectedIndex !== null"
|
||||||
|
class="button"
|
||||||
|
@click="deleteAction('stretch')"
|
||||||
|
>
|
||||||
|
<mdicon name="delete"></mdicon> Delete
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow"></div>
|
||||||
|
<div class="button" @click="cancelEdit('stretch')">Cancel</div>
|
||||||
|
<div class="button" @click="saveShortcut('stretch')">
|
||||||
|
<mdicon name="floppy"></mdicon>
|
||||||
|
|
||||||
|
<template v-if="editModeOptions?.crop?.selectedIndex === null">Add</template>
|
||||||
|
<template v-else>Save</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-action-area">
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Default for this site:</div>
|
||||||
|
<div class="select">
|
||||||
|
<div class="select">
|
||||||
|
<select
|
||||||
|
v-model="siteDefaultStretchMode"
|
||||||
|
@click="setDefaultStretchingMode($event, 'site')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(command, index) of settings?.active.commands.stretch"
|
||||||
|
:key="index"
|
||||||
|
:value="JSON.stringify(command.arguments)"
|
||||||
|
>
|
||||||
|
{{command.label}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Extension default:</div>
|
||||||
|
<div class="select">
|
||||||
|
<select
|
||||||
|
v-model="extensionDefaultStretchMode"
|
||||||
|
@click="setDefaultStretchingMode($event, 'global')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(command, index) of settings?.active.commands.stretch"
|
||||||
|
:key="index"
|
||||||
|
:value="JSON.stringify(command.arguments)"
|
||||||
|
>
|
||||||
|
{{command.label}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ShortcutButton from '../../../../../common/components/ShortcutButton.vue';
|
||||||
|
import EditShortcutButton from '../../../../../common/components/EditShortcutButton';
|
||||||
|
import EditModeMixin from '../../../utils/EditModeMixin';
|
||||||
|
import KeyboardShortcutParserMixin from '../../../utils/KeyboardShortcutParserMixin';
|
||||||
|
import CommsMixin from '../../../utils/CommsMixin';
|
||||||
|
import StretchType from '../../../../../common/enums/StretchType.enum';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
exec: null,
|
||||||
|
StretchType: StretchType,
|
||||||
|
|
||||||
|
// TODO: this should be mixin?
|
||||||
|
resizerConfig: {
|
||||||
|
crop: null,
|
||||||
|
stretch: null,
|
||||||
|
zoom: null,
|
||||||
|
pan: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [
|
||||||
|
// ComputeActionsMixin,
|
||||||
|
EditModeMixin,
|
||||||
|
KeyboardShortcutParserMixin,
|
||||||
|
CommsMixin
|
||||||
|
],
|
||||||
|
props: [
|
||||||
|
'settings',
|
||||||
|
'frame',
|
||||||
|
'eventBus',
|
||||||
|
'site',
|
||||||
|
'exec',
|
||||||
|
'isEditing'
|
||||||
|
],
|
||||||
|
components: {
|
||||||
|
ShortcutButton,
|
||||||
|
EditShortcutButton,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
extensionDefaultCrop() {
|
||||||
|
return JSON.stringify(
|
||||||
|
this.settings?.active.crop?.default ?? {type: AspectRatioType.Automatic}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
siteDefaultCrop() {
|
||||||
|
return JSON.stringify(
|
||||||
|
this.settings?.getDefaultCrop(this.site) ?? {type: AspectRatioType.Automatic}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isEditing(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.enableEditMode();
|
||||||
|
} else {
|
||||||
|
this.disableEditMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Sets default stretching mode, for either site or global
|
||||||
|
*/
|
||||||
|
setDefaultStretchingMode($event, globalOrSite) {
|
||||||
|
const commandArguments = JSON.parse($event.target.value);
|
||||||
|
|
||||||
|
if (globalOrSite === 'site') {
|
||||||
|
if (!this.settings.active.sites[this.site]) {
|
||||||
|
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
|
||||||
|
}
|
||||||
|
this.settings.active.sites[this.site].defaultStretch = commandArguments;
|
||||||
|
} else {
|
||||||
|
this.settings.active.stretch.default = commandArguments;
|
||||||
|
}
|
||||||
|
this.settings.saveWithoutReload();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given stretch command is the currently active one
|
||||||
|
*/
|
||||||
|
isActiveStretch(stretchCommand) {
|
||||||
|
if (! this.resizerConfig.stretch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const defaultCrop = this.settings.getDefaultStretch(this.site);
|
||||||
|
|
||||||
|
if ([StretchType.NoStretch, StretchType.Basic, StretchType.Hybrid, StretchType.Conditional, StretchType.Default].includes(stretchCommand.arguments.type)) {
|
||||||
|
return this.resizerConfig.stretch.type === stretchCommand.arguments.type;
|
||||||
|
}
|
||||||
|
return this.resizerConfig.crop.type === stretchCommand.arguments.type && this.resizerConfig.crop.ratio === stretchCommand.arguments.ratio;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" src="../../../../../res/css/flex.scss" scoped></style>
|
||||||
|
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
||||||
|
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-column">
|
||||||
|
<!--
|
||||||
|
min, max and value need to be implemented in js as this slider
|
||||||
|
should use logarithmic scale
|
||||||
|
-->
|
||||||
|
<div class="flex flex-row flex-end">
|
||||||
|
<Button
|
||||||
|
v-if="zoomAspectRatioLocked"
|
||||||
|
label="Unlock aspect ratio"
|
||||||
|
icon="lock-open"
|
||||||
|
:fixedWidth="true"
|
||||||
|
@click="toggleZoomAr()"
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
v-else
|
||||||
|
label="Lock aspect ratio"
|
||||||
|
icon="lock"
|
||||||
|
:fixedWidth="true"
|
||||||
|
@click="toggleZoomAr()"
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<template v-if="zoomAspectRatioLocked">
|
||||||
|
<input id="_input_zoom_slider"
|
||||||
|
class="input-slider"
|
||||||
|
type="range"
|
||||||
|
step="any"
|
||||||
|
min="-1"
|
||||||
|
max="3"
|
||||||
|
:value="zoom.x"
|
||||||
|
@input="changeZoom($event.target.value)"
|
||||||
|
/>
|
||||||
|
<div style="overflow: auto" class="flex flex-row">
|
||||||
|
<div class="flex flex-grow medium-small x-pad-1em">
|
||||||
|
Zoom: {{getZoomForDisplay('x')}}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-nogrow flex-noshrink medium-small">
|
||||||
|
<a class="_zoom_reset x-pad-1em" @click="resetZoom()">reset</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div>Horizontal zoom</div>
|
||||||
|
<input id="_input_zoom_slider"
|
||||||
|
class="input-slider"
|
||||||
|
type="range"
|
||||||
|
step="any"
|
||||||
|
min="-1"
|
||||||
|
max="4"
|
||||||
|
:value="zoom.x"
|
||||||
|
@input="changeZoom($event.target.value, 'x')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div>Vertical zoom</div>
|
||||||
|
<input id="_input_zoom_slider"
|
||||||
|
class="input-slider"
|
||||||
|
type="range"
|
||||||
|
step="any"
|
||||||
|
min="-1"
|
||||||
|
max="3"
|
||||||
|
:value="zoom.y"
|
||||||
|
@input="changeZoom($event.target.value, 'y')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div style="overflow: auto" class="flex flex-row">
|
||||||
|
<div class="flex flex-grow medium-small x-pad-1em">
|
||||||
|
Zoom: {{getZoomForDisplay('x')}} x {{getZoomForDisplay('y')}}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-nogrow flex-noshrink medium-small">
|
||||||
|
<a class="_zoom_reset x-pad-1em" @click="resetZoom()">reset</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
zoomAspectRatioLocked: true,
|
||||||
|
zoom: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: this should be mixin?
|
||||||
|
resizerConfig: {
|
||||||
|
crop: null,
|
||||||
|
stretch: null,
|
||||||
|
zoom: null,
|
||||||
|
pan: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [
|
||||||
|
|
||||||
|
],
|
||||||
|
props: [
|
||||||
|
'settings',
|
||||||
|
'frame',
|
||||||
|
'eventBus',
|
||||||
|
'site',
|
||||||
|
'exec',
|
||||||
|
'isEditing'
|
||||||
|
],
|
||||||
|
methods: {
|
||||||
|
toggleZoomAr() {
|
||||||
|
this.zoomAspectRatioLocked = !this.zoomAspectRatioLocked;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetZoom() {
|
||||||
|
// we store zoom logarithmically on this component
|
||||||
|
this.zoom = {x: 0, y: 0};
|
||||||
|
|
||||||
|
// we do not use logarithmic zoom elsewhere
|
||||||
|
// todo: replace eventBus with postMessage to parent
|
||||||
|
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'});
|
||||||
|
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'});
|
||||||
|
|
||||||
|
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'y'});
|
||||||
|
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'x'});
|
||||||
|
},
|
||||||
|
changeZoom(newZoom, axis) {
|
||||||
|
// we store zoom logarithmically on this compnent
|
||||||
|
if (!axis) {
|
||||||
|
this.zoom.x = newZoom;
|
||||||
|
} else {
|
||||||
|
this.zoom[axis] = newZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not use logarithmic zoom elsewhere, therefore we need to convert
|
||||||
|
newZoom = Math.pow(2, newZoom);
|
||||||
|
|
||||||
|
if (this.zoomAspectRatioLocked) {
|
||||||
|
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'y'});
|
||||||
|
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'x'});
|
||||||
|
} else {
|
||||||
|
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: axis ?? 'x'});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" src="../../../../../res/css/flex.scss" scoped></style>
|
||||||
|
<style lang="scss" src="../../../res-common/panels.scss" scoped></style>
|
||||||
|
<style lang="scss" src="../../../res-common/common.scss" scoped></style>
|
@ -11,7 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex-nogrow flex-noshrink"
|
class="flex-nogrow flex-noshrink"
|
||||||
@click="disableEditMode()"
|
@click="editMode = !editMode"
|
||||||
>
|
>
|
||||||
Exit edit mode
|
Exit edit mode
|
||||||
</div>
|
</div>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<div class="flex-grow"></div>
|
<div class="flex-grow"></div>
|
||||||
<div
|
<div
|
||||||
class=""
|
class=""
|
||||||
@click="enableEditMode()"
|
@click="editMode = !editMode"
|
||||||
>
|
>
|
||||||
Edit ratios and shortcuts
|
Edit ratios and shortcuts
|
||||||
</div>
|
</div>
|
||||||
@ -36,153 +36,16 @@
|
|||||||
<mdicon name="crop" :size="32" />
|
<mdicon name="crop" :size="32" />
|
||||||
<h1>Crop video:</h1>
|
<h1>Crop video:</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="sub-panel-content flex flex-row flex-wrap">
|
|
||||||
<ShortcutButton
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
class="flex b3 button"
|
|
||||||
:class="{active: editMode ? index === editModeOptions?.crop?.selectedIndex : isActiveCrop(command)}"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="editMode ? editAction(command, index, 'crop') : execAction(command)"
|
|
||||||
>
|
|
||||||
</ShortcutButton>
|
|
||||||
|
|
||||||
<!-- "Add new" button -->
|
<CropOptionsPanel
|
||||||
<ShortcutButton
|
:settings="settings"
|
||||||
v-if="editMode"
|
:frame="frame"
|
||||||
class="button b3"
|
:exec="exec"
|
||||||
:class="{active: editMode ? editModeOptions?.crop?.selectedIndex === null : isActiveCrop(command)}"
|
:eventBus="eventBus"
|
||||||
label="Add new"
|
:site="site"
|
||||||
@click="editAction(
|
:isEditing="editMode"
|
||||||
{action: 'set-ar', label: 'New aspect ratio', arguments: {type: AspectRatioType.Fixed}},
|
|
||||||
null,
|
|
||||||
'crop'
|
|
||||||
)"
|
|
||||||
></ShortcutButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- EDIT MODE PANEL -->
|
|
||||||
<div
|
|
||||||
v-if="editMode && !editModeOptions?.crop?.selected"
|
|
||||||
class="sub-panel-content"
|
|
||||||
>
|
>
|
||||||
<div class="edit-action-area">
|
</CropOptionsPanel>
|
||||||
Click a button to edit
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editMode && editModeOptions?.crop?.selected" class="sub-panel-content">
|
|
||||||
<div class="edit-action-area-header">
|
|
||||||
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.crop?.selected?.label}}</b>
|
|
||||||
<template v-if="editModeOptions?.crop?.selectedIndex === null && editModeOptions?.crop?.selected?.label !== 'New aspect ratio'">(New ratio)</template>
|
|
||||||
</div>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<!-- Some options are only shown for type 4 (fixed) crops -->
|
|
||||||
<template v-if="editModeOptions?.crop?.selected?.arguments?.type === AspectRatioType.Fixed">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Ratio:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<!-- We do an ugly in order to avoid spamming functions down at the bottom -->
|
|
||||||
<input
|
|
||||||
v-model="editModeOptions.crop.selected.arguments.ratio"
|
|
||||||
@blur="editModeOptions.crop.selected.label === 'New aspect ratio' ? editModeOptions.crop.selected.label = editModeOptions.crop.selected.arguments.ratio : null"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
You can enter a ratio in width:height format (e.g. "21:9" or "1:2.39"), or just the factor
|
|
||||||
(in this case, "1:2.39" would become "2.39" and "21:9" would become "2.33"). You should enter
|
|
||||||
your numbers without quote marks. Number will be converted to factor form on save.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.crop.selected.label">
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- editing keyboard shortcuts is always allowed -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Shortcut:</div>
|
|
||||||
<div class="">
|
|
||||||
<EditShortcutButton
|
|
||||||
:shortcut="editModeOptions?.crop?.selected?.shortcut"
|
|
||||||
@shortcutChanged="updateSelectedShortcut($event, 'crop')"
|
|
||||||
>
|
|
||||||
</EditShortcutButton>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
<b>Note:</b> Your browser and OS already use certain key combinations that involve Ctrl and Meta (Windows) keys — and, to a lesser extent, Alt.
|
|
||||||
The extension doesn't (and cannot) check whether the keyboard shortcut you enter is actually free for you to use. The extension also won't override
|
|
||||||
any keyboard shortcuts defined by the site itself.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<div
|
|
||||||
v-if="editModeOptions?.crop?.selected?.arguments?.type === AspectRatioType.Fixed && editModeOptions?.crop?.selectedIndex !== null"
|
|
||||||
class="button"
|
|
||||||
@click="deleteAction('crop')"
|
|
||||||
>
|
|
||||||
<mdicon name="delete"></mdicon> Delete
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div class="button" @click="cancelEdit('crop')">Cancel</div>
|
|
||||||
<div class="button" @click="saveShortcut('crop')">
|
|
||||||
<mdicon name="floppy"></mdicon>
|
|
||||||
|
|
||||||
<template v-if="editModeOptions?.crop?.selectedIndex === null">Add</template>
|
|
||||||
<template v-else>Save</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default for this site</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="siteDefaultCrop"
|
|
||||||
@click="setDefaultCrop($event, 'site')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Extension default</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
:value="extensionDefaultCrop"
|
|
||||||
@click="setDefaultCrop($event, 'global')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.crop"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- STRETCH OPTIONS -->
|
<!-- STRETCH OPTIONS -->
|
||||||
@ -191,173 +54,15 @@
|
|||||||
<mdicon name="stretch-to-page-outline" :size="32" />
|
<mdicon name="stretch-to-page-outline" :size="32" />
|
||||||
<h1>Stretch video:</h1>
|
<h1>Stretch video:</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-wrap">
|
|
||||||
<ShortcutButton
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
class="b3 button"
|
|
||||||
:class="{active: editMode ? index === editModeOptions?.stretch?.selectedIndex : isActiveStretch(command)}"
|
|
||||||
:key="index"
|
|
||||||
:label="command.label"
|
|
||||||
:shortcut="getKeyboardShortcutLabel(command)"
|
|
||||||
@click="editMode ? editAction(command, index, 'stretch') : execAction(command)"
|
|
||||||
>
|
|
||||||
</ShortcutButton>
|
|
||||||
|
|
||||||
<!-- "Add new" button -->
|
<StretchOptionsPanel
|
||||||
<ShortcutButton
|
:settings="settings"
|
||||||
v-if="editMode"
|
:frame="frame"
|
||||||
class="button b3"
|
:exec="exec"
|
||||||
label="Add new"
|
:eventBus="eventBus"
|
||||||
@click="editAction(
|
:site="site"
|
||||||
{action: 'set-stretch', label: 'Stretch to ...', arguments: {type: StretchType.FixedSource}},
|
:isEditing="editMode"
|
||||||
null,
|
></StretchOptionsPanel>
|
||||||
'stretch'
|
|
||||||
)"
|
|
||||||
></ShortcutButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- EDIT MODE PANEL -->
|
|
||||||
<div
|
|
||||||
v-if="editMode && !editModeOptions?.stretch?.selected"
|
|
||||||
class="sub-panel-content"
|
|
||||||
>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
Click a button to edit
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="editMode && editModeOptions?.stretch?.selected" class="sub-panel-content">
|
|
||||||
<div class="edit-action-area-header">
|
|
||||||
<span class="text-primary">Editing options for:</span> <b>{{editModeOptions?.stretch?.selected?.label}}</b>
|
|
||||||
<template v-if="editModeOptions?.stretch?.selectedIndex === null && editModeOptions?.stretch?.selected?.label !== 'Stretch to ...'">(New option)</template>
|
|
||||||
</div>
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<!-- There are some special options for 'thin borders' -->
|
|
||||||
<template v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.Conditional">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Limit:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input
|
|
||||||
v-model="editModeOptions.stretch.selected.arguments.limit"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
If vertical borders would take up less than this much of screen width, the image will be stretched. If the borders are too thick, image will not be stretched.
|
|
||||||
Value of 1 means 100%. Value of 0.1 means vertical black bars can take up 10% of the width at most. There's no validation on this, use common sense.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Some options are only shown for type 5 (fixed) stretch -->
|
|
||||||
<template v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.FixedSource">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Ratio:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<!-- We do an ugly in order to avoid spamming functions down at the bottom -->
|
|
||||||
<input
|
|
||||||
v-model="editModeOptions.stretch.selected.arguments.ratio"
|
|
||||||
@blur="editModeOptions.stretch.selected.label === 'Stretch to ...' ? editModeOptions.stretch.selected.label = `Stretch to ${editModeOptions.stretch.selected.arguments.ratio}` : null"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
You can enter a ratio in width:height format (e.g. "21:9" or "1:2.39"), or just the factor
|
|
||||||
(in this case, "1:2.39" would become "2.39" and "21:9" would become "2.33"). You should enter
|
|
||||||
your numbers without quote marks. Number will be converted to factor form on save.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">
|
|
||||||
Label:
|
|
||||||
</div>
|
|
||||||
<div class="input">
|
|
||||||
<input v-model="editModeOptions.stretch.selected.label">
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
Label for the button. You can make it say something other than ratio.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- editing keyboard shortcuts is always allowed -->
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Shortcut:</div>
|
|
||||||
<div class="">
|
|
||||||
<EditShortcutButton
|
|
||||||
:shortcut="editModeOptions?.stretch?.selected?.shortcut"
|
|
||||||
@shortcutChanged="updateSelectedShortcut($event, 'stretch')"
|
|
||||||
>
|
|
||||||
</EditShortcutButton>
|
|
||||||
</div>
|
|
||||||
<div class="hint">
|
|
||||||
<b>Note:</b> Your browser and OS already use certain key combinations that involve Ctrl and Meta (Windows) keys — and, to a lesser extent, Alt.
|
|
||||||
The extension doesn't (and cannot) check whether the keyboard shortcut you enter is actually free for you to use. The extension also won't override
|
|
||||||
any keyboard shortcuts defined by the site itself.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<div
|
|
||||||
v-if="editModeOptions?.stretch?.selected?.arguments?.type === StretchType.FixedSource && editModeOptions?.stretch?.selectedIndex !== null"
|
|
||||||
class="button"
|
|
||||||
@click="deleteAction('stretch')"
|
|
||||||
>
|
|
||||||
<mdicon name="delete"></mdicon> Delete
|
|
||||||
</div>
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div class="button" @click="cancelEdit('stretch')">Cancel</div>
|
|
||||||
<div class="button" @click="saveShortcut('stretch')">
|
|
||||||
<mdicon name="floppy"></mdicon>
|
|
||||||
|
|
||||||
<template v-if="editModeOptions?.crop?.selectedIndex === null">Add</template>
|
|
||||||
<template v-else>Save</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="edit-action-area">
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Default for this site:</div>
|
|
||||||
<div class="select">
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="siteDefaultStretchMode"
|
|
||||||
@click="setDefaultStretchingMode($event, 'site')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="label">Extension default:</div>
|
|
||||||
<div class="select">
|
|
||||||
<select
|
|
||||||
v-model="extensionDefaultStretchMode"
|
|
||||||
@click="setDefaultStretchingMode($event, 'global')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(command, index) of settings?.active.commands.stretch"
|
|
||||||
:key="index"
|
|
||||||
:value="JSON.stringify(command.arguments)"
|
|
||||||
>
|
|
||||||
{{command.label}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -367,82 +72,17 @@
|
|||||||
<mdicon name="magnify-plus-outline" :size="32" />
|
<mdicon name="magnify-plus-outline" :size="32" />
|
||||||
<h1>Manual zoom:</h1>
|
<h1>Manual zoom:</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-column">
|
|
||||||
<!--
|
|
||||||
min, max and value need to be implemented in js as this slider
|
|
||||||
should use logarithmic scale
|
|
||||||
-->
|
|
||||||
<div class="flex flex-row flex-end">
|
|
||||||
<Button
|
|
||||||
v-if="zoomAspectRatioLocked"
|
|
||||||
label="Unlock aspect ratio"
|
|
||||||
icon="lock-open"
|
|
||||||
:fixedWidth="true"
|
|
||||||
@click="toggleZoomAr()"
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
v-else
|
|
||||||
label="Lock aspect ratio"
|
|
||||||
icon="lock"
|
|
||||||
:fixedWidth="true"
|
|
||||||
@click="toggleZoomAr()"
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<template v-if="zoomAspectRatioLocked">
|
|
||||||
<input id="_input_zoom_slider"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="3"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value)"
|
|
||||||
/>
|
|
||||||
<div style="overflow: auto" class="flex flex-row">
|
|
||||||
<div class="flex flex-grow medium-small x-pad-1em">
|
|
||||||
Zoom: {{getZoomForDisplay('x')}}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-nogrow flex-noshrink medium-small">
|
|
||||||
<a class="_zoom_reset x-pad-1em" @click="resetZoom()">reset</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div>Horizontal zoom</div>
|
|
||||||
<input id="_input_zoom_slider"
|
|
||||||
class="input-slider"
|
|
||||||
type="range"
|
|
||||||
step="any"
|
|
||||||
min="-1"
|
|
||||||
max="4"
|
|
||||||
:value="zoom.x"
|
|
||||||
@input="changeZoom($event.target.value, 'x')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>Vertical zoom</div>
|
<ZoomOptionsPanel
|
||||||
<input id="_input_zoom_slider"
|
:settings="settings"
|
||||||
class="input-slider"
|
:frame="frame"
|
||||||
type="range"
|
:exec="exec"
|
||||||
step="any"
|
:eventBus="eventBus"
|
||||||
min="-1"
|
:site="site"
|
||||||
max="3"
|
></ZoomOptionsPanel>
|
||||||
:value="zoom.y"
|
|
||||||
@input="changeZoom($event.target.value, 'y')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div style="overflow: auto" class="flex flex-row">
|
|
||||||
<div class="flex flex-grow medium-small x-pad-1em">
|
|
||||||
Zoom: {{getZoomForDisplay('x')}} x {{getZoomForDisplay('y')}}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-nogrow flex-noshrink medium-small">
|
|
||||||
<a class="_zoom_reset x-pad-1em" @click="resetZoom()">reset</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- VIDEO ALIGNMENT -->
|
||||||
<div class="sub-panel">
|
<div class="sub-panel">
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<mdicon name="align-horizontal-center" :size="32" />
|
<mdicon name="align-horizontal-center" :size="32" />
|
||||||
@ -470,34 +110,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import CropOptionsPanel from './PanelComponents/VideoSettings/CropOptionsPanel'
|
||||||
|
import StretchOptionsPanel from './PanelComponents/VideoSettings/StretchOptionsPanel'
|
||||||
import Button from '../../../common/components/Button.vue'
|
import Button from '../../../common/components/Button.vue'
|
||||||
import KeyboardShortcutParser from '../../../common/js/KeyboardShortcutParser';
|
|
||||||
import ShortcutButton from '../../../common/components/ShortcutButton';
|
import ShortcutButton from '../../../common/components/ShortcutButton';
|
||||||
import EditShortcutButton from '../../../common/components/EditShortcutButton';
|
import EditShortcutButton from '../../../common/components/EditShortcutButton';
|
||||||
import ComputeActionsMixin from '../../../common/mixins/ComputeActionsMixin';
|
import ComputeActionsMixin from '../../../common/mixins/ComputeActionsMixin';
|
||||||
import ExecAction from '../ui-libs/ExecAction';
|
import ExecAction from '../ui-libs/ExecAction';
|
||||||
import BrowserDetect from '../../../ext/conf/BrowserDetect';
|
import BrowserDetect from '../../../ext/conf/BrowserDetect';
|
||||||
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
|
|
||||||
import StretchType from '../../../common/enums/StretchType.enum';
|
|
||||||
import CropModePersistence from '../../../common/enums/CropModePersistence.enum';
|
|
||||||
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
|
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
|
||||||
|
import CommsMixin from '../utils/CommsMixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
exec: null,
|
exec: null,
|
||||||
scope: 'page',
|
scope: 'page',
|
||||||
CropModePersistence: CropModePersistence,
|
|
||||||
StretchType: StretchType,
|
|
||||||
AspectRatioType: AspectRatioType,
|
|
||||||
zoomAspectRatioLocked: true,
|
|
||||||
zoom: {
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
},
|
|
||||||
editMode: false,
|
editMode: false,
|
||||||
editModeOptions: {
|
|
||||||
},
|
|
||||||
resizerConfig: {
|
resizerConfig: {
|
||||||
crop: null,
|
crop: null,
|
||||||
stretch: null,
|
stretch: null,
|
||||||
@ -508,26 +139,16 @@ export default {
|
|||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
ComputeActionsMixin,
|
ComputeActionsMixin,
|
||||||
|
CommsMixin,
|
||||||
],
|
],
|
||||||
props: [
|
props: [
|
||||||
'settings',
|
'settings',
|
||||||
'frame',
|
'frame',
|
||||||
'cropModePersistence',
|
|
||||||
'eventBus',
|
'eventBus',
|
||||||
'site'
|
'site'
|
||||||
],
|
],
|
||||||
created() {
|
created() {
|
||||||
this.exec = new ExecAction(this.settings, window.location.hostname);
|
this.exec = new ExecAction(this.settings, window.location.hostname);
|
||||||
// todo: replace event bus with postMessage
|
|
||||||
// this.eventBus.subscribe('announce-zoom', {
|
|
||||||
// function: (config) => {
|
|
||||||
// this.zoom = {
|
|
||||||
// x: Math.log2(config.x),
|
|
||||||
// y: Math.log2(config.y)
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// this.eventBus.send('get-current-config');
|
|
||||||
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
|
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -537,275 +158,16 @@ export default {
|
|||||||
ShortcutButton,
|
ShortcutButton,
|
||||||
EditShortcutButton,
|
EditShortcutButton,
|
||||||
Button,
|
Button,
|
||||||
AlignmentOptionsControlComponent
|
AlignmentOptionsControlComponent,
|
||||||
},
|
StretchOptionsPanel,
|
||||||
computed: {
|
CropOptionsPanel
|
||||||
// because this is passed to a <select>, all the values must be
|
|
||||||
// passed as strings.
|
|
||||||
extensionDefaultCrop() {
|
|
||||||
return JSON.stringify(
|
|
||||||
this.settings?.active.crop?.default ?? {type: AspectRatioType.Automatic}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
siteDefaultCrop() {
|
|
||||||
return JSON.stringify(
|
|
||||||
this.settings?.getDefaultCrop(this.site) ?? {type: AspectRatioType.Automatic}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
extensionDefaultStretchMode () {
|
|
||||||
return JSON.stringify(
|
|
||||||
this.settings?.active.stretch.default ?? {type: StretchType.NoStretch}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
siteDefaultStretchMode () {
|
|
||||||
return JSON.stringify(
|
|
||||||
this.settings?.getDefaultStretchMode(this.site) ?? {type: StretchType.NoStretch}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getZoomForDisplay(axis) {
|
|
||||||
// zoom is internally handled logarithmically, because we want to
|
|
||||||
// have x0.5, x1, x2, x4 ... magnifications spaced out at regular
|
|
||||||
// intervals. When displaying, we need to conver that back to non-
|
|
||||||
// logarithmic units.
|
|
||||||
|
|
||||||
return `${ (Math.pow(2, this.zoom[axis]) * 100).toFixed()}%`;
|
|
||||||
},
|
|
||||||
async openOptionsPage() {
|
async openOptionsPage() {
|
||||||
BrowserDetect.runtime.openOptionsPage();
|
BrowserDetect.runtime.openOptionsPage();
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleZoomAr() {
|
|
||||||
this.zoomAspectRatioLocked = !this.zoomAspectRatioLocked;
|
|
||||||
},
|
|
||||||
|
|
||||||
resetZoom() {
|
|
||||||
// we store zoom logarithmically on this component
|
|
||||||
this.zoom = {x: 0, y: 0};
|
|
||||||
|
|
||||||
// we do not use logarithmic zoom elsewhere
|
|
||||||
// todo: replace eventBus with postMessage to parent
|
|
||||||
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'});
|
|
||||||
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'});
|
|
||||||
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'y'});
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'x'});
|
|
||||||
},
|
|
||||||
changeZoom(newZoom, axis) {
|
|
||||||
// we store zoom logarithmically on this compnent
|
|
||||||
if (!axis) {
|
|
||||||
this.zoom.x = newZoom;
|
|
||||||
} else {
|
|
||||||
this.zoom[axis] = newZoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we do not use logarithmic zoom elsewhere, therefore we need to convert
|
|
||||||
newZoom = Math.pow(2, newZoom);
|
|
||||||
|
|
||||||
if (this.zoomAspectRatioLocked) {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'y'});
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'x'});
|
|
||||||
} else {
|
|
||||||
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: axis ?? 'x'});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
//#region cropping
|
|
||||||
/**
|
|
||||||
* Sets default crop, for either site or global
|
|
||||||
*/
|
|
||||||
setDefaultCrop($event, scope) {
|
|
||||||
const commandArguments = JSON.parse($event.target.value);
|
|
||||||
|
|
||||||
if (scope === 'site') {
|
|
||||||
if (!this.settings.active.sites[this.site]) {
|
|
||||||
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
|
|
||||||
}
|
|
||||||
this.settings.active.sites[this.site].defaultCrop = commandArguments;
|
|
||||||
} else {
|
|
||||||
// eventually, this 'if' will be safe to remove (and we'll be able to only
|
|
||||||
// get away with the 'else' section) Maybe in 6 months or so.
|
|
||||||
if (!this.settings.active.crop) {
|
|
||||||
this.settings.active['crop'] = {
|
|
||||||
default: commandArguments
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.settings.active.crop.default = commandArguments;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a given crop command is the currently active one
|
|
||||||
*/
|
|
||||||
isActiveCrop(cropCommand) {
|
|
||||||
if (! this.resizerConfig.crop) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultCrop = this.settings.getDefaultCrop(this.site);
|
|
||||||
|
|
||||||
if (cropCommand.arguments.type === AspectRatioType.Automatic) {
|
|
||||||
return this.resizerConfig.crop.type === AspectRatioType.Automatic
|
|
||||||
|| this.resizerConfig.crop.type === AspectRatioType.AutomaticUpdate
|
|
||||||
|| this.resizerConfig.crop.type === AspectRatioType.Initial && defaultCrop === AspectRatioType.Automatic;
|
|
||||||
}
|
|
||||||
if (cropCommand.arguments.type === AspectRatioType.Reset) {
|
|
||||||
return this.resizerConfig.crop.type === AspectRatioType.Reset
|
|
||||||
|| this.resizerConfig.crop.type === AspectRatioType.Initial && defaultCrop !== AspectRatioType.Automatic;
|
|
||||||
}
|
|
||||||
if (cropCommand.arguments.type === AspectRatioType.Fixed) {
|
|
||||||
return this.resizerConfig.crop.type === AspectRatioType.Fixed
|
|
||||||
&& this.resizerConfig.crop.ratio === cropCommand.arguments.ratio;
|
|
||||||
}
|
|
||||||
// only legacy options (fitw, fith) left to handle:
|
|
||||||
return cropCommand.arguments.type === this.resizerConfig.crop.type;
|
|
||||||
},
|
|
||||||
//#endregion cropping
|
|
||||||
|
|
||||||
//#region stretch
|
|
||||||
/**
|
|
||||||
* Sets default stretching mode, for either site or global
|
|
||||||
*/
|
|
||||||
setDefaultStretchingMode($event, globalOrSite) {
|
|
||||||
const commandArguments = JSON.parse($event.target.value);
|
|
||||||
|
|
||||||
if (globalOrSite === 'site') {
|
|
||||||
if (!this.settings.active.sites[this.site]) {
|
|
||||||
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
|
|
||||||
}
|
|
||||||
this.settings.active.sites[this.site].defaultStretch = commandArguments;
|
|
||||||
} else {
|
|
||||||
this.settings.active.stretch.default = commandArguments;
|
|
||||||
}
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a given stretch command is the currently active one
|
|
||||||
*/
|
|
||||||
isActiveStretch(stretchCommand) {
|
|
||||||
if (! this.resizerConfig.stretch) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const defaultCrop = this.settings.getDefaultStretch(this.site);
|
|
||||||
|
|
||||||
if ([StretchType.NoStretch, StretchType.Basic, StretchType.Hybrid, StretchType.Conditional, StretchType.Default].includes(stretchCommand.arguments.type)) {
|
|
||||||
return this.resizerConfig.stretch.type === stretchCommand.arguments.type;
|
|
||||||
}
|
|
||||||
return this.resizerConfig.crop.type === stretchCommand.arguments.type && this.resizerConfig.crop.ratio === stretchCommand.arguments.ratio;
|
|
||||||
},
|
|
||||||
//#endregion cropping
|
|
||||||
|
|
||||||
//#region edit mode
|
|
||||||
enableEditMode() {
|
|
||||||
this.editMode = true;
|
|
||||||
this.editModeOptions = {};
|
|
||||||
},
|
|
||||||
|
|
||||||
disableEditMode() {
|
|
||||||
this.editMode = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
editAction(command, index, actionType) {
|
|
||||||
try {
|
|
||||||
if (!this.editModeOptions[actionType]) {
|
|
||||||
this.editModeOptions[actionType] = {selected: command, selectedIndex: index}
|
|
||||||
} else {
|
|
||||||
this.editModeOptions[actionType].selected = command;
|
|
||||||
this.editModeOptions[actionType].selectedIndex = index;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[Ultrawidify] there's a problem with VideoSettings.vue::editAction():`, e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSelectedShortcut(shortcut, actionType) {
|
|
||||||
try {
|
|
||||||
if (!this.editModeOptions[actionType]?.selected) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.editModeOptions[actionType].selected.shortcut = shortcut
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[Ultrawidify] there's a problem with VideoSettings.vue::updateShortcut():`, e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelEdit(actionType) {
|
|
||||||
try {
|
|
||||||
if (!this.editModeOptions[actionType]) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.editModeOptions[actionType] = undefined;;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[Ultrawidify] there's a problem with VideoSettings.vue::cancelEdit():`, e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
saveShortcut(actionType) {
|
|
||||||
if (!this.editModeOptions[actionType]?.selectedIndex) {
|
|
||||||
this.settings.active.commands[actionType].push(this.editModeOptions[actionType].selected);
|
|
||||||
}
|
|
||||||
this.settings.active.commands[actionType][this.editModeOptions[actionType].selectedIndex] = this.editModeOptions[actionType]?.selected;
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
|
|
||||||
this.editModeOptions[actionType] = undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteAction(actionType) {
|
|
||||||
const selectedIndex = this.editModeOptions[actionType].selectedIndex;
|
|
||||||
|
|
||||||
// prevent deleting first item if 'delete' button shows on 'add new' dialog
|
|
||||||
if (selectedIndex === undefined || selectedIndex === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings.active.commands[actionType].splice(selectedIndex, 1);
|
|
||||||
this.settings.saveWithoutReload();
|
|
||||||
|
|
||||||
this.editModeOptions[actionType] = undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region comms and bus
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles 'uw-config-broadcast' messages coming to our
|
|
||||||
*/
|
|
||||||
handleConfigBroadcast(message) {
|
|
||||||
if (message.type === 'ar') {
|
|
||||||
this.resizerConfig.crop = message.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends commands to main content script in parent iframe
|
|
||||||
* @param {*} command
|
|
||||||
*/
|
|
||||||
execAction(command) {
|
|
||||||
const cmd = JSON.parse(JSON.stringify(command));
|
|
||||||
this.eventBus?.sendToTunnel(cmd.action, cmd.arguments);
|
|
||||||
},
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses command's keyboard shortcut into human-readable label
|
|
||||||
*/
|
|
||||||
getKeyboardShortcutLabel(command) {
|
|
||||||
if (! command.shortcut) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return KeyboardShortcutParser.parseShortcut(command.shortcut);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -813,42 +175,3 @@ export default {
|
|||||||
<style lang="scss" src="../../../res/css/flex.scss" scoped module></style>
|
<style lang="scss" src="../../../res/css/flex.scss" scoped module></style>
|
||||||
<style lang="scss" src="../res-common/panels.scss" scoped module></style>
|
<style lang="scss" src="../res-common/panels.scss" scoped module></style>
|
||||||
<style lang="scss" src="../res-common/common.scss" scoped module></style>
|
<style lang="scss" src="../res-common/common.scss" scoped module></style>
|
||||||
|
|
||||||
<style lang="scss" scoped module>
|
|
||||||
@import '../res-common/variables';
|
|
||||||
|
|
||||||
.options-bar {
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
&.isEditing {
|
|
||||||
background-color: $primary;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.b3 {
|
|
||||||
width: 9rem;
|
|
||||||
padding-left: 0.33rem;
|
|
||||||
padding-right: 0.33rem;
|
|
||||||
}
|
|
||||||
.input-slider {
|
|
||||||
width: 480px;
|
|
||||||
}
|
|
||||||
.warning-lite {
|
|
||||||
padding-right: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-action-area {
|
|
||||||
background-color: rgba($blackBg,0.5);
|
|
||||||
padding: 0.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
.edit-action-area-header {
|
|
||||||
background-color: $primary;
|
|
||||||
color: #000;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
padding-top: 0.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -114,3 +114,38 @@ h1, h2, h3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.options-bar {
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
&.isEditing {
|
||||||
|
background-color: $primary;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.b3 {
|
||||||
|
width: 9rem;
|
||||||
|
padding-left: 0.33rem;
|
||||||
|
padding-right: 0.33rem;
|
||||||
|
}
|
||||||
|
.input-slider {
|
||||||
|
width: 480px;
|
||||||
|
}
|
||||||
|
.warning-lite {
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-action-area {
|
||||||
|
background-color: rgba($blackBg,0.5);
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.edit-action-area-header {
|
||||||
|
background-color: $primary;
|
||||||
|
color: #000;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
}
|
||||||
|
@ -9,3 +9,4 @@
|
|||||||
.sub-panel-content {
|
.sub-panel-content {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
src/csui/src/utils/CommsMixin.js
Normal file
20
src/csui/src/utils/CommsMixin.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
handleConfigBroadcast(message) {
|
||||||
|
if (message.type === 'ar') {
|
||||||
|
this.resizerConfig.crop = message.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick( () => this.$forceUpdate() );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends commands to main content script in parent iframe
|
||||||
|
* @param {*} command
|
||||||
|
*/
|
||||||
|
execAction(command) {
|
||||||
|
const cmd = JSON.parse(JSON.stringify(command));
|
||||||
|
this.eventBus?.sendToTunnel(cmd.action, cmd.arguments);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
89
src/csui/src/utils/EditModeMixin.js
Normal file
89
src/csui/src/utils/EditModeMixin.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
editMode: false,
|
||||||
|
editModeOptions: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
enableEditMode() {
|
||||||
|
this.editMode = true;
|
||||||
|
this.editModeOptions = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
disableEditMode() {
|
||||||
|
this.editMode = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
editAction(command, index, actionType) {
|
||||||
|
try {
|
||||||
|
if (!this.editModeOptions[actionType]) {
|
||||||
|
this.editModeOptions[actionType] = {selected: command, selectedIndex: index}
|
||||||
|
} else {
|
||||||
|
this.editModeOptions[actionType].selected = command;
|
||||||
|
this.editModeOptions[actionType].selectedIndex = index;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[Ultrawidify] there's a problem with VideoSettings.vue::editAction():`, e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSelectedShortcut(shortcut, actionType) {
|
||||||
|
try {
|
||||||
|
if (!this.editModeOptions[actionType]?.selected) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.editModeOptions[actionType].selected.shortcut = shortcut
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[Ultrawidify] there's a problem with VideoSettings.vue::updateShortcut():`, e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelEdit(actionType) {
|
||||||
|
try {
|
||||||
|
if (!this.editModeOptions[actionType]) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.editModeOptions[actionType] = undefined;;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[Ultrawidify] there's a problem with VideoSettings.vue::cancelEdit():`, e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveShortcut(actionType) {
|
||||||
|
if (!this.editModeOptions[actionType]?.selectedIndex) {
|
||||||
|
this.settings.active.commands[actionType].push(this.editModeOptions[actionType].selected);
|
||||||
|
}
|
||||||
|
this.settings.active.commands[actionType][this.editModeOptions[actionType].selectedIndex] = this.editModeOptions[actionType]?.selected;
|
||||||
|
this.settings.saveWithoutReload();
|
||||||
|
|
||||||
|
this.editModeOptions[actionType] = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteAction(actionType) {
|
||||||
|
const selectedIndex = this.editModeOptions[actionType].selectedIndex;
|
||||||
|
|
||||||
|
// prevent deleting first item if 'delete' button shows on 'add new' dialog
|
||||||
|
if (selectedIndex === undefined || selectedIndex === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settings.active.commands[actionType].splice(selectedIndex, 1);
|
||||||
|
this.settings.saveWithoutReload();
|
||||||
|
|
||||||
|
this.editModeOptions[actionType] = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses command's keyboard shortcut into human-readable label
|
||||||
|
*/
|
||||||
|
getKeyboardShortcutLabel(command) {
|
||||||
|
if (! command.shortcut) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return KeyboardShortcutParser.parseShortcut(command.shortcut);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
15
src/csui/src/utils/KeyboardShortcutParserMixin.js
Normal file
15
src/csui/src/utils/KeyboardShortcutParserMixin.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import KeyboardShortcutParser from '../../../common/js/KeyboardShortcutParser';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Parses command's keyboard shortcut into human-readable label
|
||||||
|
*/
|
||||||
|
getKeyboardShortcutLabel(command) {
|
||||||
|
if (! command.shortcut) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return KeyboardShortcutParser.parseShortcut(command.shortcut);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -20,8 +20,13 @@
|
|||||||
Build channel: {{BrowserDetect?.processEnvChannel}}
|
Build channel: {{BrowserDetect?.processEnvChannel}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="">
|
||||||
|
TODO: force open UI button
|
||||||
|
</div>
|
||||||
<div class="flex flex-row body no-overflow flex-grow">
|
<div class="flex flex-row body no-overflow flex-grow">
|
||||||
|
<div class="">
|
||||||
|
side menu
|
||||||
|
</div>
|
||||||
<pre>
|
<pre>
|
||||||
---- site:
|
---- site:
|
||||||
{{site}}
|
{{site}}
|
||||||
@ -262,7 +267,7 @@ html {
|
|||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
font-size: 2.7em;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
.header-small {
|
.header-small {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
74
src/popup/panels/PopupVideoSettings.vue
Normal file
74
src/popup/panels/PopupVideoSettings.vue
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-row flex-wrap" style="padding-bottom: 20px">
|
||||||
|
|
||||||
|
<div v-if="settings" class="sub-panel">
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<mdicon name="crop" :size="32" />
|
||||||
|
<h1>Crop video:</h1>
|
||||||
|
</div>
|
||||||
|
<div class="sub-panel-content flex flex-row flex-wrap">
|
||||||
|
<ShortcutButton
|
||||||
|
v-for="(command, index) of settings?.active.commands.crop"
|
||||||
|
class="flex b3 button"
|
||||||
|
:class="{active: editMode ? index === editModeOptions?.crop?.selectedIndex : isActiveCrop(command)}"
|
||||||
|
:key="index"
|
||||||
|
:label="command.label"
|
||||||
|
:shortcut="getKeyboardShortcutLabel(command)"
|
||||||
|
@click="editMode ? editAction(command, index, 'crop') : execAction(command)"
|
||||||
|
>
|
||||||
|
</ShortcutButton>
|
||||||
|
|
||||||
|
<!-- "Add new" button -->
|
||||||
|
<ShortcutButton
|
||||||
|
v-if="editMode"
|
||||||
|
class="button b3"
|
||||||
|
:class="{active: editMode ? editModeOptions?.crop?.selectedIndex === null : isActiveCrop(command)}"
|
||||||
|
label="Add new"
|
||||||
|
@click="editAction(
|
||||||
|
{action: 'set-ar', label: 'New aspect ratio', arguments: {type: AspectRatioType.Fixed}},
|
||||||
|
null,
|
||||||
|
'crop'
|
||||||
|
)"
|
||||||
|
></ShortcutButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-action-area">
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Default for this site</div>
|
||||||
|
<div class="select">
|
||||||
|
<select
|
||||||
|
:value="siteDefaultCrop"
|
||||||
|
@click="setDefaultCrop($event, 'site')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(command, index) of settings?.active.commands.crop"
|
||||||
|
:key="index"
|
||||||
|
:value="JSON.stringify(command.arguments)"
|
||||||
|
>
|
||||||
|
{{command.label}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="label">Extension default</div>
|
||||||
|
<div class="select">
|
||||||
|
<select
|
||||||
|
:value="extensionDefaultCrop"
|
||||||
|
@click="setDefaultCrop($event, 'global')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(command, index) of settings?.active.commands.crop"
|
||||||
|
:key="index"
|
||||||
|
:value="JSON.stringify(command.arguments)"
|
||||||
|
>
|
||||||
|
{{command.label}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user