Spin off VideoSettings component into subcomponents

This commit is contained in:
Tamius Han 2022-11-20 22:45:34 +01:00
parent 13bfd63dc2
commit b9b021f466
11 changed files with 977 additions and 715 deletions

View File

@ -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>&nbsp;
<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>
&nbsp;
<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>

View File

@ -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>&nbsp;
<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>
&nbsp;
<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>

View File

@ -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>

View File

@ -11,7 +11,7 @@
</div>
<div
class="flex-nogrow flex-noshrink"
@click="disableEditMode()"
@click="editMode = !editMode"
>
Exit edit mode
</div>
@ -20,7 +20,7 @@
<div class="flex-grow"></div>
<div
class=""
@click="enableEditMode()"
@click="editMode = !editMode"
>
Edit ratios and shortcuts
</div>
@ -36,153 +36,16 @@
<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"
<CropOptionsPanel
:settings="settings"
:frame="frame"
:exec="exec"
:eventBus="eventBus"
:site="site"
:isEditing="editMode"
>
<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>&nbsp;
<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>
&nbsp;
<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>
</CropOptionsPanel>
</div>
<!-- STRETCH OPTIONS -->
@ -191,173 +54,15 @@
<mdicon name="stretch-to-page-outline" :size="32" />
<h1>Stretch video:</h1>
</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 -->
<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>&nbsp;
<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>
&nbsp;
<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>
<StretchOptionsPanel
:settings="settings"
:frame="frame"
:exec="exec"
:eventBus="eventBus"
:site="site"
:isEditing="editMode"
></StretchOptionsPanel>
</div>
@ -367,82 +72,17 @@
<mdicon name="magnify-plus-outline" :size="32" />
<h1>Manual zoom:</h1>
</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>
<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>
<ZoomOptionsPanel
:settings="settings"
:frame="frame"
:exec="exec"
:eventBus="eventBus"
:site="site"
></ZoomOptionsPanel>
</div>
<!-- VIDEO ALIGNMENT -->
<div class="sub-panel">
<div class="flex flex-row">
<mdicon name="align-horizontal-center" :size="32" />
@ -470,34 +110,25 @@
</template>
<script>
import CropOptionsPanel from './PanelComponents/VideoSettings/CropOptionsPanel'
import StretchOptionsPanel from './PanelComponents/VideoSettings/StretchOptionsPanel'
import Button from '../../../common/components/Button.vue'
import KeyboardShortcutParser from '../../../common/js/KeyboardShortcutParser';
import ShortcutButton from '../../../common/components/ShortcutButton';
import EditShortcutButton from '../../../common/components/EditShortcutButton';
import ComputeActionsMixin from '../../../common/mixins/ComputeActionsMixin';
import ExecAction from '../ui-libs/ExecAction';
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 CommsMixin from '../utils/CommsMixin';
export default {
data() {
return {
exec: null,
scope: 'page',
CropModePersistence: CropModePersistence,
StretchType: StretchType,
AspectRatioType: AspectRatioType,
zoomAspectRatioLocked: true,
zoom: {
x: 0,
y: 0
},
editMode: false,
editModeOptions: {
},
resizerConfig: {
crop: null,
stretch: null,
@ -508,26 +139,16 @@ export default {
},
mixins: [
ComputeActionsMixin,
CommsMixin,
],
props: [
'settings',
'frame',
'cropModePersistence',
'eventBus',
'site'
],
created() {
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)});
},
mounted() {
@ -537,275 +158,16 @@ export default {
ShortcutButton,
EditShortcutButton,
Button,
AlignmentOptionsControlComponent
},
computed: {
// 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}
);
}
AlignmentOptionsControlComponent,
StretchOptionsPanel,
CropOptionsPanel
},
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() {
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>
@ -813,42 +175,3 @@ export default {
<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/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>

View File

@ -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;
}

View File

@ -9,3 +9,4 @@
.sub-panel-content {
margin-top: 1.5rem;
}

View 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);
},
}
}

View 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);
},
}
}

View 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);
},
}
}

View File

@ -20,8 +20,13 @@
Build channel: {{BrowserDetect?.processEnvChannel}}
</div>
</div>
<div class="">
TODO: force open UI button
</div>
<div class="flex flex-row body no-overflow flex-grow">
<div class="">
side menu
</div>
<pre>
---- site:
{{site}}
@ -262,7 +267,7 @@ html {
padding-top: 8px;
padding-left: 15px;
padding-bottom: 1px;
font-size: 2.7em;
font-size: 1.75rem;
}
.header-small {
overflow: hidden;

View 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>