Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom layout #832

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 91 additions & 23 deletions editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { error, success } from './utilities/messages.js';
import { Canvas } from 'lively.components/canvas.js';
import { TIMELINE_CONSTANTS } from './timeline/constants.js';
import { LabeledCheckBox, DropDownSelector } from 'lively.components/widgets.js';
import { ResizeablePanel } from './utilities/resizeable-panel.js';

const CONSTANTS = {
EDITOR_WIDTH: 1000,
Expand All @@ -37,7 +38,8 @@ const CONSTANTS = {
MENU_BAR_WIDGET_WIDTH: 100,
MENU_BAR_WIDGET_HEIGHT: 25,
FONT_SIZE_TEXT: 18,
FONT_SIZE_HEADINGS: 20
FONT_SIZE_HEADINGS: 20,
SCROLL_BAR_HEIGHT: 10
};
CONSTANTS.SIDEBAR_WIDTH = (CONSTANTS.EDITOR_WIDTH - CONSTANTS.PREVIEW_WIDTH) / 2;
CONSTANTS.TIMELINE_HEIGHT = CONSTANTS.EDITOR_HEIGHT - CONSTANTS.SUBWINDOW_HEIGHT - CONSTANTS.MENU_BAR_HEIGHT;
Expand Down Expand Up @@ -91,20 +93,23 @@ export class InteractivesEditor extends QinoqMorph {
if (!this._deserializing) this.ui = {};
}
},
_latestSubWindowRatio: {
defaultValue: CONSTANTS.SUBWINDOW_HEIGHT / CONSTANTS.EDITOR_HEIGHT
},
_snappingDisabled: {}
};
}

async initialize () {
if ($world.get('lively top bar')) this.customizeTopBar();
connect($world, 'onTopBarLoaded', this, 'customizeTopBar');
this.initializeLayout();
this.ui.window = this.openInWindow({
title: 'Interactives Editor',
name: 'window for interactives editor',
acceptsDrops: false
});
await this.initializePanels();
this.initializeLayout();
connect(this.ui.window, 'close', this, 'abandon');
connect(this.ui.window, 'position', this, 'positionChanged');
connect(this.ui.window, 'minimized', this, 'onWindowMinimizedChange');
Expand All @@ -118,6 +123,8 @@ export class InteractivesEditor extends QinoqMorph {
}

async initializePanels () {
this.ui.preview = this.addMorph(new Preview({ _editor: this }));

this.ui.interactiveGraph = this.addMorph(new InteractiveGraph({
position: pt(0, 0),
extent: pt(CONSTANTS.SIDEBAR_WIDTH, CONSTANTS.SUBWINDOW_HEIGHT),
Expand All @@ -131,8 +138,6 @@ export class InteractivesEditor extends QinoqMorph {
}
}));

this.ui.preview = this.addMorph(new Preview({ _editor: this }));

this.ui.inspector = new InteractiveMorphInspector({
position: pt(CONSTANTS.PREVIEW_WIDTH + CONSTANTS.SIDEBAR_WIDTH, 0),
extent: pt(CONSTANTS.SIDEBAR_WIDTH, CONSTANTS.SUBWINDOW_HEIGHT),
Expand All @@ -147,8 +152,19 @@ export class InteractivesEditor extends QinoqMorph {
});
this.addMorph(this.ui.inspector);

this.ui.subWindow = new SubWindow({
extent: pt(CONSTANTS.EDITOR_WIDTH, CONSTANTS.SUBWINDOW_HEIGHT),
resizers: { north: true }
});
connect(this.ui.subWindow, 'onResize', this, 'relayout', {
converter: '() => target.extent'
});
connect(this.ui.subWindow, 'onResizeEnd', this, 'relayout', {
converter: '() => target.extent'
});

this.ui.menuBar = new MenuBar({
position: pt(0, CONSTANTS.SUBWINDOW_HEIGHT),
extent: pt(CONSTANTS.EDITOR_WIDTH, CONSTANTS.MENU_BAR_HEIGHT),
_editor: this,
borderWidth: {
bottom: CONSTANTS.BORDER_WIDTH,
Expand All @@ -159,7 +175,7 @@ export class InteractivesEditor extends QinoqMorph {
}
});
this.ui.menuBar.disableUIElements();
this.addMorph(this.ui.menuBar);
this.ui.subWindow.addMorph(this.ui.menuBar);
connect(this, 'onDisplayedTimelineChange', this.ui.menuBar, 'onGlobalTimelineTab', {
updater: `($update, displayedTimeline) => {
if (displayedTimeline == source.ui.globalTimeline) $update();
Expand All @@ -179,8 +195,6 @@ export class InteractivesEditor extends QinoqMorph {

this.ui.tabContainer = await resource('part://tabs/tabs').read();
Object.assign(this.ui.tabContainer, {
position: pt(0, CONSTANTS.SUBWINDOW_HEIGHT + CONSTANTS.MENU_BAR_HEIGHT),
extent: pt(CONSTANTS.EDITOR_WIDTH, CONSTANTS.TIMELINE_HEIGHT),
showNewTabButton: false,
tabHeight: 28,
visible: false
Expand All @@ -199,16 +213,43 @@ export class InteractivesEditor extends QinoqMorph {
this.ui.globalTab.closeable = false;
this.ui.globalTab.borderColor = COLOR_SCHEME.PRIMARY;

this.addMorph(this.ui.tabContainer);
this.ui.subWindow.addMorph(this.ui.tabContainer);
this.addMorph(this.ui.subWindow);
}

initializeLayout () {
this.layout = new ProportionalLayout({
lastExtent: this.extent
});
connect(this, 'extent', this, 'relayout');
this.extent = pt(CONSTANTS.EDITOR_WIDTH, CONSTANTS.EDITOR_HEIGHT);
}

relayout (extent) {
if (!this.ui.subWindow.isResizing) {
this.ui.subWindow.extent = pt(extent.x, this.height * this._latestSubWindowRatio);
this.ui.subWindow.position = pt(0, extent.y - this.ui.subWindow.height);
} else {
// remember the ratio between top and sub window
// so we can keep that ratio when resizing the window
this._latestSubWindowRatio = this.ui.subWindow.height / this.height;
}

const topWindowHeight = this.ui.subWindow.top - CONSTANTS.SCROLL_BAR_HEIGHT;

this.ui.interactiveGraph.extent = pt(this.ui.interactiveGraph.width, topWindowHeight);

this.ui.inspector.position = pt(extent.x - this.ui.inspector.width, 0);
this.ui.inspector.extent = pt(this.ui.inspector.width,
topWindowHeight);

this.ui.preview.extent =
pt(extent.x - this.ui.interactiveGraph.width - this.ui.inspector.width,
topWindowHeight);
this.ui.preview.position =
pt(this.ui.interactiveGraph.right +
(this.ui.inspector.left -
this.ui.interactiveGraph.right -
this.ui.preview.width) / 2, 0);
}

async createInteractiveWithNamePrompt () {
const name = await $world.prompt(
['New Interactive\n', {}, 'Enter a name for this Interactive:',
Expand Down Expand Up @@ -251,6 +292,7 @@ export class InteractivesEditor extends QinoqMorph {

connect(this.interactive, 'remove', this, 'reset');
connect(this.interactive, '_length', this.ui.menuBar.ui.scrollPositionInput, 'max').update(this.interactive.length);
// TODO: let this work with zoom
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd appreciate if this stays for now please.

connect(this.ui.preview, 'extent', this.interactive, 'extent');
connect(this.interactive, 'interactiveZoomed', this, 'onInteractiveZoomed');

Expand All @@ -262,16 +304,7 @@ export class InteractivesEditor extends QinoqMorph {
}

onInteractiveZoomed () {
const previewExtent = this.ui.preview.extent;

// only show scrollbars if they are necessary
if (this.interactive.extent.x >= previewExtent.x) {
this.ui.preview.clipMode = 'scroll';
}
if (this.interactive.extent.y >= previewExtent.y) {
this.ui.preview.clipMode = 'scroll';
}
if (!(this.interactive.extent.x > previewExtent.x) && !(this.interactive.extent.y > previewExtent.y)) this.ui.preview.clipMode = 'hidden';
this.ui.preview.updateScrollbarVisibility();
}

// call this to propagate changes to the scrollPosition to the actual interactive
Expand Down Expand Up @@ -998,7 +1031,12 @@ class Preview extends QinoqMorph {
defaultValue: 'preview'
},
extent: {
defaultValue: pt(CONSTANTS.PREVIEW_WIDTH, CONSTANTS.SUBWINDOW_HEIGHT)
defaultValue: pt(CONSTANTS.PREVIEW_WIDTH, CONSTANTS.SUBWINDOW_HEIGHT),
after: ['_editor', 'ui'],
set (extent) {
this.setProperty('extent', extent);
if (!this._deserializing) this.updateScrollbarVisibility();
}
},
borderColor: {
defaultValue: COLOR_SCHEME.ON_BACKGROUND_DARKER_VARIANT
Expand Down Expand Up @@ -1115,6 +1153,20 @@ class Preview extends QinoqMorph {
removeAnimationPreview () {
this.animationPreview = null;
}

updateScrollbarVisibility () {
if (!this.interactive) return;

// only show scrollbars if they are necessary
if (this.interactive.width >= this.width ||
this.interactive.height >= this.height) {
this.clipMode = 'scroll';
}
if (!(this.interactive.width > this.width) &&
!(this.interactive.height > this.height)) {
this.clipMode = 'hidden';
}
}
}

class PositionAnimationPreview extends Canvas {
Expand Down Expand Up @@ -1457,6 +1509,22 @@ class MenuBar extends QinoqMorph {
}
}

class SubWindow extends ResizeablePanel {
relayout () {
super.relayout();

const menuBar = this.get('menu bar');
const tabs = this.get('aTabs');

if (!menuBar || !tabs) return;

menuBar.position = pt(0, 0);
menuBar.extent = pt(this.width, menuBar.height);
tabs.position = pt(0, menuBar.height);
tabs.extent = pt(this.width, this.height - menuBar.height);
}
}

export class Settings extends QinoqMorph {
static get properties () {
return {
Expand Down
16 changes: 11 additions & 5 deletions inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ export class InteractiveMorphInspector extends QinoqMorph {
extent: {
set (extent) {
this.setProperty('extent', extent);
if (!this._deserializing && this.ui && this.ui.tabContainer) this.ui.tabContainer.extent = pt(this.width, this.height - this.ui.headlinePane.height);
// if (this._deserializing) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this save to remove? If so, can it go for good?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the same as the check below I would appreciate if we use this instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not, can/should be removed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference?

if (this.ui && this.ui.tabContainer && this.ui.headlinePane) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are those checks whether we are deserializing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily, but it might have the same effect

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then I think we should give this a try since it feels cleaner

this.ui.tabContainer.position = pt(0, this.ui.headlinePane.height);
this.ui.tabContainer.extent =
pt(this.ui.tabContainer.width, this.height - this.ui.headlinePane.height);
}
}
}
};
Expand Down Expand Up @@ -86,13 +91,12 @@ export class InteractiveMorphInspector extends QinoqMorph {
this.ui.headlinePane.layout = new HorizontalLayout({ spacing: 5, align: 'center' });
this.ui.headlinePane.addMorph(this.ui.targetPicker);
this.ui.headlinePane.addMorph(this.ui.headline);

this.addMorph(this.ui.headlinePane);

this.ui.tabContainer = await resource('part://tabs/tabs').read();
Object.assign(this.ui.tabContainer, {
position: pt(1, 38),
extent: pt(this.width, this.height - this.ui.headlinePane.height - CONSTANTS.TAB_HEADER_HEIGHT),
position: pt(0, 38),
extent: pt(this.width, this.height - this.ui.headlinePane.height),
showNewTabButton: false,
tabHeight: 25
});
Expand All @@ -107,6 +111,7 @@ export class InteractiveMorphInspector extends QinoqMorph {

this.initializeAnimationsInspector();
this.initializeStyleInspector();

this.ui.animationsInspectorTab.selected = true;
this.addMorph(this.ui.tabContainer);
this.ui.tabContainer.getSubmorphNamed('tab content container').acceptsDrops = false;
Expand All @@ -132,7 +137,8 @@ export class InteractiveMorphInspector extends QinoqMorph {

selectMorphThroughHalo (morph) {
if (Array.isArray(morph)) morph = morph[0]; // Multi select through halo
if (this.interactive && this.interactive.sequences.includes(Sequence.getSequenceOfMorph(morph))) {
if (this.interactive &&
this.interactive.sequences.includes(Sequence.getSequenceOfMorph(morph))) {
Comment on lines +140 to +141
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an intended change or was it introduced by windows formatting complications?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't Sequence.getSequenceOfMorph already check whether there is a Sequence in the interactive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@linusha This is intended
@Paula-Kli No, it only traverses the morph owner change until the first Sequence

this.targetMorph = morph;
}
}
Expand Down
8 changes: 5 additions & 3 deletions interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export class Interactive extends DeserializationAwareMorph {
defaultValue: 16 / 9,
set (aspectRatio) {
this.setProperty('fixedAspectRatio', aspectRatio);
// eslint-disable-next-line no-self-assign
this.extent = this.applyAspectRatio(this.extent);
}
},
Expand All @@ -69,6 +68,7 @@ export class Interactive extends DeserializationAwareMorph {
}
this.setProperty('extent', extent);
if (!this._deserializing) {
this.updateSequenceExtents();
this.scaleText(previousHeight);
}
}
Expand Down Expand Up @@ -100,6 +100,10 @@ export class Interactive extends DeserializationAwareMorph {
};
}

updateSequenceExtents () {
this.sequences.forEach(sequence => sequence.extent = this.extent);
}

applyAspectRatio (extent, calculateAspectRatio = false) {
let aspectRatio;
aspectRatio = calculateAspectRatio ? this.width / this.height : this.fixedAspectRatio;
Expand Down Expand Up @@ -278,12 +282,10 @@ export class Interactive extends DeserializationAwareMorph {
sequence.interactive = this;
this.updateInteractiveLength();
signal(this, 'onSequenceAddition', sequence);
connect(this, 'extent', sequence, 'extent');
}

removeSequence (sequence) {
disconnectAll(sequence);
disconnect(this, 'extent', sequence, 'extent');
arr.remove(this.sequences, sequence);
sequence.remove();
signal(this, 'onSequenceRemoval', sequence);
Expand Down
Loading