Skip to content

Commit

Permalink
Merge tag 'v0.9.6-preview.0' into merge-upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
berlysia committed Jul 9, 2019
2 parents 6d9e45e + 113faf4 commit 59a6272
Show file tree
Hide file tree
Showing 19 changed files with 334 additions and 116 deletions.
5 changes: 3 additions & 2 deletions app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Raven from 'raven-js';
import RavenVue from 'raven-js/plugins/vue';
import RavenConsole from 'raven-js/plugins/console';
import VTooltip from 'v-tooltip';
import Toasted from 'vue-toasted';
import VueI18n from 'vue-i18n';
import moment from 'moment';
import { setupGlobalContextMenuForEditableElement } from 'util/menus/GlobalMenu';
Expand Down Expand Up @@ -86,9 +87,9 @@ require('./app.less');
// Initiates tooltips and sets their parent wrapper
Vue.use(VTooltip);
VTooltip.options.defaultContainer = '#mainWrapper';

Vue.use(Toasted);
Vue.use(VeeValidate); // form validations
Vue.use(VModal);
Vue.use(VeeValidate);


// Disable chrome default drag/drop behavior
Expand Down
42 changes: 14 additions & 28 deletions app/components/Tabs.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<template>
<div>
<div>
<ul class="tab-container">
<li
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.value"
class="tab"
:class="{ 'active': tab.value === value }"
class="tab-button"
:class="{ active: tab.value === value }"
@click="showTab(tab.value)">
{{ tab.name }}
</li>
</ul>
</button>
</div>
</div>
<div>
<slot v-for="tab in tabs" :name="tab.value" v-if="tab.value === value"/>
Expand All @@ -23,29 +23,15 @@
<style lang="less" scoped>
@import "../styles/_colors";
.tab-container {
.tabs {
display: flex;
flex-direction: row;
list-style-type: none;
margin: 0;
}
.tab {
flex-grow: 1;
text-align: center;
padding: 10px 0;
cursor: pointer;
background-color: @bg-primary;
box-sizing: border-box;
border-radius: 3px 3px 0 0;
border: 1px solid @border;
&.active {
border-bottom: none;
}
&:first-of-type {
border-left: none;
}
&:last-of-type {
border-right: none;
}
position: absolute;
z-index: 1;
width: 100%;
padding: 0 15px;
height: 54px;
border-bottom: 1px solid @border;
}
</style>
2 changes: 1 addition & 1 deletion app/components/shared/Display.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="display" ref="display"></div>
<div class="display" ref="display" @click="onClickHandler"></div>
</template>

<script lang="ts" src="./Display.vue.ts"></script>
Expand Down
5 changes: 5 additions & 0 deletions app/components/shared/Display.vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default class Display extends Vue {
@Prop() sourceId: string;
@Prop({ default: 0 }) paddingSize: number;
@Prop({ default: false }) drawUI: boolean;
@Prop() clickHandler: boolean;

$refs: {
display: HTMLElement
Expand All @@ -26,6 +27,10 @@ export default class Display extends Vue {
this.createDisplay();
}

onClickHandler(event: MouseEvent) {
this.$emit('click', event);
}

createDisplay() {
const displayId = this.videoService.getRandomDisplayId();
this.display = new OBSDisplay(displayId, {
Expand Down
71 changes: 50 additions & 21 deletions app/components/shared/forms/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,13 @@ export function inputValuesToObsValues(


export function getPropertiesFormData(obsSource: obs.ISource): TFormData {
setupSourceDefaults(obsSource);

const sourceType = obsSource.id;
const formData: TFormData = [];
const obsProps = obsSource.properties;
const obsSettings = obsSource.settings;

setupConfigurableDefaults(obsSource, obsProps, obsSettings);

if (!obsProps) return null;
if (!obsProps.count()) return null;

Expand Down Expand Up @@ -432,7 +432,7 @@ export function getPropertiesFormData(obsSource: obs.ISource): TFormData {
export function setPropertiesFormData(obsSource: obs.ISource, form: TFormData) {
const buttons: IFormInput<boolean>[] = [];
const formInputs: IFormInput<TObsValue>[] = [];
const properties = obsSource.properties;
let properties = null;

form.forEach(item => {
if (item.type === 'OBS_PROPERTY_BUTTON') {
Expand All @@ -442,6 +442,14 @@ export function setPropertiesFormData(obsSource: obs.ISource, form: TFormData) {
}
});

/* Don't fetch properties unless we use it. */
if (buttons.length !== 0) properties = obsSource.properties;

for (const button of buttons) {
const obsButtonProp = properties.get(button.name) as obs.IButtonProperty;
obsButtonProp.buttonClicked(obsSource);
}

const settings: Dictionary<any> = {};
formInputs.forEach(property => {
settings[property.name] = property.value;
Expand All @@ -452,35 +460,56 @@ export function setPropertiesFormData(obsSource: obs.ISource, form: TFormData) {
}
});

obsSource.update(settings);
/* Don't update unless we need to. */
if (formInputs.length === 0) return;

buttons.forEach(buttonInput => {
if (!buttonInput.value) return;
const obsButtonProp = properties.get(buttonInput.name) as obs.IButtonProperty;
obsButtonProp.buttonClicked(obsSource);
});
obsSource.update(settings);
}


export function setupSourceDefaults(obsSource: obs.ISource) {
const propSettings = obsSource.settings;
/* Passing a properties and settings object here
* prevents a copy and object creation which
* also requires IPC. Highly recommended to
* pass all parameters. */
export function setupConfigurableDefaults(
configurable: obs.IConfigurable,
properties?: obs.IProperties,
settings?: obs.ISettings
) {
if (!settings) settings = configurable.settings;
if (!properties) properties = configurable.properties;
const defaultSettings = {};
const properties = obsSource.properties;

if (!properties) return;
if (!properties.count()) return;

let obsProp = properties.first();
do {
if (
propSettings[obsProp.name] !== void 0 ||
!isListProperty(obsProp) ||
obsProp.details.items.length === 0
) continue;
defaultSettings[obsProp.name] = obsProp.details.items[0].value;
} while (obsProp = obsProp.next());
if (!isListProperty(obsProp)) continue;

const items = obsProp.details.items;

if (items.length === 0) continue;

/* If setting isn't set at all, set to first element. */
if (settings[obsProp.name] === void 0) {
defaultSettings[obsProp.name] = items[0].value;
continue;
}

let validItem = false;

/* If there is a setting, make sure it's a valid item */
for (let i = 0; i < items.length; ++i) {
if (settings[obsProp.name] === items[i].value) {
validItem = true;
break;
}
}

if (!validItem) defaultSettings[obsProp.name] = items[0].value;
} while ((obsProp = obsProp.next()));
const needUpdate = Object.keys(defaultSettings).length > 0;
if (needUpdate) obsSource.update(defaultSettings);
if (needUpdate) configurable.update(defaultSettings);
}

export abstract class Input<TValueType> extends Vue {
Expand Down
4 changes: 4 additions & 0 deletions app/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// <reference path="../vendor/toasted.d.ts" />

// all global interfaces here

interface Dictionary<TItemType> {
Expand Down Expand Up @@ -53,6 +55,8 @@ declare module 'vuedraggable';
declare module 'font-manager';
declare module 'vue-codemirror';
declare module 'recursive-readdir';
declare module 'vue-toasted';
declare module 'hyperform';
declare module '*.svg';

// uncomment to allow TS to import components without type definitions
Expand Down
12 changes: 7 additions & 5 deletions app/services-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ export class ServicesManager extends Service {
subscriptions: Dictionary<Subscription> = {};

init() {

// this helps to debug services from the console
if (Utils.isDevMode()) {
window['sm'] = this;
}

if (!Utils.isMainWindow()) {
Service.setupProxy(service => this.applyIpcProxy(service));
Service.setupInitFunction(service => {
Expand All @@ -170,10 +176,6 @@ export class ServicesManager extends Service {

Service.serviceAfterInit.subscribe(service => this.initObservers(service));

// this helps to debug services from console
if (Utils.isDevMode()) {
window['sm'] = this;
}
}

private initObservers(observableService: Service): Service[] {
Expand Down Expand Up @@ -396,7 +398,7 @@ export class ServicesManager extends Service {
* @example
* source = getResource('Source[12]')
*/
private getResource(resourceId: string) {
getResource(resourceId: string) {
if (resourceId === 'ServicesManager') {
return this;
}
Expand Down
19 changes: 17 additions & 2 deletions app/services/scene-collections/nodes/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Inject } from '../../../util/injector';
import * as obs from '../../../../obs-api';
import * as fi from 'node-fontinfo';
import { $t } from 'services/i18n';
import { ScenesService } from 'services/scenes';

interface ISchema {
items: ISourceInfo[];
Expand Down Expand Up @@ -57,13 +58,27 @@ export class SourcesNode extends Node<ISchema, {}> {
@Inject() private fontLibraryService: FontLibraryService;
@Inject() private sourcesService: SourcesService;
@Inject() private audioService: AudioService;
@Inject() private scenesService: ScenesService;

getItems() {
return this.sourcesService.sources.filter(source => source.type !== 'scene');

const linkedSourcesIds = this.scenesService.getSceneItems()
.map(sceneItem => sceneItem.sourceId);

return this.sourcesService.sources.filter(source => {
// we store scenes in separated config
if (source.type === 'scene') return false;

// global audio sources must be saved
if (source.channel) return true;

// prevent sources without linked sceneItems to be saved
if (!linkedSourcesIds.includes(source.sourceId)) return false;
return true;
});
}

save(context: {}): Promise<void> {
const items: ISourceInfo[] = [];
const promises: Promise<ISourceInfo>[] = this.getItems().map(source => {
return new Promise(resolve => {
const hotkeys = new HotkeysNode();
Expand Down
13 changes: 9 additions & 4 deletions app/services/scenes/scene-folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { Inject } from 'util/injector';
import { Selection, SelectionService } from 'services/selection';
import {
ISceneItemFolderApi,
ISceneItemNode,
TSceneNodeType,
SceneItem,
ISceneHierarchy,
TSceneNode
Expand Down Expand Up @@ -108,12 +106,19 @@ export class SceneItemFolder extends SceneItemNode implements ISceneItemFolderAp
});
}

getNestedNodes(): TSceneNode[] {
getNestedNodes(traversedNodesIds: string[] = []): TSceneNode[] {
traversedNodesIds = [].concat(traversedNodesIds);
const nodes: TSceneNode[] = [];
this.getNodes().forEach(node => {
if (traversedNodesIds.includes(node.id)) {
// TODO: find the use-case that causes loops in folders structure
console.error(`Loop in folders structure detected', ${this.name} -> ${node.name}`);
return;
}
nodes.push(node);
traversedNodesIds.push(node.id);
if (node.sceneNodeType !== 'folder') return;
nodes.push(...((node as SceneItemFolder).getNestedNodes()));
nodes.push(...((node as SceneItemFolder).getNestedNodes(traversedNodesIds)));
});
return nodes;
}
Expand Down
8 changes: 6 additions & 2 deletions app/services/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Subject } from 'rxjs/Subject';

const singleton = Symbol();
const singletonEnforcer = Symbol();
const instances: Service[] = [];

export abstract class Service {
static hasInstance = false;
static isSingleton = true;

/**
Expand All @@ -32,6 +32,10 @@ export abstract class Service {
return this.proxyFn ? this.proxyFn(instance) : instance;
}

static get hasInstance(): boolean {
return !!instances[this.name];
}

/**
* proxy function will be applied for all services instances
*/
Expand All @@ -53,10 +57,10 @@ export abstract class Service {
if (ServiceClass.hasInstance) {
throw 'Unable to create more than one singleton service';
}
ServiceClass.hasInstance = true;
ServiceClass.isSingleton = true;
const instance = new ServiceClass(singletonEnforcer);
ServiceClass[singleton] = instance;
instances[ServiceClass.name] = instance;

const mustInit = this.initFn ? !this.initFn(instance) : true;

Expand Down
2 changes: 2 additions & 0 deletions app/services/sources/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class Source implements ISourceApi {

updateSettings(settings: Dictionary<any>) {
this.getObsInput().update(settings);
this.sourcesService.sourceUpdated.next(this.sourceState);
}


Expand Down Expand Up @@ -121,6 +122,7 @@ export class Source implements ISourceApi {
setPropertiesFormData(properties: TFormData) {
const manager = this.sourcesService.propertiesManagers[this.sourceId].manager;
manager.setPropertiesFormData(properties);
this.sourcesService.sourceUpdated.next(this.sourceState);
}


Expand Down
1 change: 1 addition & 0 deletions app/services/sources/sources-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface ISourcesServiceApi {
settings?: Dictionary<any>,
options?: ISourceCreateOptions
): ISourceApi;
removeSource(id: string): void;
getAvailableSourcesTypes(): TSourceType[];
getAvailableSourcesTypesList(): IListOption<TSourceType>[];
getSources(): ISourceApi[];
Expand Down
Loading

0 comments on commit 59a6272

Please sign in to comment.