diff --git a/pxtblocks/plugins/comments/blockComment.ts b/pxtblocks/plugins/comments/blockComment.ts index 0d7da5f0dc8..457c40e8861 100644 --- a/pxtblocks/plugins/comments/blockComment.ts +++ b/pxtblocks/plugins/comments/blockComment.ts @@ -34,10 +34,10 @@ export class CommentIcon extends Blockly.icons.Icon { */ protected weight = 3; - protected elementName: string = "comment"; protected xOffsetFieldName = COMMENT_OFFSET_X_FIELD_NAME; protected yOffsetFieldName = COMMENT_OFFSET_Y_FIELD_NAME; protected bubbleClasses: string[] = []; + protected bubbleHeaderText: string = undefined; /** The bubble used to show editable text to the user. */ protected textInputBubble: TextInputBubble | null = null; @@ -175,7 +175,7 @@ export class CommentIcon extends Blockly.icons.Icon { eventUtils.fire( new (eventUtils.get(eventUtils.BLOCK_CHANGE))( this.sourceBlock, - this.elementName, + "comment", null, oldText, text, @@ -253,7 +253,7 @@ export class CommentIcon extends Blockly.icons.Icon { eventUtils.fire( new (eventUtils.get(eventUtils.BLOCK_CHANGE))( this.sourceBlock, - this.elementName, + "comment", null, this.text, newText, @@ -315,7 +315,7 @@ export class CommentIcon extends Blockly.icons.Icon { new (eventUtils.get(eventUtils.BUBBLE_OPEN))( this.sourceBlock, visible, - this.elementName, + "comment", ), ); } @@ -333,6 +333,7 @@ export class CommentIcon extends Blockly.icons.Icon { this.getBubbleOwnerRect(), false, this.bubbleClasses, + this.bubbleHeaderText, ); this.textInputBubble.setText(this.getText()); this.textInputBubble.setSize(this.bubbleSize, true); @@ -362,6 +363,7 @@ export class CommentIcon extends Blockly.icons.Icon { this.getBubbleOwnerRect(), true, this.bubbleClasses, + this.bubbleHeaderText, ); this.textInputBubble.setText(this.getText()); this.textInputBubble.setSize(this.bubbleSize, true); diff --git a/pxtblocks/plugins/comments/bubble.ts b/pxtblocks/plugins/comments/bubble.ts index a86323986c2..d416cdfe38e 100644 --- a/pxtblocks/plugins/comments/bubble.ts +++ b/pxtblocks/plugins/comments/bubble.ts @@ -1,6 +1,7 @@ import * as Blockly from "blockly"; import dom = Blockly.utils.dom; +import { setHsvSaturation } from "blockly/core/utils/colour"; /** * The abstract pop-up bubble class. This creates a UI that looks like a speech @@ -56,6 +57,8 @@ export abstract class Bubble implements Blockly.IDeletable { private topBar: SVGRectElement; + protected header: SVGTextElement; + protected deleteIcon: SVGImageElement; private collapseIcon: SVGImageElement; @@ -76,6 +79,7 @@ export abstract class Bubble implements Blockly.IDeletable { public readonly workspace: Blockly.WorkspaceSvg, protected anchor: Blockly.utils.Coordinate, protected ownerRect?: Blockly.utils.Rect, + protected headerText?: string, ) { this.id = Blockly.utils.idGenerator.getNextUniqueId(); this.svgRoot = dom.createSvgElement( @@ -121,6 +125,17 @@ export abstract class Bubble implements Blockly.IDeletable { embossGroup ); + if (this.headerText) { + this.header = dom.createSvgElement( + Blockly.utils.Svg.TEXT, + { + 'class': 'blocklyText bubbleHeaderText' + }, + embossGroup + ); + this.header.textContent = this.headerText; + } + this.deleteIcon = dom.createSvgElement( Blockly.utils.Svg.IMAGE, { @@ -227,6 +242,7 @@ export abstract class Bubble implements Blockly.IDeletable { const topBarSize = this.topBar.getBBox(); const deleteSize = this.deleteIcon.getBBox(); const foldoutSize = this.collapseIcon.getBBox(); + const headerSize = this.header?.getBBox(); size.width = Math.max(size.width, Bubble.MIN_SIZE); size.height = Math.max(size.height, Bubble.MIN_SIZE); @@ -239,6 +255,9 @@ export abstract class Bubble implements Blockly.IDeletable { this.updateDeleteIconPosition(size, topBarSize, deleteSize); this.updateFoldoutIconPosition(topBarSize, foldoutSize); + if (headerSize) { + this.updateHeaderPosition(topBarSize, foldoutSize, deleteSize, headerSize); + } if (relayout) { this.positionByRect(this.ownerRect); @@ -701,6 +720,25 @@ export abstract class Bubble implements Blockly.IDeletable { this.collapseIcon.setAttribute('x', `${foldoutMargin}`); } + private updateHeaderPosition(topBarSize: Blockly.utils.Size, foldoutSize: Blockly.utils.Size, deleteSize: Blockly.utils.Size, headerSize: Blockly.utils.Size) { + const foldoutMargin = this.calcFoldoutMargin(topBarSize, foldoutSize); + const deleteMargin = this.calcDeleteMargin(topBarSize, deleteSize); + + // Position right of the foldout icon. + // Note, the y position is relative to the bottom of the text. + const xPos = foldoutSize.width + foldoutMargin + Bubble.BORDER_WIDTH; + const yPos = topBarSize.height - headerSize.height / 2 + Bubble.BORDER_WIDTH + 1.5 /* 1.5 is hackathon nudge to get this thing centered */; + + // foldoutMargin intentionally included twice (once for left of icon, once for right) + if (topBarSize.width > xPos + foldoutMargin + headerSize.width + deleteSize.width + deleteMargin) { + this.header?.setAttribute('visibility', 'visibile'); + this.header?.setAttribute('y', `${yPos}`); + this.header?.setAttribute('x', `${xPos}`); + } else { + this.header?.setAttribute('visibility', 'collapse'); + } + } + /** Calculates the margin that should exist around the delete icon. */ private calcDeleteMargin(topBarSize: Blockly.utils.Size, deleteSize: Blockly.utils.Size) { return ((topBarSize.height - deleteSize.height) / 2) + Bubble.BORDER_WIDTH; @@ -720,4 +758,8 @@ Blockly.Css.register(` .blocklyBubble .blocklyTextarea.blocklyText { color: #575E75; } -`); \ No newline at end of file + +.blocklyText.bubbleHeaderText { + font-weight: bold; +} +`); diff --git a/pxtblocks/plugins/comments/reviewCommentIcon.ts b/pxtblocks/plugins/comments/reviewCommentIcon.ts index 725ab6f3e9b..010fdf51f2d 100644 --- a/pxtblocks/plugins/comments/reviewCommentIcon.ts +++ b/pxtblocks/plugins/comments/reviewCommentIcon.ts @@ -38,6 +38,7 @@ export class ReviewCommentIcon extends CommentIcon { protected xOffsetFieldName = REVIEW_COMMENT_OFFSET_X_FIELD_NAME; protected yOffsetFieldName = REVIEW_COMMENT_OFFSET_Y_FIELD_NAME; protected bubbleClasses = ["reviewCommentBubble"]; + protected bubbleHeaderText = lf("Feedback"); constructor(protected readonly sourceBlock: Blockly.Block) { super(sourceBlock); diff --git a/pxtblocks/plugins/comments/textinput_bubble.ts b/pxtblocks/plugins/comments/textinput_bubble.ts index 26fb1eff4af..405dd05236a 100644 --- a/pxtblocks/plugins/comments/textinput_bubble.ts +++ b/pxtblocks/plugins/comments/textinput_bubble.ts @@ -68,8 +68,9 @@ export class TextInputBubble extends Bubble { protected ownerRect?: Blockly.utils.Rect, protected readonly readOnly?: boolean, protected readonly additionalClasses?: string[], + protected headerText?: string, ) { - super(workspace, anchor, ownerRect); + super(workspace, anchor, ownerRect, headerText); dom.addClass(this.svgRoot, 'blocklyTextInputBubble'); if (additionalClasses?.length) { additionalClasses.forEach((c) => dom.addClass(this.svgRoot, c));