Skip to content

Commit

Permalink
mrtextinput update pass 2 - separation into textarea and textfield (#546
Browse files Browse the repository at this point in the history
)

Signed-off-by: Hannah Bollar <[email protected]>
Signed-off-by: hanbollar <[email protected]>
  • Loading branch information
hanbollar authored Apr 5, 2024
1 parent b6c221f commit 3e65be5
Show file tree
Hide file tree
Showing 9 changed files with 437 additions and 358 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
![The MRjs logo, an indigo and purple bowtie.](https://docs.mrjs.io/static/mrjs-logo.svg)

An extensible library of Web Components for the spatial web.

[![npm run build](https://github.com/Volumetrics-io/mrjs/actions/workflows/build.yml/badge.svg)](https://github.com/Volumetrics-io/mrjs/actions/workflows/build.yml) [![npm run test](https://github.com/Volumetrics-io/mrjs/actions/workflows/test.yml/badge.svg)](https://github.com/Volumetrics-io/mrjs/actions/workflows/test.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Volumetrics-io/mrjs/blob/main/LICENSE)
Expand Down
14 changes: 7 additions & 7 deletions dist/mr.js

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions src/core/MRApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class MRApp extends MRElement {
if (this.getAttribute('occlusion') == 'spotlight') {
this.scene.add(this.user.initSpotlight());
}

// order matters for all the below system creation items
this.panelSystem = new PanelSystem();
this.layoutSystem = new LayoutSystem();
Expand All @@ -146,7 +147,7 @@ export class MRApp extends MRElement {
this.skyBoxSystem = new SkyBoxSystem();
this.audioSystem = new AudioSystem();

// these must be the last three systems since
// These must be the last three systems since
// they affect rendering. Clipping must happen
// before masking. Rendering must be the last step.
this.clippingSystem = new ClippingSystem();
Expand Down Expand Up @@ -339,9 +340,7 @@ export class MRApp extends MRElement {

window.addEventListener('resize', this.onWindowResize);

const lightString = this.getAttribute('lighting');

if (lightString) {
if (this.getAttribute('lighting') ?? false) {
this.lighting = mrjsUtils.string.stringToJson(this.lighting);
}

Expand Down
34 changes: 1 addition & 33 deletions src/core/componentSystems/MaterialStyleSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,39 +70,7 @@ export class MaterialStyleSystem extends MRSystem {
* @description Sets the background based on compStyle and inputted css elements.
*/
setBackground(entity) {
const color = entity.compStyle.backgroundColor;

if (color.startsWith('rgba')) {
const rgba = color
.match(/rgba?\(([^)]+)\)/)[1]
.split(',')
.map((n) => parseFloat(n.trim()));
entity.background.material.color.setStyle(`rgb(${rgba[0]}, ${rgba[1]}, ${rgba[2]})`);
entity.background.material.transparent = rgba.length === 4 && rgba[3] < 1;
entity.background.material.opacity = rgba.length === 4 ? rgba[3] : 1;
entity.background.visible = !(rgba.length === 4 && rgba[3] === 0);
} else if (color.startsWith('rgb')) {
// RGB colors are treated as fully opaque
entity.background.material.color.setStyle(color);
entity.background.material.transparent = false;
entity.background.visible = true;
} else if (color.startsWith('#')) {
const { r, g, b, a } = mrjsUtils.color.hexToRgba(color);
entity.background.material.color.setStyle(`rgb(${r}, ${g}, ${b})`);
entity.background.material.transparent = a < 1;
entity.background.material.opacity = a;
entity.background.visible = a !== 0;
} else {
// This assumes the color is a CSS color word or another valid CSS color value
entity.background.material.color.setStyle(color);
entity.background.material.transparent = false;
entity.background.visible = true;
}

if (entity.compStyle.opacity < 1) {
entity.background.material.opacity = entity.compStyle.opacity;
}
entity.background.material.needsUpdate = true;
mrjsUtils.color.setObject3DColor(entity.background, entity.compStyle.backgroundColor, entity.compStyle.opacity);
}

/**
Expand Down
125 changes: 65 additions & 60 deletions src/core/componentSystems/TextSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ export class TextSystem extends MRSystem {
constructor() {
super(false);

// Setup all the preloaded fonts
this.preloadedFonts = {};

this.styles = {};
const styleSheets = Array.from(document.styleSheets);

styleSheets.forEach((styleSheet) => {
const cssRules = Array.from(styleSheet.cssRules);
// all the font-faces rules
Expand All @@ -48,10 +46,10 @@ export class TextSystem extends MRSystem {
});
});

// Handle text style needs update
this.app.addEventListener('trigger-text-style-update', (e) => {
// The event has the entity stored as its detail.
if (e.detail !== undefined) {
// console.log('trigger-text-style-update for ', e.detail)
this._updateSpecificEntity(e.detail);
}
});
Expand All @@ -76,24 +74,72 @@ export class TextSystem extends MRSystem {
* @description The per entity triggered update call. Handles updating all text items including updates for style and cleaning of content for special characters.
*/
_updateSpecificEntity(entity) {
this.checkIfTextContentChanged(entity);
this.handleTextContentUpdate(entity);
}

/**
*
* @param {object} entity - checks if the content changed and if so, updates it to match.
* @returns {boolean} true if the content needed to be updated, false otherwise.
*/
checkIfTextContentChanged(entity) {
// Add a check in case a user manually updates the text value
let text =
entity instanceof MRTextInputEntity
? entity.hiddenInput?.value ?? false
: // troika honors newlines/white space
// we want to mimic h1, p, etc which do not honor these values
// so we have to clean these from the text
// ref: https://github.com/protectwise/troika/issues/289#issuecomment-1841916850
entity.textContent
.replace(/(\n)\s+/g, '$1')
.replace(/(\r\n|\n|\r)/gm, ' ')
.trim();

if (entity.textObj.text != text) {
entity.textObj.text = text;
return true;
}
return false;
}

/**
*
* @param {object} entity - the entity whose content updated.
*/
handleTextContentUpdate(entity) {
this.updateStyle(entity);

// the sync step ensures troika's text render info and geometry is up to date
// The sync step ensures troika's text render info and geometry is up to date
// with any text content changes.
entity.textObj.sync(() => {
if (entity instanceof MRButtonEntity) {
// MRButtonEntity

entity.textObj.anchorX = 'center';
} else {
} else if (entity instanceof MRTextInputEntity) {
// MRTextAreaEntity, MRTextFieldEntity, etc

// textObj positioning and dimensions
entity.textObj.maxWidth = entity.width;
entity.textObj.maxHeight = entity.height;
entity.textObj.position.setX(-entity.width / 2);
entity.textObj.position.setY(entity.height / 2);
}

if (entity instanceof MRTextFieldEntity || entity instanceof MRTextAreaEntity) {
// cursor positioning and dimensions
entity.cursorStartingPosition.x = entity.textObj.position.x;
entity.cursorStartingPosition.y = entity.textObj.position.y - entity.cursorHeight / 2;
// handle activity
if (entity == document.activeElement) {
entity.updateCursorPosition();
} else {
entity.blur();
}
} else {
// MRTextEntity

entity.textObj.position.setX(-entity.width / 2);
entity.textObj.position.setY(entity.height / 2);
}
});
}
Expand All @@ -104,27 +150,8 @@ export class TextSystem extends MRSystem {
*/
eventUpdate = () => {
for (const entity of this.registry) {
// Add a check in case a user manually
let text =
entity instanceof MRTextFieldEntity || entity instanceof MRTextAreaEntity
? entity.hiddenInput.value
: // troika honors newlines/white space
// we want to mimic h1, p, etc which do not honor these values
// so we have to clean these from the text
// ref: https://github.com/protectwise/troika/issues/289#issuecomment-1841916850
entity.textContent
.replace(/(\n)\s+/g, '$1')
.replace(/(\r\n|\n|\r)/gm, ' ')
.trim();

let textContentChanged = entity.textObj.text != text;

// Now that we know text is different or at least definitely needs an update
// we can go and do the larger calculations and changes.
if (textContentChanged) {
entity.textObj.text = text;
this._updateSpecificEntity(entity);
}
this.checkIfTextContentChanged(entity);
this.handleTextContentUpdate(entity);
}
};

Expand All @@ -146,28 +173,26 @@ export class TextSystem extends MRSystem {
*/
updateStyle = (entity) => {
const { textObj } = entity;
if (textObj.text.trim().length != 0) {
textObj.font = this.preloadedFonts[entity.compStyle.fontFamily];
} else {
textObj.font = null;
}

// Font
textObj.font = textObj.text.trim().length != 0 ? this.preloadedFonts[entity.compStyle.fontFamily] : null;
textObj.fontSize = this.parseFontSize(entity.compStyle.fontSize, entity);
textObj.fontWeight = this.parseFontWeight(entity.compStyle.fontWeight);
textObj.fontStyle = entity.compStyle.fontStyle;

// Alignment
textObj.anchorY = this.getVerticalAlign(entity.compStyle.verticalAlign, entity);

textObj.textAlign = this.getTextAlign(entity.compStyle.textAlign);

textObj.lineHeight = this.getLineHeight(entity.compStyle.lineHeight, entity);

textObj.material.opacity = entity.compStyle.opacity ?? 1;

this.setColor(textObj, entity.compStyle.color);
// Color and opacity
mrjsUtils.color.setTEXTObject3DColor(textObj, entity.compStyle.color, entity.compStyle.opacity ?? 1);

// Whitespace and Wrapping
textObj.whiteSpace = entity.compStyle.whiteSpace ?? textObj.whiteSpace;
textObj.maxWidth = entity.width * 1.001;

// Offset position for visibility on top of background plane
textObj.position.z = 0.0001;
};

Expand Down Expand Up @@ -269,26 +294,6 @@ export class TextSystem extends MRSystem {
return textAlign;
}

/**
* @function
* @description Sets the matrial color and opacity based on the css color element
* @param {object} textObj - the textObj whose color is being updated
* @param {object} color - the representation of color as `rgba(xxx,xxx,xxx)` or as `#xxx`
*/
setColor(textObj, color) {
if (color.includes('rgba')) {
const rgba = color
.substring(5, color.length - 1)
.split(',')
.map((part) => parseFloat(part.trim()));
textObj.material.color.setStyle(`rgb(${rgba[0]}, ${rgba[1]}, ${rgba[2]})`);

textObj.material.opacity = rgba[3];
} else {
textObj.material.color.setStyle(color ?? '#000');
}
}

/**
* @function
* @description Based on the given font-face value in the passed cssString, tries to either use by default or download the requested font-face
Expand Down
Loading

0 comments on commit 3e65be5

Please sign in to comment.