smoosic
Version:
<sub>[Github site](https://github.com/Smoosic/smoosic) | [source documentation](https://smoosic.github.io/Smoosic/release/docs/modules.html) | [change notes](https://aarondavidnewman.github.io/Smoosic/changes.html) | [application](https://smoosic.github.i
387 lines (374 loc) • 13 kB
text/typescript
import { SmoScoreText, SmoTextGroup } from '../../../smo/data/scoreText';
import { KeyEvent, ElementLike, RemoveElementLike } from '../../../smo/data/common';
import { SuiTextSession } from '../../../render/sui/textEdit';
import { SuiScroller } from '../../../render/sui/scroller';
import { SuiScoreViewOperations } from '../../../render/sui/scoreViewOperations';
import { SuiDialogNotifier, SuiComponentBase, SuiComponentParent } from './baseComponent';
import { SuiButtonComposite } from './button';
import { SuiRockerComposite } from './rocker';
import { SuiDropdownComposite } from './dropdown';
import { SuiInlineText } from '../../../render/sui/textRender';
import { buildDom } from '../../../common/htmlHelpers';
declare var $: any;
/**
* @category SuiDialog
*/
export interface SuiTextInPlaceParams {
id: string,
classes: string,
label: string,
smoName: string,
control: string
}
/**
* Edit the text in an SVG element, in the same scale etc. as the text in the score SVG DOM.
* This component just manages the text editing component of the renderer.
* @category SuiDialog
*/
export class SuiTextInPlace extends SuiComponentBase {
scroller: SuiScroller;
editMode: boolean = false;
value: SmoTextGroup;
staticText: Record<string, string>;
altLabel: string;
view: SuiScoreViewOperations;
session: SuiTextSession | null = null;
constructor(dialog: SuiDialogNotifier, parameter: SuiTextInPlaceParams) {
super(dialog, parameter);
this.scroller = dialog.getView().scroller;
this.value = new SmoTextGroup(SmoTextGroup.defaults);
this.view = this.dialog.getView();
const modifier = this.dialog.getModifier();
if (modifier && SmoTextGroup.isTextGroup(modifier)) {
this.value = modifier;
}
this.staticText = this.dialog.getStaticText();
this.altLabel = this.staticText.editorLabel;
}
show() {}
hide() {}
get html() {
const b = buildDom;
const id = this.parameterId;
const r = b('div').classes(this.makeClasses('cbTextInPlace smoControl')).attr('id', this.parameterId).attr('data-param', this.smoName)
.append(b('button').attr('type', 'checkbox').classes('toggleTextEdit')
.attr('id', id + '-input').append(
b('span').classes('icon icon-pencil'))
.append(
b('label').attr('for', id + '-input').text(this.label)));
return r;
}
endSession() {
$(this._getInputElement()).find('label').text(this.label);
const button = document.getElementById(this.parameterId);
$(button).find('span.icon').removeClass('icon-checkmark').addClass('icon-pencil');
this.value.skipRender = false;
if (this.session) {
this.session.textGroup.tryParseUnicode();
this.value = this.session.textGroup;
this.session.stopSession();
}
$('body').removeClass('text-edit');
this.handleChanged();
}
get isRunning() {
return this.session && this.session.isRunning;
}
getValue() {
return this.value;
}
_getInputElement() {
var pid = this.parameterId;
return $(this.dialog.dgDom.element).find('#' + pid).find('button');
}
mouseMove(ev: any) {
if (this.session && this.session.isRunning) {
this.session.handleMouseEvent(ev);
}
}
mouseClick(ev: any) {
if (this.session && this.session.isRunning) {
this.session.handleMouseEvent(ev);
}
}
_renderInactiveBlocks() {
const modifier = this.value;
const context = this.view.renderer.pageMap.getRendererFromModifier(this.value).getContext();
context.save();
context.setFillStyle('#ddd');
const svgPage = this.view.renderer.pageMap.getRendererFromModifier(this.value);
modifier.textBlocks.forEach((block) => {
const st = block.text;
if (st.attrs.id !== this.value.getActiveBlock().attrs.id && svgPage) {
const svgText = SuiInlineText.fromScoreText(st, svgPage, this.view.renderer.pageMap, this.scroller);
if (st.logicalBox) {
svgText.startX += st.logicalBox.x - st.x;
svgText.startY += (st.y - st.logicalBox.y) - st.logicalBox.height / 2;
}
const sgrp = context.openGroup();
sgrp.classList.add('inactive-text');
sgrp.classList.add('suiInlineText');
svgText.render();
context.closeGroup();
}
});
context.restore();
}
startEditSession() {
$(this._getInputElement()).find('label').text(this.altLabel);
const modifier = this.value;
modifier.skipRender = true;
let pageContext = this.view.renderer.pageMap.getRendererFromModifier(this.value);
$(pageContext.svg).find('#' + modifier.attrs.id).remove();
this._renderInactiveBlocks();
const ul = modifier.ul();
// this.textElement=$(this.dialog.layout.svg).find('.'+modifier.attrs.id)[0];
this.session = new SuiTextSession({
renderer: this.view.renderer,
scroller: this.scroller,
x: ul.x,
y: ul.y,
textGroup: modifier,
text: modifier.getActiveBlock().text,
scoreText: modifier.getActiveBlock()
});
$('body').addClass('text-edit');
this.value = this.session.textGroup;
const button = document.getElementById(this.parameterId);
$(button).find('span.icon').removeClass('icon-pencil').addClass('icon-checkmark');
// Erase the original since we are going to edit it now.
if (this.value) {
this.value.elements.forEach((el: ElementLike) => {
RemoveElementLike(el);
});
this.value.elements = [];
}
this.session.startSession();
// blur the button so key events don't get passed to it.
$(this._getInputElement()).blur();
}
evKey(evdata: KeyEvent) {
if (this.session) {
this.session.evKey(evdata);
}
}
bind() {
$(this._getInputElement()).off('click').on('click', () => {
if (this.session && this.session.isRunning) {
this.endSession();
} else {
this.startEditSession();
}
});
}
}
/**
* @category SuiDialog
*/
export interface SuiTextBlockComponentParams {
id: string,
classes: string,
label: string,
smoName: string,
control: string
}
/**
* @category SuiDialog
*/
export interface SuiTextBlockValue {
modifier: SmoTextGroup,
activeScoreText: SmoScoreText
}
/**
* Dialog to edit an block of text components. Each block can have
* different font settings and placement.
* @category SuiDialog
*/
export class SuiTextBlockComponent extends SuiComponentParent {
addBlockCtrl: SuiButtonComposite;
toggleBlockCtrl: SuiButtonComposite;
removeBlockCtrl: SuiButtonComposite;
relativePositionCtrl: SuiDropdownComposite;
justificationCtrl: SuiDropdownComposite;
spacingCtrl: SuiRockerComposite;
modifier: SmoTextGroup;
activeScoreText: SmoScoreText;
constructor(dialog: SuiDialogNotifier, parameter: SuiTextBlockComponentParams) {
super(dialog, parameter);
this.addBlockCtrl = new SuiButtonComposite(this.dialog,
{
id: this.id + 'addBlock',
smoName: 'addBlock',
parentControl: this,
icon: 'icon-plus',
classes: 'hide-when-editing hide-when-moving',
control: 'SuiButtonComponent',
label: 'Add Text Block'
});
this.toggleBlockCtrl = new SuiButtonComposite(this.dialog,
{
id: this.id + 'toggleBlock',
smoName: 'toggleBlock',
parentControl: this,
icon: 'icon-arrow-right',
classes: 'hide-when-editing hide-when-moving',
control: 'SuiButtonComponent',
label: 'Next Block'
});
this.removeBlockCtrl = new SuiButtonComposite(this.dialog,
{
id: this.id + 'removeBlock',
smoName: 'removeBlock',
parentControl: this,
icon: 'icon-minus',
classes: 'hide-when-editing hide-when-moving',
control: 'SuiButtonComponent',
label: 'Remove Block'
});
this.relativePositionCtrl = new SuiDropdownComposite(
this.dialog,
{
id: this.id + 'relativePosition',
smoName: 'relativePosition',
parentControl: this,
classes: 'hide-when-editing hide-when-moving',
control: 'SuiDropdownComponent',
label: 'Block Positions',
options: [{
value: SmoTextGroup.relativePositions.ABOVE,
label: 'Above'
}, {
value: SmoTextGroup.relativePositions.BELOW,
label: 'Below'
}, {
value: SmoTextGroup.relativePositions.LEFT,
label: 'Left'
}, {
value: SmoTextGroup.relativePositions.RIGHT,
label: 'Right'
}]
}
);
this.justificationCtrl = new SuiDropdownComposite(
this.dialog,
{
id: this.id + 'justification',
smoName: 'justification',
parentControl: this,
classes: 'hide-when-editing hide-when-moving',
control: 'SuiDropdownComponent',
label: 'Justification',
options: [{
value: SmoTextGroup.justifications.LEFT,
label: 'Left'
}, {
value: SmoTextGroup.justifications.RIGHT,
label: 'Right'
}, {
value: SmoTextGroup.justifications.CENTER,
label: 'Center'
}]
});
this.spacingCtrl = new SuiRockerComposite(
this.dialog,
{
id: this.id + 'spacing',
smoName: 'spacing',
defaultValue: 0,
parentControl: this,
classes: 'hide-when-editing hide-when-moving',
control: 'SuiRockerComponent',
label: 'Spacing',
dataType: 'float',
increment: 0.1
},
);
const mod = this.dialog.getModifier();
if (mod && SmoTextGroup.isTextGroup(mod)) {
this.modifier = mod;
} else {
this.modifier = new SmoTextGroup(SmoTextGroup.defaults);
}
this.activeScoreText = this.modifier.textBlocks[0].text;
}
changed() {
if (this.addBlockCtrl.changeFlag && this.modifier) {
const nt = new SmoScoreText(this.activeScoreText);
this.modifier.addScoreText(nt);
this.activeScoreText = nt;
this.modifier.setActiveBlock(nt);
this._updateMultiiFields();
}
if (this.relativePositionCtrl.changeFlag) {
this.modifier.setRelativePosition(parseInt(this.relativePositionCtrl.getValue().toString(), 10));
}
if (this.justificationCtrl.changeFlag) {
this.modifier.justification = parseInt(this.justificationCtrl.getValue().toString(), 10);
}
if (this.removeBlockCtrl.changeFlag) {
this.modifier.removeBlock(this.activeScoreText);
this.activeScoreText = this.modifier.firstBlock();
this._updateMultiiFields();
}
if (this.toggleBlockCtrl.changeFlag) {
const curIx = this.modifier.indexOf(this.activeScoreText);
const newIx = (curIx + 1) % this.modifier.textBlocks.length;
this.activeScoreText = this.modifier.textBlocks[newIx].text;
this.modifier.setActiveBlock(this.activeScoreText);
}
if (this.spacingCtrl.changeFlag) {
const val = this.spacingCtrl.getValue();
if (val >= 0) {
this.modifier.spacing = val;
}
}
this.handleChanged();
}
get html() {
const b = buildDom;
const q = b('div').classes(this.makeClasses('multiControl smoControl'));
q.append(this.addBlockCtrl.html);
q.append(this.removeBlockCtrl.html);
q.append(this.toggleBlockCtrl.html);
q.append(this.relativePositionCtrl.html);
q.append(this.justificationCtrl.html);
q.append(this.spacingCtrl.html);
return q;
}
_getInputElement() {
return $(this.dialog.dgDom.element).find('#' + this.parameterId);
}
getValue() {
return {
activeScoreText: this.activeScoreText,
modifier: this.modifier
};
}
_updateMultiiFields() {
const fields = [this.justificationCtrl, this.relativePositionCtrl,
this.removeBlockCtrl, this.toggleBlockCtrl, this.spacingCtrl];
fields.forEach((field) => {
if (this.modifier.textBlocks.length < 2) {
$('#' + field.parameterId).addClass('hide');
} else {
$('#' + field.parameterId).removeClass('hide');
}
});
}
setValue(value: SuiTextBlockValue) {
this.activeScoreText = value.activeScoreText;
this.modifier = value.modifier;
this.relativePositionCtrl.setValue(this.modifier.relativePosition);
this._updateMultiiFields();
this.justificationCtrl.setValue(this.modifier.justification);
this.spacingCtrl.setValue(this.modifier.spacing);
}
bind() {
this.addBlockCtrl.bind();
this.relativePositionCtrl.bind();
this.justificationCtrl.bind();
this.removeBlockCtrl.bind();
this.toggleBlockCtrl.bind();
this.spacingCtrl.bind();
}
}